tomcat-users mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Jon Pearson" <Jon.Pear...@sixnet.com>
Subject RE: Classloaders
Date Wed, 27 May 2009 13:08:36 GMT
> >> 2. The documentation on classloaders is here:
> >> http://tomcat.apache.org/tomcat-6.0-doc/class-loader-howto.html
> >
> > Thanks, but I've read through that a few times. It describes the
> > existing classloader layout but does not describe how to 
> avoid problems
> > when using your own within Tomcat.
> >
> 
> Well, at least you should cooperate with this layout. ;)

As far as I know, I am. I was using the classloader of the current class as the parent when
constructing the URLClassLoader, now I am using Thread.currentThread().getContextClassloader()
(although this did not solve the problem).

> Some notes:
> 
> The class loader of a web application can be retrieved as
> Thread#getContextClassLoader()

Just tried this, didn't help. But it seems like the proper thing to do, so I'll leave it in.

> Also, if you run in development mode (see Jasper 
> configuration), each JSP
> is executed with its own class loader, so that they can be reloaded.
> http://tomcat.apache.org/tomcat-6.0-doc/jasper-howto.html

I don't think I've even gotten to a JSP yet; I'm running with Struts2, and the first thing
that the action tries to do is connect to the database.

> >> 4. Put the jar file into WEB-INF/lib, and then restart your
> >> web application.
> >>  (E.g., using Tomcat Manager).  Your jar library will be loaded.
> >> http://tomcat.apache.org/tomcat-6.0-doc/html-manager-howto.html
> >> http://tomcat.apache.org/tomcat-6.0-doc/manager-howto.html
> >
> > It is more of a plugin than a library. The location is 
> user-specified, a
> > directory to watch for plugin JARs. Each JAR contains 
> metadata in the
> > manifest describing the main class of the plugin. That main class is
> > loaded using Class.forName(), causing a static{} block in 
> the class to
> > run, performing any registrations necessary to integrate with the
> > application.
> >
> > When the class is needed (i.e. when I try to connect to a 
> database), a
> > new instance is created. That involves calling Class.forName()
> 
> Do you pass a ClassLoader reference to that forName() call?
> What is classloader hierarchy for the ClassLoader used by that call?

sun.misc.Launcher$ExtClassLoader --> sun.misc.Launcher$AppClassLoader --> org.apache.catalina.loader.StandardClassLoader
--> org.apache.catalina.loader.WebAppClassLoader --> java.net.FactoryURLClassLoader

I'm guessing that:
 * sun.misc.Launcher$ExtClassLoader is the bootstrap class loader
 * sun.misc.Launcher$AppClassLoader is the system class loader
 * org.apache.catalina.loader.StandardClassLoader is the Tomcat 'common' class loader
 * org.apache.catalina.loader.WebAppClassLoader is the Tomcat 'webapp' class loader for my
project
 * java.net.FactoryURLClassLoader is the class loader that I created to load classes out of
my JAR plugin

I was not passing a classloader to the Class.forName() call (to load org.postgresql.Driver),
but when I call

  Class.forName("org.postgresql.Driver", true, PostgreSQLDatabase.class.getClassLoader())

I still see the same result -- "java.lang.ClassNotFoundException: org.postgresql.Driver".
Also, I checked which classloader PostgreSQLDatabase.class.getClassLoader() was and it is
the URLClassLoader like I expected.

I also tried using Thread.currentThread.getContextClassLoader() (which is the Webapp classloader,
I checked) for the Class.forName call, but I got the same result.

> See
> http://java.sun.com/javase/6/docs/api/java/lang/Class.html#for
> Name(java.lang.String)
> http://java.sun.com/javase/6/docs/api/java/lang/Class.html#for
> Name(java.lang.String,%20boolean,%20java.lang.ClassLoader)
> 
> > to load
> > the PostgreSQL database driver (a class called 
> 'org.postgresql.Driver'),
> > contained in postgresql-8.3-604.jdbc4.jar (present in 
> WEB-INF/lib). But
> > that call to Class.forName() to load org.postgresql.Driver 
> fails because
> > the class cannot be found.
> >
> > This is confounding because an ancestor classloader of my 
> URLClassLoader
> > that made the classes in my plugin JAR available should 
> have access to
> > org.postgresql.Driver. In fact, one of them *must* because when the
> > classes in my plugin JAR are placed into WEB-INF/classes in 
> their raw
> > .class state (not packaged into a jar), everything works.
> >
> 
> It might be. What happens, step-by-step, in this case?  Additional
> classes will be picked up by web-app classloader instantly. Additional
> JARs - only upon restart of the web application, because it requires
> reconfiguration of the class loader (adds additional URLs to scan for
> classes).

When the classes are in WEB-INF/classes (instead of in a plugin JAR), here is a step-by-step:
 1) I modify the configuration file that specifies where plugin classes come from. The two
options are:
    a) Built-into the application, just use Class.forName(String)
    b) In plugin JARs, either specified as individual JAR files or as a directory to watch
for JARs
 2) When the app starts, it loads the configuration file. Since the config specifies that
the classes are built into the app, it calls Class.forName(String) on each specified class
name
 3) I request a new database for the provider specified in the configuration file (PostgreSQL
in this case) and, assuming it was constructed successfully, I use it to connect to the database.
This works here, but fails using the other method.

When the classes are in separate plugin JARs:
 1) I modify the configuration file to point to the specific JAR (I could point it at a directory,
but this is more straightforward)
 2) When the app starts, it loads the configuration file. The config specifies a plugin JAR
to load, so I read the metadata out of the jar to check
    a) Is it a plugin
    b) What is the class in that plugin that needs to be initialized (to register itself appropriately)
 3) I then construct a new URLClassLoader that points at that jar
 4) I call Class.forName(name of the main plugin class, true, the new URLClassLoader) to initialize
the class and allow it to perform its registration
 5) I request a new database... This is where it fails, because the constructor of PostgreSQLDatabase
(my main plugin class) is unable to load 'org.postgresql.Driver' using Class.forName() (either
version).

I have been re-deploying the webapp, or copying the plugins and restarting Tomcat any time
I make a change, so there is no class loader reconfiguration going in. When I tested with
the classes in WEB-INF/classes, I hand-edited the WAR to place those classes there and then
re-deployed.

> Best regards,
> Konstantin Kolinko

~Jonathan Pearson

---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@tomcat.apache.org
For additional commands, e-mail: users-help@tomcat.apache.org


Mime
View raw message