classloader issue with WARs
drtog16 Mar 24, 2007 5:50 PMIt 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)