3 Replies Latest reply on Mar 26, 2007 9:04 AM by drtog16

    classloader issue with WARs

    drtog16

      It seems that JBoss is changing the class loader of my WAR half way through deployment. I have done a bunch of reading in the last few days on class loaders in jboss to get what I am trying to do working, but am stuck.

      Enthronement: JBoss 4.0.5.GA, JRockit, Windows XP

      Objective
      =========================
      I want to have a number of WARs, and EARs that all share standard/common jars. I want to have multiple groupings of WARs / EARs. Eg. So i could have group A and group B ? each with their own setup of standard/common JARs.

      To accomplish the sharing I am setting the ?loader-repository? on each EAR and WAR of the same group to the same thing. Then for each group I also have a SAR where I put all the JARs i want shared in the group.

      Setup (I don't have multiple ?groups?, just one.)
      ========================
      First, here is my SAR that contains all the JARs
      projects-a.sar
      - lib
      - myfaces-api-1.1.4.jar
      - etc.
      - META-INF
      - jboss-service.xml
      <?xml version="1.0" ?>

      <loader-repository>
      com.test.projects:loader=projects-a
      <loader-repository-config>java2ParentDelegation=true</loader-repository-config>
      </loader-repository>


      Here is my WAR
      test.war
      - index.html ? Just a standard HTML file
      - jboss-web.xml
      <jboss-web>
      <class-loading java2ClassLoadingCompliance="true">
      <loader-repository>
      com.test.projects:loader=projects-a
      <loader-repository-config>java2ParentDelegation=true</loader-repository-config>
      </loader-repository>
      </class-loading>
      </jboss-web>
      - web.xml
      <?xml version="1.0"?>
      <web-app xmlns="http://java.sun.com/xml/ns/j2ee"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
      version="2.4">

      <listener-class>org.apache.myfaces.webapp.StartupServletContextListener</listener-class>


      <servlet-name>Faces Servlet</servlet-name>
      <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
      <load-on-startup>1</load-on-startup>

      <servlet-mapping>
      <servlet-name>Faces Servlet</servlet-name>
      <url-pattern>*.html</url-pattern>
      </servlet-mapping>
      </web-app>

      (Just to make sure there is no confusion, the WAR is NOT inside the SAR.)

      Problem
      ===============================
      When I startup JBoss I get the output at the bottom of this post.

      JSF is saying that StartupServletContextListener hasn't been initialized. If you look above you see that I have initialized it AND as you see in the output it is initialized right before the exception:
      16:17:55,934 INFO [StartupServletContextListener] ServletContext 'C:\Programs\jboss-4.0.5.GA\server\default\.\deploy\test\test.war\' initialized.
      16:17:55,934 INFO [StartupServletContextListener] Serialization provider : class org.apache.myfaces.shared_impl.util.serial.DefaultSerialFactory

      I placed debugged with breakpoints in javax.faces.FactoryFinder to examine the problem in more detail. When StartupServletContextListener starts up it puts the name of classes to be used by a factory in a static Map that is stored on FactoryFinder. It stores the information in the Map with the key being the class loader of the current thread.

      When it stores the information in the Map of FactoryFinder I have found that the class loader is the one I expect (the ?shared one?). When it pulls in the information out of the Map the first three times everything is fine. The fourth time when it pulls the information out the Map the thread class loader has magically changed so FactoryFinder says that no class names were registered in the Map.

      Here is the result of running the following code from right inside FactoryFinder
      System.out.println("Class loader of FactoryFinder: " + FactoryFinder.class.getClassLoader());
      // classLoader is a variable set in FactoryFinder. It is equal toThread.currentThread().getContextClassLoader()
      System.out.println("classLoader: " + classLoader);
      the first few times the it gives me

      16:30:27,668 INFO [STDOUT] Class loader of FactoryFinder: org.jboss.mx.loading.UnifiedClassLoader3@228dca{ url=file:/C:/Programs/jboss-4.0.5.GA/server/default/deploy/test/projects-a.sar/ ,addedOrder=15}
      16:30:27,684 INFO [STDOUT] classLoader: WebappClassLoader
      delegate: false
      repositories:
      ----------> Parent Classloader:
      java.net.FactoryURLClassLoader@436005

      the fourth time it gives this
      16:32:17,559 INFO [STDOUT] Class loader of FactoryFinder: org.jboss.mx.loading.UnifiedClassLoader3@5f7106{ url=file:/C:/Programs/jboss-4.0.5.GA/server/default/deploy/jbossweb-tomcat55.sar/ ,addedOrder=10}
      16:32:17,574 INFO [STDOUT] classLoader: WebappClassLoader
      delegate: false
      repositories:
      ----------> Parent Classloader:
      java.net.FactoryURLClassLoader@436005

      (Note the classloader changed)

      this is when the exception is thrown in the output below. FactoryFinder throws the exception because there is nothing in the Map under the current thread's classloader. The problem is when StartupServletContextListener registered the information it was in a different classloader.

      So from what I have seen, when FactoryFinder puts information in the Map and gets the information the first three times the classloader of the current thread is one thing, but the fourth time the current thread's classlorder has chanegd. (From what I know it seems the classloader is set to something it should NOT be set to at that time since a classloader is set in jboss-web.xml)

      Last thing to note is that JBoss ships with a myfaces that is in ?default\deploy\jbossweb-tomcat55.sar\jsf-libs?. I have removed this copy of myfaces, in some of my tests, to only leave the copy I supply inside of my SAR, but everything seems to act exactly as I just described.

      What is wrong with my setup? Is this a JBoss bug?

      Thanks for the help. I hope I have been detailed but concise.


      JBoss output on startup:
      ...
      16:17:55,012 INFO [TomcatDeployer] deploy, ctxPath=/test, warUrl=.../deploy/test/test.war/
      16:17:55,215 INFO [FacesConfigurator] Reading standard config org/apache/myfaces/resource/standard-faces-config.xml
      16:17:55,340 INFO [FacesConfigurator] Reading config jar:file:/C:/Programs/jboss-4.0.5.GA/server/default/tmp/deploy/tmp36669jsf-facelets-1.1.11.jar!/META-INF/faces-config.xml
      16:17:55,340 INFO [FacesConfigurator] Reading config jar:file:/C:/Programs/jboss-4.0.5.GA/server/default/tmp/deploy/tmp36677tomahawk-1.1.5-SNAPSHOT.jar!/META-INF/faces-config.xml
      16:17:55,465 WARN [LocaleUtils] Locale name in faces-config.xml null or empty, setting locale to default locale : en_US
      16:17:55,934 INFO [StartupServletContextListener] ServletContext 'C:\Programs\jboss-4.0.5.GA\server\default\.\deploy\test\test.war\' initialized.
      16:17:55,934 INFO [StartupServletContextListener] Serialization provider : class org.apache.myfaces.shared_impl.util.serial.DefaultSerialFactory
      16:17:55,949 ERROR [[/test]] StandardWrapper.Throwable
      java.lang.IllegalStateException: No Factories configured for this Application. This happens if the faces-initialization does not work at all - make sure that you properly include all configuration settings necessary for a basic faces application and that all the necessary libs are included. Also check the logging output of your web application and your container for any exceptions!
      If you did that and find nothing, the mistake might be due to the fact that you use some special web-containers which do not support registering context-listeners via TLD files and a context listener is not setup in your web.xml.
      A typical config looks like this;

      <listener-class>org.apache.myfaces.webapp.StartupServletContextListener</listener-class>


      at javax.faces.FactoryFinder.getFactory(FactoryFinder.java:90)
      at javax.faces.webapp.FacesServlet.init(FacesServlet.java:88)
      at org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:1105)
      at org.apache.catalina.core.StandardWrapper.load(StandardWrapper.java:932)
      at org.apache.catalina.core.StandardContext.loadOnStartup(StandardContext.java:3951)
      at org.apache.catalina.core.StandardContext.start(StandardContext.java:4225)
      at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:759)
      at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:739)
      at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:524)
      at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
      at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
      at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
      at java.lang.reflect.Method.invoke(Method.java:585)
      at org.apache.commons.modeler.BaseModelMBean.invoke(BaseModelMBean.java:503)
      at org.jboss.mx.server.RawDynamicInvoker.invoke(RawDynamicInvoker.java:164)
      at org.jboss.mx.server.MBeanServerImpl.invoke(MBeanServerImpl.java:659)
      at org.apache.catalina.core.StandardContext.init(StandardContext.java:5052)
      at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
      at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
      at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
      at java.lang.reflect.Method.invoke(Method.java:585)
      at org.apache.commons.modeler.BaseModelMBean.invoke(BaseModelMBean.java:503)
      at org.jboss.mx.server.RawDynamicInvoker.invoke(RawDynamicInvoker.java:164)
      at org.jboss.mx.server.MBeanServerImpl.invoke(MBeanServerImpl.java:659)
      at org.jboss.web.tomcat.tc5.TomcatDeployer.performDeployInternal(TomcatDeployer.java:297)
      at org.jboss.web.tomcat.tc5.TomcatDeployer.performDeploy(TomcatDeployer.java:103)
      at org.jboss.web.AbstractWebDeployer.start(AbstractWebDeployer.java:371)
      at org.jboss.web.WebModule.startModule(WebModule.java:83)
      16:17:55,949 ERROR [[/test]] Servlet /test threw load() exception
      java.lang.IllegalStateException: No Factories configured for this Application. This happens if the faces-initialization does not work at all - make sure that you properly include all configuration settings necessary for a basic faces application and that all the necessary libs are included. Also check the logging output of your web application and your container for any exceptions!
      If you did that and find nothing, the mistake might be due to the fact that you use some special web-containers which do not support registering context-listeners via TLD files and a context listener is not setup in your web.xml.
      A typical config looks like this;

      <listener-class>org.apache.myfaces.webapp.StartupServletContextListener</listener-class>


      at javax.faces.FactoryFinder.getFactory(FactoryFinder.java:90)
      at javax.faces.webapp.FacesServlet.init(FacesServlet.java:88)
      at org.apache.catalina.core.StandardWrapper.loadServlet(StandardWrapper.java:1105)
      at org.apache.catalina.core.StandardWrapper.load(StandardWrapper.java:932)
      at org.apache.catalina.core.StandardContext.loadOnStartup(StandardContext.java:3951)
      at org.apache.catalina.core.StandardContext.start(StandardContext.java:4225)
      at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:759)
      at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:739)
      at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:524)
      at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
      at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
      at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
      at java.lang.reflect.Method.invoke(Method.java:585)
      at org.apache.commons.modeler.BaseModelMBean.invoke(BaseModelMBean.java:503)
      at org.jboss.mx.server.RawDynamicInvoker.invoke(RawDynamicInvoker.java:164)
      at org.jboss.mx.server.MBeanServerImpl.invoke(MBeanServerImpl.java:659)
      at org.apache.catalina.core.StandardContext.init(StandardContext.java:5052)
      at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
      at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
      at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
      at java.lang.reflect.Method.invoke(Method.java:585)
      at org.apache.commons.modeler.BaseModelMBean.invoke(BaseModelMBean.java:503)
      at org.jboss.mx.server.RawDynamicInvoker.invoke(RawDynamicInvoker.java:164)
      at org.jboss.mx.server.MBeanServerImpl.invoke(MBeanServerImpl.java:659)
      at org.jboss.web.tomcat.tc5.TomcatDeployer.performDeployInternal(TomcatDeployer.java:297)
      at org.jboss.web.tomcat.tc5.TomcatDeployer.performDeploy(TomcatDeployer.java:103)
      at org.jboss.web.AbstractWebDeployer.start(AbstractWebDeployer.java:371)
      at org.jboss.web.WebModule.startModule(WebModule.java:83)

        • 1. Re: classloader issue with WARs
          drtog16

          I do have a small example of the WAR and SAR as described in the post if everyone wants it. (I don't see a way to post attachments on this forum.)

          • 2. Re: classloader issue with WARs
            drtog16

            It looks like the classloader is the same. The problem is actually the Map on FactoryFinder is static. The forth time FactoryFidner gets something from the Map, it is working with a different FactoryFinder class definition.

            For some reason the class gets loaded into the repository i want it to be in (a child one) and then it ends up in the default repository. Once in the default repository everything uses it from there it seems. Since the Map was initialized on the FactoryFinder that is in the child repository FactoryFinder complains that it hasn't been initialized once it exists in the default repository -- since that class definition static Map wasn't initialized with the values needed.

            • 3. Re: classloader issue with WARs
              drtog16

              I still don't know why I get the issue described above, but I did get it working by changing my settings. I changed this
              java2ParentDelegation = true
              to
              java2ParentDelegation = false
              in jboss-app.xml and jboss-web.xml

              If anyone gets confused by how java2ParentDelegation works I would recommend looking over the source code for HeirarchicalLoaderRepository3
              Here it is in SVN for JBoss 4.0.5 GA
              https://svn.jboss.org/repos/jbossas/branches/JBoss_4_0_5_GA_JBAS_3657/jmx/src/main/org/jboss/mx/loading/HeirarchicalLoaderRepository3.java

              Looking at the code cleared somethings up for me that are either documented wrong on the wiki or I didn't read correctly.