Bug 36852 - Custom Webapp loaders don't correctly honor context's privileged="true" attribute
Summary: Custom Webapp loaders don't correctly honor context's privileged="true" attri...
Status: RESOLVED DUPLICATE of bug 39704
Alias: None
Product: Tomcat 5
Classification: Unclassified
Component: Catalina (show other bugs)
Version: 5.5.9
Hardware: All Windows XP
: P2 normal with 1 vote (vote)
Target Milestone: ---
Assignee: Tomcat Developers Mailing List
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2005-09-29 01:38 UTC by Matt Brinkley
Modified: 2006-08-21 19:02 UTC (History)
0 users



Attachments
A class loader which works in Tomcat 5.5.12 (58.47 KB, application/x-zip-compressed)
2006-04-20 08:57 UTC, Bruce Mundin
Details
Patch for Sysdeo's DevLoader.java (541 bytes, patch)
2006-08-03 09:16 UTC, Angelo Turetta
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Matt Brinkley 2005-09-29 01:38:18 UTC
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())
Comment 1 Matt Brinkley 2005-09-29 19:05:13 UTC
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...
Comment 2 Yoav Shapira 2005-11-16 19:35:17 UTC
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.
Comment 3 Yoav Shapira 2005-11-16 19:51:17 UTC
Fixed for 5.5.13.
Comment 4 Bruce Mundin 2006-04-20 08:54:16 UTC
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.

 

Comment 5 Bruce Mundin 2006-04-20 08:57:46 UTC
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
Comment 6 robert engels 2006-06-07 18:28:01 UTC
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.
Comment 7 Angelo Turetta 2006-08-03 09:16:11 UTC
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
Comment 8 Mark Thomas 2006-08-22 02:02:38 UTC

*** This bug has been marked as a duplicate of 39704 ***