> >> 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-unsubscr...@tomcat.apache.org
For additional commands, e-mail: users-h...@tomcat.apache.org

Reply via email to