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

Reply via email to