You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by "Len, Peter" <pl...@orionsci.com> on 2001/01/13 07:26:11 UTC

RE: TC4's classloader choking on xerces.jar (maybe)

Bob,

I don't really understand it but what I heard was that any xml jar that
you want to add to your context will need to go in the $TOMCAT_HOME/lib
dir rather that your context's WEB-INF/lib dir.  XML apparently is
considered differently that your other .jar files.  Again, I don't know
exactly why but that seesm to be the case.  I have my xerces.jar in the
$TOMCAT_HOME/lib and the rest of my jars in the WEB-INF/lib and
everything is fine.


FYI:  Tomcat 3.2 comes with a jaxp.jar file for its own use.  If you
just drop your xerces.jar file in that lib directory you may encounter
problems (as I had) because the jaxp.jar will come before the xerces.jar
file in the CLASSPATH.  I renamed my xerces.jar to a_xerces.jar so that
I know it gets loaded first and that I won't run into a conflict with
the same classes listed in both jar files.

Peter Len


Re: TC4's classloader choking on xerces.jar (maybe)

Posted by "Craig R. McClanahan" <Cr...@eng.sun.com>.
Gokul Singh wrote:

> ----- Original Message -----
> From: "Craig R. McClanahan" <Cr...@eng.sun.com>
>
> > Tomcat 4 follows the new rules in the servlet 2.3 PFD spec, which allows a
> > container to change this so that loading starts with your WEB-INF areas
> first.
> >
> > Consider the following scenario - I put a copy of the Postgres JDBC driver
> (just
> > to show that it's not specific to xml parsers :-) in my shared library
> > directory, because lots of my apps need it.  But, one of my webapps needs
> a
> > different version of the Postgres driver, because it depends on a new
> feature
> > that was implemented in a later version.  So, I put the new driver file in
> the
> > WEB-INF/lib directory of my webapp, and install it in Tomcat.
> >
> > Under Tomcat 3.2, the newer driver is ignored (because it's got the same
> class
> > names).  Under Tomcat 4.0, the newer driver is respected for that webap --
> all
> > others continue to use the shared one.
>
> Craig, I have a small doubt here.
>
> If I place the same jar file containing the class xyz.class in the
> tomcat/lib directory and in WEB-INF/lib directory, then will tomcat load the
> class from the tomcat/lib directory or WEB-INF/lib directory? If it is
> loaded from the WEB-INF/lib directory then this class gets loaded by a
> different classloader(webapp class loader) and is incompatible with the
> classes loaded from tomcat/lib.
>

In Tomcat 3.2, the "xyz" class would be loaded from the tomcat/lib directory, by
the system class loader.  Therefore, it would be shared across all webapps (and,
among other things, static variables would be crossing the boundaries).

In Tomcat 4.0, the "xyz" class would be loaded from the WEB-INF/lib directory,
and would be completely independent of the "xyz" class loaded by some other
webapp.

>
> Should the container not check the META-INF/MANIFEST.MF to find out if the
> versions are same and if the versions are same it loads the class from
> tomcat/lib directory.
>

There is a requirement somewhat like this in the 2.3 spec -- your JAR files can
declare dependencies on external shared libraries, using the "Extensions"
mechanism described in the JDK documentation bundle.  For example, you can
declare "my webapp requires JDBC driver Foo, version 1.3 or later, be made
available to me by the container because it is not in my WEB-INF/lib directory".

>
> Or do the specs mandate that any class in the WEB-INF/lib directory
> overrides class found in the tomcat/lib directory.
> Do the classes in WEB-INF/lib override the classes on the classpath in
> tomcat 4.0?
>

The precise spec wording is in Servlet Spec 2.3 (Proposed Final Draft), section
9.6.2, on page 62:

    The classloader that a container uses to load a servlet
    in a WAR must not allow the WAR to override JDK
    or Java Servlet API classes, and is recommended not to
    allow Servlets in the WAR visibility of the web container's
    implementation classes.

    If a web container has a mechanism for exposing
    container-wide library JARs to application classloaders,
    it is recommended that the application classloader be
    implemented in such a way that classes packaged within
    the WAR are able to override classes residing in
    container-wide library JARs.

Tomcat 4.0 follows these recommendations, although you can turn off the
"override" feature by an appropriate setting in server.xml if you want to.  In
particular, the Tomcat 4.0 mechanism for "exposing container-wide library JARs"
is that it creates a classloader that accesses all JAR files under the
$CATALINA_HOME/lib directory, and makes this classloader the parent of all
webapp classloaders.

The details of Tomcat 4.0's class loader architecture are defined in a document
in the source tree, at "catalina/docs/dev/classloaders.html".

>
> Regds,
> Gokul
>
> >
> > Craig McClanahan
> >
>

Craig



Re: TC4's classloader choking on xerces.jar (maybe)

Posted by Gokul Singh <go...@wipro.com>.
----- Original Message -----
From: "Craig R. McClanahan" <Cr...@eng.sun.com>


> Tomcat 4 follows the new rules in the servlet 2.3 PFD spec, which allows a
> container to change this so that loading starts with your WEB-INF areas
first.
>
> Consider the following scenario - I put a copy of the Postgres JDBC driver
(just
> to show that it's not specific to xml parsers :-) in my shared library
> directory, because lots of my apps need it.  But, one of my webapps needs
a
> different version of the Postgres driver, because it depends on a new
feature
> that was implemented in a later version.  So, I put the new driver file in
the
> WEB-INF/lib directory of my webapp, and install it in Tomcat.
>
> Under Tomcat 3.2, the newer driver is ignored (because it's got the same
class
> names).  Under Tomcat 4.0, the newer driver is respected for that webap --
all
> others continue to use the shared one.

Craig, I have a small doubt here.

If I place the same jar file containing the class xyz.class in the
tomcat/lib directory and in WEB-INF/lib directory, then will tomcat load the
class from the tomcat/lib directory or WEB-INF/lib directory? If it is
loaded from the WEB-INF/lib directory then this class gets loaded by a
different classloader(webapp class loader) and is incompatible with the
classes loaded from tomcat/lib.

Should the container not check the META-INF/MANIFEST.MF to find out if the
versions are same and if the versions are same it loads the class from
tomcat/lib directory.

Or do the specs mandate that any class in the WEB-INF/lib directory
overrides class found in the tomcat/lib directory.
Do the classes in WEB-INF/lib override the classes on the classpath in
tomcat 4.0?

Regds,
Gokul

>
> Craig McClanahan
>


Re: TC4's classloader choking on xerces.jar (maybe)

Posted by Peter Donald <do...@apache.org>.
At 04:34  21/1/01 -0800, Craig R. McClanahan wrote:
>The precise answer to what works depends on how your particular class
loader is
>implemented.  My experience (although this classloading stuff is "black
magic" at
>times) is that URLClassLoader, which is what Tomcat 4.0 uses, follows the
process
>described in the javadocs for java.lang.ClassLoader.loadClass():

Right - I wasn't talking about how the ClassLoader works but how
Class.forName() works. From experience I asuume that 

Class.forName( "Foo" );

is equivelent to 

Class.forName( "Foo", true, Thread.currentThread().getContextClassLoader() );

which seems to make more sens. If
Thread.currentThread().getContextClassLoader() is null then it gets it's
parent threads ContextClassLoader. If it reaches first thread then it uses
system classloader.

Cheers,

Pete

*-----------------------------------------------------*
| "Faced with the choice between changing one's mind, |
| and proving that there is no need to do so - almost |
| everyone gets busy on the proof."                   |
|              - John Kenneth Galbraith               |
*-----------------------------------------------------*


Re: TC4's classloader choking on xerces.jar (maybe)

Posted by "Craig R. McClanahan" <Cr...@eng.sun.com>.
Peter Donald wrote:

> At 04:08  21/1/01 -0800, Craig R. McClanahan wrote:
> >Sealing is one "user error" issue that will cause classloading to fail inside
> >Tomcat.  Another is the fact that a particular class can only see other
> classes
> >in its own classloaders, and parents of that classloader, but not
> children.  The
> >net effect is that some class libraries will NOT work unless you put them
> inside
> >WEB-INF/lib, because they need to be able to access other classes specific to
> >that webapp.
> >
> >Consider the following scenario:
> >* Class L is in a shared library ($TOMCAT_HOME/lib for Tomcat)
> >* Class W is in WEB-INF/classes or WEB-INF/lib/*.jar
> >* You pass the fully qualified class name of class W to a
> >  method in class L whose job is to instantiate a new object
> >  of this class.
> >* Class L does essentially the following:
> >        Class wClass = Class.forName(wClassName);
> >  and encounters ClassNotFoundException.
> >
> >The reason for the exception is that Class L was loaded from the shared
> library
> >classloader (which is the parent of all webapp classloaders in Tomcat).
> >Therefore, it cannot see into the webapp to find class W.
>
> I was under the impression that Class.forName() loaded classes from the
> context classloader.

Do you mean the one you set with Thread.setContextClassLoader()?  That's not the
way the standard ones work, although you could write such a beast (of course, this
is specific to Java2 as well).

The precise answer to what works depends on how your particular class loader is
implemented.  My experience (although this classloading stuff is "black magic" at
times) is that URLClassLoader, which is what Tomcat 4.0 uses, follows the process
described in the javadocs for java.lang.ClassLoader.loadClass():

* Call findLoadedClass(String) to see if this class
  has already been loaded by this class loader.

* Call loadClass() on the parent class loader of
  this class loader (NOTE:  if found, then that class's
  classloader will be our parent, not us).

* Attempt to load the class from our own repositories
  (i.e. the list of URLs it was initialized with for a
  URLClassLoader).



> So in theory the above would work if context
> classloader was set up correctly. The only thing that wouldn't work is
> something like
>
> Class wClass = getClass().getClassLoader().loadClass( wClassName );
>

> Cheers,
>
> Pete
>

Craig



Re: TC4's classloader choking on xerces.jar (maybe)

Posted by Peter Donald <do...@apache.org>.
At 04:08  21/1/01 -0800, Craig R. McClanahan wrote:
>Sealing is one "user error" issue that will cause classloading to fail inside
>Tomcat.  Another is the fact that a particular class can only see other
classes
>in its own classloaders, and parents of that classloader, but not
children.  The
>net effect is that some class libraries will NOT work unless you put them
inside
>WEB-INF/lib, because they need to be able to access other classes specific to
>that webapp.
>
>Consider the following scenario:
>* Class L is in a shared library ($TOMCAT_HOME/lib for Tomcat)
>* Class W is in WEB-INF/classes or WEB-INF/lib/*.jar
>* You pass the fully qualified class name of class W to a
>  method in class L whose job is to instantiate a new object
>  of this class.
>* Class L does essentially the following:
>        Class wClass = Class.forName(wClassName);
>  and encounters ClassNotFoundException.
>
>The reason for the exception is that Class L was loaded from the shared
library
>classloader (which is the parent of all webapp classloaders in Tomcat).
>Therefore, it cannot see into the webapp to find class W.

I was under the impression that Class.forName() loaded classes from the
context classloader. So in theory the above would work if context
classloader was set up correctly. The only thing that wouldn't work is
something like

Class wClass = getClass().getClassLoader().loadClass( wClassName );

Cheers,

Pete

*-----------------------------------------------------*
| "Faced with the choice between changing one's mind, |
| and proving that there is no need to do so - almost |
| everyone gets busy on the proof."                   |
|              - John Kenneth Galbraith               |
*-----------------------------------------------------*


Re: TC4's classloader choking on xerces.jar (maybe)

Posted by "Craig R. McClanahan" <Cr...@eng.sun.com>.
Aaron Mulder wrote:

> On Wed, 17 Jan 2001, Craig R. McClanahan wrote:
> > Consider the following scenario - I put a copy of the Postgres JDBC
> > driver (just to show that it's not specific to xml parsers :-) in my
> > shared library directory, because lots of my apps need it.  But, one
> > of my webapps needs a different version of the Postgres driver,
> > because it depends on a new feature that was implemented in a later
> > version.  So, I put the new driver file in the WEB-INF/lib directory
> > of my webapp, and install it in Tomcat.
> >
> > Under Tomcat 3.2, the newer driver is ignored (because it's got the
> > same class names).  Under Tomcat 4.0, the newer driver is respected
> > for that webap -- all others continue to use the shared one.
>
>         There is still the "sealing" issue, right?

Not directly ... you can get package sealing problems in stand-alone Java
applications as well, if you try to load classes in a particular package from
more than one JAR file, and the JAR is marked "sealed".

>  Certain libraries
> cannot be loaded in more than one ClassLoader, and are marked as "sealed"
> in the Manifest.  If you put one in the Tomcat lib and another in the
> webapp lib, you'll get cryptic "Sealing Violation" exceptions.  I think
> some of the XML JARs do this (JAXP, perhaps?).

Sealing is one "user error" issue that will cause classloading to fail inside
Tomcat.  Another is the fact that a particular class can only see other classes
in its own classloaders, and parents of that classloader, but not children.  The
net effect is that some class libraries will NOT work unless you put them inside
WEB-INF/lib, because they need to be able to access other classes specific to
that webapp.

Consider the following scenario:
* Class L is in a shared library ($TOMCAT_HOME/lib for Tomcat)
* Class W is in WEB-INF/classes or WEB-INF/lib/*.jar
* You pass the fully qualified class name of class W to a
  method in class L whose job is to instantiate a new object
  of this class.
* Class L does essentially the following:
        Class wClass = Class.forName(wClassName);
  and encounters ClassNotFoundException.

The reason for the exception is that Class L was loaded from the shared library
classloader (which is the parent of all webapp classloaders in Tomcat).
Therefore, it cannot see into the webapp to find class W.

This kind of pattern is very common when parsing XML files -- it's not the
parser itself that has the problem, it's the code you try to run in your SAX
event handlers -- but is not unique to them.

>
>         I don't have a full understanding of this, though.  Perhaps it
> only breaks if loaded more than once in serial ClassLoaders not parallel
> ones.  But I think tomcat's lib is sill in serial with webapp lib, right?
>

I'm not quite sure what you mean by "serial" with the webapp lib, but the
classloader used for $TOMCAT_HOME/lib is a *parent* classloader of the
classloader used for each web application.

>
> Aaron
>

Craig McClanahan



Re: TC4's classloader choking on xerces.jar (maybe)

Posted by Aaron Mulder <am...@alumni.princeton.edu>.
On Wed, 17 Jan 2001, Craig R. McClanahan wrote:
> Consider the following scenario - I put a copy of the Postgres JDBC
> driver (just to show that it's not specific to xml parsers :-) in my
> shared library directory, because lots of my apps need it.  But, one
> of my webapps needs a different version of the Postgres driver,
> because it depends on a new feature that was implemented in a later
> version.  So, I put the new driver file in the WEB-INF/lib directory
> of my webapp, and install it in Tomcat.
>
> Under Tomcat 3.2, the newer driver is ignored (because it's got the
> same class names).  Under Tomcat 4.0, the newer driver is respected
> for that webap -- all others continue to use the shared one.

	There is still the "sealing" issue, right?  Certain libraries
cannot be loaded in more than one ClassLoader, and are marked as "sealed"
in the Manifest.  If you put one in the Tomcat lib and another in the
webapp lib, you'll get cryptic "Sealing Violation" exceptions.  I think
some of the XML JARs do this (JAXP, perhaps?).
	I don't have a full understanding of this, though.  Perhaps it
only breaks if loaded more than once in serial ClassLoaders not parallel
ones.  But I think tomcat's lib is sill in serial with webapp lib, right?

Aaron


Re: TC4's classloader choking on xerces.jar (maybe)

Posted by "Craig R. McClanahan" <Cr...@eng.sun.com>.
"Len, Peter" wrote:

> Bob,
>
> I don't really understand it but what I heard was that any xml jar that
> you want to add to your context will need to go in the $TOMCAT_HOME/lib
> dir rather that your context's WEB-INF/lib dir.  XML apparently is
> considered differently that your other .jar files.  Again, I don't know
> exactly why but that seesm to be the case.  I have my xerces.jar in the
> $TOMCAT_HOME/lib and the rest of my jars in the WEB-INF/lib and
> everything is fine.
>

Actually, there is nothing special about XML jars versus others.  But what goes
in depends very much on which Tomcat version you are talking about.

Tomcat 3.2 follows the normal Java2 delegation model (simulated when running on
a JDK 1.1 system), so that if you ask for class a.b.c.foo, the loader looks in
the shared class path *first*, and the WEB-INF area *second*.  The net effect of
this is that you cannot override classes found in the shared area with the same
name -- even if you put your own JAR file in WEB-INF/lib.

Tomcat 4 follows the new rules in the servlet 2.3 PFD spec, which allows a
container to change this so that loading starts with your WEB-INF areas first.

Consider the following scenario - I put a copy of the Postgres JDBC driver (just
to show that it's not specific to xml parsers :-) in my shared library
directory, because lots of my apps need it.  But, one of my webapps needs a
different version of the Postgres driver, because it depends on a new feature
that was implemented in a later version.  So, I put the new driver file in the
WEB-INF/lib directory of my webapp, and install it in Tomcat.

Under Tomcat 3.2, the newer driver is ignored (because it's got the same class
names).  Under Tomcat 4.0, the newer driver is respected for that webap -- all
others continue to use the shared one.

>
> FYI:  Tomcat 3.2 comes with a jaxp.jar file for its own use.  If you
> just drop your xerces.jar file in that lib directory you may encounter
> problems (as I had) because the jaxp.jar will come before the xerces.jar
> file in the CLASSPATH.  I renamed my xerces.jar to a_xerces.jar so that
> I know it gets loaded first and that I won't run into a conflict with
> the same classes listed in both jar files.
>

If you want to use Xerces under Tomcat 3.2, you should put xerces.jar under
$TOMCAT_HOME/lib and remove jaxp.jar and parser.jar.

>
> Peter Len
>

Craig McClanahan