I'd said

> > I wish that classpaths didn't have to come up in a soap-user 
> > list at all,
> > but this seems to be the most common source of introductory 
> > problems and
> > there is sometimes something more subtle & nasty going on somewhere. \

and I asked for references, and Rick Hansen commented

>At http://www.javageeks.com/Papers/ there are severa good articles on
>classloader and such.

and this is true..and thanks...and some were helpful, but I'm still
thinking it would be nice to have some sort of a Classpath FAQ to
point to or (if brief enough) to include within existing FAQs,
something as simple as possible which will nonetheless deal with
the fact that sometimes, while playing with SOAP, you find that 
"being on the classpath" is not a simple notion; a class can be 
on the classpath and yet it can't be found by the class trying 
to load it. So far I think I see four basic types of classpath errors.
-----------------------------------------
Classpath FAQ version 0.00: (incomplete, long-winded, maybe wrong.)

   Q: why do I get this ClassNotFoundException, NoSuchMethodException, 
service not resolved, namespace blowup, &c, &c... error message? 
   A: your classpath is messed up. To fix it, look for the following:

type 0 classpath error: the class you're looking for is neither on 
the classpath nor in a jar within your JAVA_HOME's jre/lib/ext directory.
This will normally produce an actual ClassNotFoundException, but it may
be caught and retransmitted as service not resolved or other.
   type 0a: it's just plain simply not there, so add it.
   type 0b: it's in a class file, e.g. in 
       soap-2_2/samples/addressbook/GetAddress.class,
     and you put that addressbook directory on the classpath without
     noticing that it's packaged as  samples.addressbook.GetAddress.
     Put the soap-2_2 directory on the classpath instead.
   type 0c: it's in a jar file, and you put the directory containing
     the jar file on the classpath. Add the jar file itself instead.
   type 0d: it's in a jar file, it's explicitly on the classpath,
     but you packaged the jar from the wrong directory, e.g. in the
     addressbook directory you said 
        jar -cf address.jar *.class
     where you should have gone up two levels so you could say
        jar -cf address.jar samples/addressbook/*.class

Notice that with most of these errors, a newbie will say "it is on
the classpath, I checked" and some of the rest of us (ulp) will 
sometimes do that too.

type 1 classpath error: two such classes are available, but the wrong
one is found because it comes "earlier" on the classpath. This is a
biggie for SOAP; in the year 2001 it's a biggie for any namespace XML
processing because everybody has more than one XML parser around, and
they're not all equivalent. In SOAP, this is usually what people mean
by "put Xerces on the front of your classpath", but of course you can
have Xerces on the front of an explicit classpath and still get this
problem because another parser is somewhere in jre/lib/ext. This 
usually yields a NoSuchMethod or a namespace blowup, but it can yield a 
ClassNotFound, in fact it can produce any error at all (including no 
error message, just wrong output) because you've loaded the wrong class 
and it's doing the wrong thing.
   type 1a: a bad class is on the classpath in front of yours.
   type 1b: a bad class is in jre/lib/ext, so it gets loaded before yours.
To find these, you have to see what's in the directory or in the jar,
so use "jar -t" to get tables of contents, or to find a class named
com.sun.tools.javac.Main you can just look for the uncompressed text
string "com/sun/tools/javac/Main" with grep on Unix or ^F on a Windows
directory; remember that if you look for an interface like Node, it's
okay to find org/w3c/dom/Node mentioned in several jars, e.g. xerces.jar,
xalan.jar, Tidy.jar, and so forth, but if you're using jre/lib/ext then
no class should be defined more than once because you don't control the
order.

Type 0 (absence) and type 1 (ordering) classpath errors are common, but 
they are not the only types that mess up Apache SOAP, because it runs in
a servlet context within a hierarchy of class loaders: the extension
class loader which loads jre/lib/ext is not the same as the regular
class loader which loads what's explicitly on the classpath as the JVM
starts, and this is not the same as what loads servlet code; details
will vary from one servlet container to another.

Type 2 classpath error: your class is present on the classpath, it is 
the first definition of that class on the classpath, but it is still
not found because it's in the wrong place on the loader hierarchy.
This will usually generate a ClassNotFound or service not resolved.

To see how this happens, try defining a one-line TestLoadee.java:
   public class TestLoadee {}
and a four-line TestLoader.java:
   public class TestLoader {
     public static void main(String[]args)throws Exception{
       Class.forName("TestLoadee");
     }
   }
compile them both in the same directory, run "java TestLoader" and 
get no output because there's no problem, okay? As long as "." is 
on the classpath, they're found. Now put TestLoader into a jar, and 
raise the jar into jre/lib/ext: "java TestLoader" does a ClassNotFound 
exception even though you haven't touched TestLoadee.class. Raise
TestLoadee into its own jar, also in jre/lib/ext: we're back to
normal, TestLoader can find TestLoadee. Now drop TestLoader.jar
from jre/lib/ext; the TestLoader.class file on the explicit classpath
can find the TestLoadee file up in the jar within jre/lib/ext. We can 
look classes up, but my four-year-old's request to "look it down" will
never work in Java. (The regular classpath is above the WEB-INF 
directories in the same way.)

Similarly, if you have soap.jar inside jre/lib/ext, it will be
visible to samples/addressbook/AddressBook on the classpath, but
samples.addressbook.AddressBook will not be visible to soap.jar;
the service is on the classpath, it is the only definition of
samples.addressbook.AddressBook on the classpath, but it won't
be found. You can usually fix this by moving AddressBook up or
by moving soap.jar down. In the worst case, you can keep
absolutely everything inside jre/lib/ext and then all classes
can see each other, but of course you've thrown away the benefits
of multiple classloaders and you need to restart your JVM (i.e.,
Tomcat) every time you change anything. Moving soap.jar down
is more complicated than moving Addressbook up, e.g. you have
to make sure that it's visible to the compiler as well as
being visible to the servlet-runner, so you may have to put it
or its name in two places. For downwards adjustment, consider
Craig McLanahan's remarks at 
http://perl.jann.com/tomcat/200103/msg01657.html 

Type 3 classpath errors: you have more than one copy loading
of a Singleton class, e.g. a DatabaseManager class. Here you
should move the class upwards, if necessary to jre/lib/ext.
I haven't seen this one in a SOAP context, or at least if I
saw it I didn't know that's what it was. Consider

http://members.spree.com/education/developergrp/code/dynamic.htm

-------------------------------------------------------
Maybe somebody can think of a briefer, clearer way to put
that kind of issue, and put it on a FAQ...Jonathan? hmm.
I dunno.

Tom Myers

Reply via email to