If you create a context.xml for a webapp and include a nested <Loader> element within the <Context> element and the <Context> is set to privileged="true", then the classloaders are not set up correctly and the webapp cannot see any of the privileged classes (i.e. org.catalina.*) For example, the manager webapp won't work, giving a NoClassDefFoundError for org.apache.catalina.ContainerServlet I belive that the XML for the loader element is processed like this in ContextRuleSet.begin() (around line 251): // Look up the required parent class loader ClassLoader parentClassLoader = null; Object ojb = digester.peek(); if (ojb instanceof Container) { parentClassLoader = ((Container)ojb).getParentClassLoader(); } // Instantiate a new Loader implementation object String className = loaderClass; if (attributeName != null) { String value = attributes.getValue(attributeName); if (value != null) className = value; } Class clazz = Class.forName(className); Class types[] = { ClassLoader.class }; Object args[] = { parentClassLoader }; Constructor constructor = clazz.getDeclaredConstructor(types); Loader loader = (Loader) constructor.newInstance(args); However, this is not retrieving the correct parent classloader for privileged webapps. See the following code in StandardContext.start() (around line 3950): if (getLoader() == null) { ClassLoader parent = null; if (getPrivileged()) { if (log.isDebugEnabled()) log.debug("Configuring privileged default Loader"); parent = this.getClass().getClassLoader(); } else { if (log.isDebugEnabled()) log.debug("Configuring non-privileged default Loader"); parent = getParentClassLoader(); } WebappLoader webappLoader = new WebappLoader(parent); webappLoader.setDelegate(getDelegate()); setLoader(webappLoader); } In the case where the <Loader> element was specified, getLoader() will return non-null and this block will never be entered. However, when that loader was created in ContextRuleSet, only the "non-privileged" parent class was used (i.e. StandardContext.getParentClassLoader()) rather than the privileged classloader at the appropriate times (i.e. StandardContext.getClass().getClassLoader())
Here is a suggested fix, which appears to fix the problem in my environment. It is in the method CreateLoaderRule.begin() ... my changes are flanked with the comment //NEW:mdb: public void begin(String namespace, String name, Attributes attributes) throws Exception { // Look up the required parent class loader ClassLoader parentClassLoader = null; Object ojb = digester.peek(); if (ojb instanceof Container) { parentClassLoader = ((Container)ojb).getParentClassLoader(); } //NEW:mdb: if the context is priviledged, set the classloader to // the context's classloader... if( ojb instanceof org.apache.catalina.Context && ((org.apache.catalina.Context)ojb).getPrivileged() ) { parentClassLoader = ojb.getClass().getClassLoader(); } //NEW:mdb:end change to test for privileged flag...
Matt's tested this fix for a few weeks now, said it looks fine in a message on the dev list. It seems reasonable to be as well, will apply.
Fixed for 5.5.13.
I’ve been developing using Eclipse and the Sysdeo plugin (http://www.sysdeo.com/eclipse/tomcatplugin) and I found that it worked fine right up until we started using tomcat 5.5.12. Consequently I picked up the source and started digging. I found that when the custom class loader runs using 5.5.9 the hierarchy of class loaders is : o webappX (WebappClassloader) o Shared (StandardClassloader) o Common (StandardClassloader) o System (AppClassloader) o Bootstrap (ExtClassloader) But when 5.5.12 (and up) are used it becomes: o webappX (WebappClassloader) o System (AppClassloader) o Bootstrap (ExtClassloader) Consequently the class loader stops working. I find that when my class loader works as follows … public myDevLoader(ClassLoader parent) { super(parent); } which is equivalent to … public myDevLoader(ClassLoader parent) { super(ClassLoader.getSystemClassLoader()); } The devloader will not find the common and shared class loaders however if I do the following…. public myDevLoader(ClassLoader parent) { super(Thread.currentThread().getContextClassLoader()); } Then the devloader works as it should.
Created attachment 18144 [details] A class loader which works in Tomcat 5.5.12 This attachment is a patch for the Sysdeo Eclipse plugin. It includes the source and the configuration documents for Tomcat 5.5.12
I don't think this is fixed yet. When using the tomcat sysdeo plugin with eclipse, neither the common or shared class loader is used. If I mark the context as privileged, then the common is in the chain but not the shared.
Created attachment 18673 [details] Patch for Sysdeo's DevLoader.java I tried to use the Sysdeo classloader to add maven dependencies to the webapp context, and I got stumped. Next, I found a glue in the javadoc for WebappClassLoader http://tomcat.apache.org/tomcat-5.5-doc/catalina/docs/api/org/apache/catalina/loader/WebappClassLoader.html that puzzled me. Apparently, a Webapp classloader cannot contain the servlet api classes. This patch solved the problem for me, but it's rather unelegant (it arbitrarily checks the jar pathname, it should try and find the exact classes) Recompile with the following command from the server\classes dir: javac -classpath .;..\lib\catalina.jar;..\..\common\lib\servlet-api.jar;%JRE_DIR%\lib\rt.jar org\apache\catalina\loader\DevLoader.java Hope this helps Angelo Turetta
*** This bug has been marked as a duplicate of 39704 ***