Jan Luehe's Blog
Why is org.apache.commons.digester.Digester not finding my classes?!
org.apache.commons.digester.Digester not finding my classes?!Why is org.apache.commons.digester.Digester not finding my classes?!
As people have tried to deploy the Roller Blog web application to Glassfish, they have run into a mystifying ClassNotFoundException, where the class that was not being found (in this case: org.roller.presentation.tags.menu.MenuImpl) was bundled with the Roller Blog web application itself.
The ClassNotFoundException has come with this stack trace:
java.lang.ClassNotFoundException: org.roller.presentation.tags.menu.MenuImpl
at org.apache.commons.digester.Digester.createSAXException(Digester.java:2540)
at org.apache.commons.digester.Digester.createSAXException(Digester.java:2566)
at org.apache.commons.digester.Digester.startElement(Digester.java:1276)
at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.startElement(AbstractSAXParser.java:533)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanStartElement(XMLDocumentFragmentScannerImpl.java:878)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl$FragmentContentDispatcher.dispatch(XMLDocumentFragmentScannerImpl.java:1693)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(XMLDocumentFragmentScannerImpl.java:368)
at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:834)
at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:764)
at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(XMLParser.java:148)
at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(AbstractSAXParser.java:1242)
at org.apache.commons.digester.Digester.parse(Digester.java:1567)
Looks as if org.apache.commons.digester.Digester is having problems with processing rules that reference locally bundled classes (i.e., classes located in WEB-INF/classes or WEB-INF/lib/[*.jar|*.zip]).
This problem is not specific to the Roller Blog web application: All web applications that are using the commons-digester package from Apache with a version equal to or greater than 1.1 are suffering from it. So what is going on?!
The ClassNotFoundException is caused by a regression in commons-digester, which was introduced between versions 1.0 and 1.1 of this package.
As of commons-digester version 1.1, org.apache.commons.digester.Digester no longer uses the context classloader (obtained via a call to Thread.currentThread().getContextClassLoader()) by default to resolve or load any classes referenced by any rules it processes. Remember that in a Java EE environment, the web container sets a web application's classloader as the context classloader before invoking any of the web application's components, which means that when any code in your web application calls Thread.currentThread().getContextClassLoader(), it will receive a reference to its own classloader.
Instead, as of commons-digester version 1.1, org.apache.commons.digester.Digester uses the classloader by which it was loaded (i.e., the classloader returned by digester_instance.getClass().getClassLoader()) by default. In order to restore the 1.0 behaviour of using the context classloader, you must set the useContextClassLoader property of your org.apache.commons.digester.Digester instance to TRUE, as follows:
digester.setUseContextClassLoader(true);
This regression does not cause any problems if a web application bundles its own commons-digester (as has been the case with the Roller Blog web application), as long as there is no commons-digester further up in the classloader delegation chain: In this case, digester_instance.getClass().getClassLoader() will return your web application's classloader, giving digester_instance access to all of its classes.
However, if there is a commons-digester higher up in the classloader delegation chain (as is the case with Glassfish!), the web application classloader will delegate to it by default, giving the commons-digester package higher up in the chain preference over the locally bundled version, resulting in the ClassNotFoundException seen above.
We are going to address this package conflict by renaming all commons packages bundled with Glassfish from org.apache to com.sun.org.apache.
In the meantime, you can work around this problem by instructing your web application's classloader to resolve and load classes locally before delegating to its parent. You do this by adding the following element to your web application's WEB-INF/sun-web.xml resource (if your web application does not already contain this resource, you need to create one):
<sun-web-app>
<class-loader delegate="false"/>
</sun-web-app>
Alternatively, if your code has access to the org.apache.commons.digester.Digester instance that is processing your rules, you can try setting its useContextClassLoader property to TRUE, as described above.
I am going to update you as soon as the package renaming effort has been completed, which will eliminate the need for any of these workarounds.
Posted at 01:02PM Feb 17, 2006 by Jan Luehe in Sun | Comments[37]
Honestly, this seems like two issues with GlassFish as opposed to a "regression in commons-digester". From the Java Servlet Specification Version 2.5:
SRV.9.7.2 Web Application Class Loader
... It is recommended also that the application class loader be implemented so that classes and resources packaged within the WAR are loaded in preference to classes and resources residing in container-wide library JARs.
It doesn't seem proper that you have to explicitly configure GlassFish to comply with the spec's recommendation. The second issue, which you've already identified and apparently plan to fix, is the package name conflict. Both of those issues are also present in some other app servers, so it would be great if GlassFish could start off by doing the right thing.
Posted by Kris Schneider on February 17, 2006 at 04:56 PM PST #
Even now, when a user configures their webapp's classloader to not delegate, we must ignore this setting when resolving symbols in the org.apache.commons.logging package, and always delegate.
This is to avoid the following error which had been reported when bundling commons-logging.jar in WEB-INF/lib and setting the delegate flag to FALSE:
java.lang.ExceptionInInitializerError at org.apache.catalina.core.ApplicationContext.getRequestDispatcher(ApplicationContext.java:447) at org.apache.catalina.core.ApplicationContextFacade.getRequestDispatcher(ApplicationContextFacade.java:208) at org.apache.coyote.tomcat5.CoyoteRequest.getRequestDispatcher(CoyoteRequest.java:1341) at org.apache.coyote.tomcat5.CoyoteRequestFacade.getRequestDispatcher(CoyoteRequestFacade.java:576) at javax.servlet.ServletRequestWrapper.getRequestDispatcher(ServletRequestWrapper.java:392) ... more Caused by: org.apache.commons.logging.LogConfigurationException: org.apache.commons.logging.LogConfigurationException: org.apache.commons.logging.LogConfigurationException: Class org.apache.commons.logging.impl.Jdk14Logger does not implement Log at org.apache.commons.logging.impl.LogFactoryImpl.newInstance(LogFactoryImpl.java:555) at org.apache.commons.logging.impl.LogFactoryImpl.getInstance(LogFactoryImpl.java:289) at org.apache.commons.logging.impl.LogFactoryImpl.getInstance(LogFactoryImpl.java:259) at org.apache.commons.logging.LogFactory.getLog(LogFactory.java:390) at org.apache.catalina.core.ApplicationDispatcher.<clinit>(ApplicationDispatcher.java:166) ... 41 more Caused by: org.apache.commons.logging.LogConfigurationException: org.apache.commons.logging.LogConfigurationException: Class org.apache.commons.logging.impl.Jdk14Logger does not implement Log at org.apache.commons.logging.impl.LogFactoryImpl.getLogConstructor(LogFactoryImpl.java:420) at org.apache.commons.logging.impl.LogFactoryImpl.newInstance(LogFactoryImpl.java:548) ... 45 more Caused by: org.apache.commons.logging.LogConfigurationException: Class org.apache.commons.logging.impl.Jdk14Logger does not implement Log at org.apache.commons.logging.impl.LogFactoryImpl.getLogConstructor(LogFactoryImpl.java:416) ... 46 more |#]Notice the "Class org.apache.commons.logging.impl.Jdk14Logger does not implement Log" message.
When the appserver's "org.apache.catalina.core.ApplicationDispatcher" is resolved, its static initializer calls:
private static Log log = LogFactory.getLog(ApplicationDispatcher.class);which in turn invokes org.apache.commons.logging.impl.LogFactoryImpl.getLogConstructor(), which has this code:
if (!Log.class.isAssignableFrom(logClass)) { throw new LogConfigurationException ("Class " + logClassName + " does not implement Log"); }Notice that the "org.apache.commons.logging.Log" interface to which 'logClass' is being assigned has been resolved against the appserver's commons-logging.jar, because "org.apache.catalina.core.ApplicationDispatcher" is part of the appserver's impl.
"org.apache.commons.logging.impl.LogFactoryImpl" loads the logger impl ("org.apache.commons.logging.impl.Jdk14Logger") using the thread's context classloader. If the appserver's "org.apache.catalina.core.ApplicationDispatcher" is first resolved when processing a client request, the thread context classloader corresponds to the webapp's classloader, which, in the above example, was bundling common-logging.jar. This means that, unless your webapp classloader has been instructed to delegate, it will resolve both the logger impl ("org.apache.commons.logging.impl.Jdk14Logger"), as well as the "org.apache.commons.logging.Log" interface it implements, against the local commons-logging.jar.
This means that the following condition:
if (!Log.class.isAssignableFrom(logClass)) { throw new LogConfigurationException ("Class " + logClassName + " does not implement Log"); }will evaluate to false and throw an exception (see the above stacktrace), because the "org.apache.commons.logging.Log" interface implemented by 'logClass' and the "org.apache.commons.logging.Log" interface it is being assigned to were loaded by different classloaders.
This is just to demonstrate the kind of problems that you may run into when not delegating.
We're in the process of renaming the appserver's bundled org.apache.commons packages to com.sun.org.apache.commons, to avoid any kind of collision regardless of a webapp classloader's delegate setting.
Posted by Jan Luehe on March 07, 2006 at 08:15 PM PST #
<sun-web-app> <class-loader delegate="false"/> </sun-web-app>in order to make Roller work on Glassfish is no longer required, because Glassfish has renamed its org.apache.commons.* packages to com.sun.org.apache.commons.*, to avoid any collisions when a webapp (such as Roller) bundles any of those packages.
Posted by Jan Luehe on April 05, 2006 at 11:23 AM PDT #
Posted by 顶瑞 on May 07, 2006 at 12:20 AM PDT #
Posted by jsdfksj on May 15, 2006 at 10:44 PM PDT #
Posted by jasdfi on May 15, 2006 at 10:45 PM PDT #
Posted by sdefasdef on May 18, 2006 at 01:29 AM PDT #
Posted by dsds on May 25, 2006 at 02:00 AM PDT #
Posted by dfs on June 03, 2006 at 10:00 AM PDT #
Posted by ddssssssssssss on June 12, 2006 at 12:08 AM PDT #
Posted by fgs on June 17, 2006 at 07:47 PM PDT #
Posted by ertyte on June 17, 2006 at 07:49 PM PDT #
Posted by fd on June 18, 2006 at 01:50 AM PDT #
Posted by sdf on June 29, 2006 at 02:27 AM PDT #
Posted by sdf on July 07, 2006 at 05:20 AM PDT #
Posted by sdf on July 07, 2006 at 05:21 AM PDT #