tomcat-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Elgs Chen <elgs1...@hotmail.com>
Subject WebClassLoader using SystemClassLoader to load ServletDef?
Date Tue, 25 Sep 2012 12:19:09 GMT
Dear Tomcat Developers,

I have a question regarding the behavior of the WebClassLoader.loadClass(String name, boolean
resolve) in the org.apache.catalina.loader package.

I have the following assumptions, please correct me if any is wrong:
1, It seems the WebClassLoader only uses the SystemClassLoader (or AppClassLoader) to loader
tomcat core classes, like ServletDef;
2, It seems if tomcat is started up by the startup.sh from the bin directory, the classpath
in the startup command line will include only the jars in the bin directory: bootstrap.jar,
commons-daemon.jar and tomcat-juli.jar;
3, ClassLoader.getSystemClassLoader() by default returns the class loader with the startup
command-line classpath; 

Now if the above three assumption are correct, it seems that it's not possible for the WebClassLoader
to load the tomcat core classes as only the few bootstrap jars are in the system class loader's
classpath. However, the fact is the startup.sh script indeed starts tomcat up. Why?

For this I wrote some test code. My idea is to write a very simple bootstrap class(Bootstrap)
which creates an instance of URLClassLoader, the instance of URLClassLoader has all jars shipped
with the embedded tomcat 7.0.30. This classloader then loads another Class(TomcatMain), then
instantiates it and calls its main method reflectively to start it up. The purpose of doing
so is to save the effort to modify classpath in the startup script each time new jars are
added. My Bootstrap class looks something like this:
    public static void main(String[] args) throws Exception {
        List<File> jarList = new ArrayList<File>();
        System.out.println(getServerHome());
        getClassPath(jarList, new File(getServerHome(), "lib"));
        URL[] urlList = toURL(jarList);
        URLClassLoader loader = new URLClassLoader(urlList);
        Class c = loader.loadClass(args[0]);
        Method m = c.getMethod("main", new Class[] { args.getClass() });
        String[] appArgs = new String[args.length - 1];
        System.arraycopy(args, 1, appArgs, 0, args.length - 1);
        m.invoke(null, new Object[] { appArgs });
    }

 And the TomcatMain looks something like this:
    public static void main(String[] args) throws Exception {
        if (args[0].indexOf("start") != -1) {
            System.out.println("Self:" + TomcatMain.class.getClassLoader());
            System.out.println("Parent:" + TomcatMain.class.getClassLoader().getParent());
            String appBase = getServerHome() + "/web";
            final Tomcat tomcat = new Tomcat();
            tomcat.setPort(8080);
            //...
        }
    }
               
Then I pack the Bootstrap in loader.jar, running the following command from the command-line:
java -cp loader.jar Bootstrap TomcatMain start

I get the following error output:
    Self:java.net.URLClassLoader@60ec2ea8
    Parent:sun.misc.Launcher$AppClassLoader@e776f7
    ...
    SEVERE: End event threw exception
    java.lang.ClassNotFoundException: org.apache.catalina.deploy.ServletDef
        at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1714)
        at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1559)
        at org.apache.tomcat.util.IntrospectionUtils.callMethod1(IntrospectionUtils.java:852)
        ...
Then I debug into the WebClassLoader, it turned out that the SystemClassLoader is not able
to load the ServletDef class. And it looks reasonable, as the system class loader should be
the one that I put the few bootstrap jars in the command line, as the assumption 2 implies.
Seems The WebClassLoader doesn't intend to load the ServletDef from the URLClassLoader created
by the Bootstrap at all.

To further prove what I thought, I tried to start up the TomcatMain directly from the command
line, with all embedded tomcat shipped jars in the command line classpath.  This time it works.
Tomcat started up successfully, with the following output: 
    Self:sun.misc.Launcher$AppClassLoader@492833ff
    Parent:sun.misc.Launcher$ExtClassLoader@6e6dcfde
This time, it uses the system(app) class loader to load the ServletDef, which enclosing jar
is in the command line classpath. So far, that proved what I guess.

So my question is:
How does the startup.sh shipped with tomcat binary make the WebClassLoader to load the ServletDef?

Thanks,
Elgs
Mime
  • Unnamed multipart/alternative (inline, None, 0 bytes)
View raw message