You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@tomcat.apache.org by Vincent Massol <vm...@octo.com> on 2001/07/25 15:42:36 UTC

WebappClassLoader question

Hi,

Here is the situation :

* I have a class that makes use of JUnit (by extending the JUnit TestCase class). Let's call it ServletTestCase
* I have a second class that is used to call a method in ServletTestCase, let's call it MyProxyClass
* I have a third class (a servlet) that does _not_ make use of JUnit. Let's call it ServletTestRedirector. This class actually instanciate MyProxyClass and calls one of its method.
* I package these classes in a war file and I _don't_ include junit.jar in this war file

When I access the servlet, I get a ClassNotFoundException on a JUnit class. So far it is normal ...
When I debugged it, I have actually found that the error was happening when ServletTestRedirector was instancianting MyProxyClass (which does _not_ make use of JUnit) and before it was calling its method.

Here is the stack trace I got :

java.lang.NoClassDefFoundError: junit/framework/TestCase
 at java.lang.ClassLoader.defineClass0(Native Method)
 at java.lang.ClassLoader.defineClass(ClassLoader.java:486)
 at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:111)
 at org.apache.catalina.loader.WebappClassLoader.findClassInternal(WebappClassLoader.java:1475)
 at org.apache.catalina.loader.WebappClassLoader.findClass(WebappClassLoader.java:836)
 at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1215)
 at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1098)
 at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:313)
 at org.apache.commons.cactus.server.ServletTestRedirector.doPost(ServletTestRedirector.java:143)

Here is what I imagined is happening (tell me if this correct or wrong !) :

As MyProxyClass is within the war file, the WebappClassLoader gets called to load it. The WebappClassLoader, in trying to find out the correct class, actually loads some other class in memory, and thus the ServletTestCase, which fails to load because the junit jar is not in the classpath.

Is that correct ?
Don't you find it strange that the error about the missing class is reported when calling a class that has nothing to do with the problem ? It gets very hard to catch errors ...

For example, in MyProxyClass, the code that calls the ServletTestCase method is as follows :

        ServletTestCase testInstance = null;
        try {
            testClass = Class.forName(theClassName);
            Constructor constructor = testClass.getConstructor(new Class[] { String.class });
            testInstance = (ServletTestCase)constructor.newInstance(new Object[] { theMethod });
        } catch (Exception e) {
            logger.debug("Error instanciating class [" + theClassName + "]", e);
            e.printStackTrace();
            throw new ServletException("Error instanciating class [" + theClassName + "]", e);
        }

And there is never any exception caught here .... because the error happens earlier in the call stack, when the ServletTestRedirector instanciates MyProxyClass ...

... or am I missing something ? :)

Thanks
-Vincent Massol


Re: WebappClassLoader question

Posted by "Craig R. McClanahan" <cr...@apache.org>.
Vincent, can you send me (privately) a WAR file that illustrates your
difficulties?  But with the code below, and JUnit not visible to the web
app, I would expect to get the error when instantiating B, due to its
direct reference to D, which has a reference to JUnit.

From my experiments (and experience on other apps), it doesn't make any
difference whether the application classes are packed into JARs in
/WEB-INF/lib or unpacked in /WEB-INF/classes (since these are all loaded
by the web app class loader).

Craig

On Fri, 27 Jul 2001, Vincent Massol wrote:

> Hi Craig,
> 
> Thanks again. I have tried it again and it still doesn't work. I'll narrow a
> bit more my example.
> 
> C.java
> ---
> public class C extend D
> {
> ...
> }
> ---
> 
> D.java
> ---
> import junit.framework.*;
> 
> public class D extends TestCase
> {
> ...
> }
> ---
> 
> and the exact code in class B:
> 
> B.java
> ---
> public class B
> {
>   public B()
>   {
>   }
>   public doSomething()
>   {
>      D testInstance = null;
>       try {
>           testClass = Class.forName("C");
>           Constructor constructor = testClass.getConstructor(new Class[] {
> String.class });
>           testInstance = (D)constructor.newInstance(new Object[] {
> "something" });
>       } catch (Exception e) {
>         log("I would have thought here");
>       }
>   }
> }
> 
> The error I am getting between "before error" and "after error" is
> "java.lang.NoClassDefFoundError: junit/framework/TestCase".
> 
> ... so yes, class D (which depends on JUnit) is explicitely mentionned in
> class B. So, what you said in your first email is that it is normal
> (although yo have to agree it is very hard to debug and track and means I'll
> have to put a try catch around "B myB = new B()" in class A, which looks at
> bit surnatural ... :-) (I'll need to put a good comment to explain it at
> least!).
> 
> ... then in your second email you say it is working fine with the "I would
> have thought here" message printed ... so that's why I'm resending these
> additional details.
> 
> Classes A, B, and D are in a jar under WEB-INF/lib
> Class C is in WEB-INF/classes
> 
> Any idea. If not, I'll try to make the most simple example that reproduces
> what I have and I'll send it over.
> Thanks a lot.
> -Vincent
> 
> ----- Original Message -----
> From: "Craig R. McClanahan" <cr...@apache.org>
> To: <to...@jakarta.apache.org>
> Cc: "Vincent Massol" <vm...@octo.com>
> Sent: Thursday, July 26, 2001 10:08 PM
> Subject: Re: WebappClassLoader question
> 
> 
> 
> 
> On Thu, 26 Jul 2001, Craig R. McClanahan wrote:
> 
> > Hmm, I just tried a case like what you have below.  As long as B does not
> > explicitly reference class C, then it works.  In other words, in my
> > example where you've got the "// call the method by reflection" comment, I
> > added
> >
> >   Object c = myClass.newInstance();
> >
> > and got the class not found exception at "I would have thought here".  On
> > the other hand, if I changed the above line to:
> >
> >   C c = (C) myClass.newInstance();
> >
> > (and compiled with class C on the compiler classpath, but not in the
> > webapp), then I get the error in between "before error" and "after error".
> >
> > This makes sense, because the latter statement makes B explicitly
> > dependent on C, where the former doesn't.
> >
> > I'll mess around some more, playing with JAR-ing up some but not all the
> > classes involved.
> >
> 
> One more follow-up ... this works correctly for me with A and B in a jar
> file under /WEB-INF/lib, and C unpacked under /WEB-INF/classes as well.
> 
> > Craig
> >
> 
> Craig
> 
> 
> >
> > On Thu, 26 Jul 2001, Vincent Massol wrote:
> >
> > > Thanks Craig,
> > >
> > > However I am still not sure this mechanism explains the problem I had.
> It is
> > > not easy to describe in word so I'll write it in java code instead.
> > >
> > > A.java
> > > ----
> > > public class A implements HttpServlet
> > > {
> > >   public void doGet()
> > >   {
> > >     log("before error");
> > >     B myB = new B();
> > >     log("after error");
> > >   }
> > > }
> > > ----
> > >
> > > B.java
> > > ----
> > > public class B
> > > {
> > >   public B()
> > >   {
> > >   }
> > >   public doSomething()
> > >   {
> > >     try {
> > >       Class myClass = Class.forName("C");
> > >       // call the method by reflection ...
> > >     } catch (Exception e) {
> > >       log("I would have thought here");
> > >     }
> > >   }
> > > }
> > > ----
> > >
> > > C.java
> > > ----
> > > import junit.framework.*;
> > >
> > > public class C extends TestCase
> > > {
> > > ...
> > > }
> > > ----
> > >
> > > Now all of this is packaged in a war, classes A and B and in a jar put
> in
> > > WEB-INF/lib and class C is put in WEB-INF/classes. The junit jar is
> *not*
> > > put in WEB-INF/lib.
> > >
> > > Calling the servlet A result in an error occurring between the logs
> "before
> > > error" and "after error" and the log "I would have thought here" is
> never
> > > printed because the error happens _before_ ... This is what I don't
> > > understand.
> > >
> > > Any idea ?
> > > Thanks a lot
> > > -Vincent
> > >
> > > ----- Original Message -----
> > > From: "Craig R. McClanahan" <cr...@apache.org>
> > > To: <to...@jakarta.apache.org>; "Vincent Massol"
> <vm...@octo.com>
> > > Sent: Thursday, July 26, 2001 6:18 PM
> > > Subject: Re: WebappClassLoader question
> > >
> > >
> > > On Thu, 26 Jul 2001, Vincent Massol wrote:
> > >
> > > > Thanks Alex,
> > > >
> > > > I don't think the standard classloader mechanism is involved here. I
> > > believe
> > > > it is a 'feature' of Tomcat and more specifically of it's
> > > WebappClassLoader.
> > > > When you do standard java code and you have the following situation :
> > > >
> > > > 1st -> 2nd --- (using reflection) --> 3rd (not in classpath)
> > > >
> > > > then the error always happen in the 2nd class and you can put the code
> > > > between a try catch block and you'll be able to catch the
> > > > ClassNotFoundException. However what happens here is that the error
> > > happens
> > > > when calling a method on the 1st class that instanciates the 2nd class
> !
> > > > This is what I don't understand.
> > > >
> > > > Am I dreaming or is this a behaviour of Tomcat 4 ?
> > > >
> > >
> > > The web app class loader in Tomcat 4 is based on
> java.net.URLClassLoader,
> > > and has the same basic class loading behavior.  In particular, when a
> > > class A is loaded, all the classes that A directly references (i.e.
> listed
> > > in import statements, used as a variable declaration, and so on) are
> also
> > > loaded.  This is done recursively on the referenced classes, until the
> > > entire tree of references is resolved.  If the load for class A fails,
> you
> > > will get ClassNotFoundException.  However, if the load for one of the
> > > referenced classes fails, you will typically get NoClassDefError instead
> > > (and the class named in the error message may or may not be the one that
> > > is actually missing, which complicates debugging this problem
> tremendously
> > > :-).
> > >
> > > A good rule of thumb to avoid this kind of problem -- if you need to add
> > > classes to your CLASSPATH at compile time (when rebuilding the entire
> > > app), be sure all of those classes are visible to the web app at
> runtime.
> > >
> > > Using reflection, on the other hand, lets you defer loading of
> "dependent"
> > > classes until runtime, and you can deal with ClassNotFoundException
> errors
> > > that you might run into.  Note however that, even if you load a class
> > > dynamically, *that* class still contains direct references to other
> > > classes that must all be resolved in the manner described above.
> > >
> > >
> > > > Thanks
> > > > -Vincent
> > > >
> > >
> > > Craig McClanahan
> > >
> > > PS:  One place where the Tomcat 4 class loader *does* vary from the
> usual
> > > class loader behavior is in the order of places it looks to load a
> > > class.  The usual pattern in Java2 is to delegate to the parent class
> > > loader first, then look locally.  Tomcat 4 does the opposite -- it
> checks
> > > in /WEB-INF/classes and /WEB-INF/lib of your web application *before*
> > > looking up the parent class loader chain.  This means that, if you have
> a
> > > class in the $CATALINA_HOME/lib directory (shared across web apps), and
> a
> > > version of that same class in your web app, the version in your webapp
> > > wins.
> > >
> > >
> > > > ----- Original Message -----
> > > > From: "Alex Fernández" <af...@tid.es>
> > > > To: <to...@jakarta.apache.org>
> > > > Sent: Wednesday, July 25, 2001 3:44 PM
> > > > Subject: Re: WebappClassLoader question
> > > >
> > > >
> > > > > Hi Vincent!
> > > > >
> > > > > I've run into the same situation a couple of times, when one class
> uses
> > > > > a second class, and this second class uses a third one that is not
> > > > > present.
> > > > >
> > > > > 1st -> 2nd -> 3rd (missing)
> > > > >
> > > > > One would think that instantiating the 2nd should give an error, but
> > > > > that loading the 2nd and/or instantiating the 1st should be ok. In
> fact,
> > > > > all of the behaviors raise exceptions.
> > > > >
> > > > > The following paragraph in ClassLoader javadoc might be of help:
> > > > >
> > > > > "The methods and constructors of objects created by a class loader
> may
> > > > > reference other classes. To determine the class(es) referred to, the
> > > > > Java virtual machine calls the loadClass method of the class loader
> that
> > > > > originally created the class."
> > > > >
> > > > > Or, to find out what the JVM is doing, the spec is here:
> > > > > http://java.sun.com/docs/books/vmspec/
> > > > >
> > > > > Hope it helps.
> > > > >
> > > > > Un saludo,
> > > > >
> > > > > Alex.
> > > > >
> > > > > > Vincent Massol wrote:
> > > > > >
> > > > > > Hi,
> > > > > >
> > > > > > Here is the situation :
> > > > > >
> > > > > > * I have a class that makes use of JUnit (by extending the JUnit
> > > > > > TestCase class). Let's call it ServletTestCase
> > > > > > * I have a second class that is used to call a method in
> > > > > > ServletTestCase, let's call it MyProxyClass
> > > > > > * I have a third class (a servlet) that does _not_ make use of
> JUnit.
> > > > > > Let's call it ServletTestRedirector. This class actually
> instanciate
> > > > > > MyProxyClass and calls one of its method.
> > > > > > * I package these classes in a war file and I _don't_ include
> > > > > > junit.jar in this war file
> > > > > >
> > > > > > When I access the servlet, I get a ClassNotFoundException on a
> JUnit
> > > > > > class. So far it is normal ...
> > > > > > When I debugged it, I have actually found that the error was
> happening
> > > > > > when ServletTestRedirector was instancianting MyProxyClass (which
> does
> > > > > > _not_ make use of JUnit) and before it was calling its method.
> > > > > >
> > > > > > Here is the stack trace I got :
> > > > > >
> > > > > > java.lang.NoClassDefFoundError: junit/framework/TestCase
> > > > > >  at java.lang.ClassLoader.defineClass0(Native Method)
> > > > > >  at java.lang.ClassLoader.defineClass(ClassLoader.java:486)
> > > > > >  at
> > > > > >
> > > java.security.SecureClassLoader.defineClass(SecureClassLoader.java:111)
> > > > > >  at
> > > > > >
> > > >
> > >
> org.apache.catalina.loader.WebappClassLoader.findClassInternal(WebappClassLo
> > > > ader.java:1475)
> > > > > >  at
> > > > > >
> > > >
> > >
> org.apache.catalina.loader.WebappClassLoader.findClass(WebappClassLoader.jav
> > > > a:836)
> > > > > >  at
> > > > > >
> > > >
> > >
> org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.jav
> > > > a:1215)
> > > > > >  at
> > > > > >
> > > >
> > >
> org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.jav
> > > > a:1098)
> > > > > >  at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:313)
> > > > > >  at
> > > > > >
> > > >
> > >
> org.apache.commons.cactus.server.ServletTestRedirector.doPost(ServletTestRed
> > > > irector.java:143)
> > > > > > Here is what I imagined is happening (tell me if this correct or
> wrong
> > > > > > !) :
> > > > > >
> > > > > > As MyProxyClass is within the war file, the WebappClassLoader gets
> > > > > > called to load it. The WebappClassLoader, in trying to find out
> the
> > > > > > correct class, actually loads some other class in memory, and thus
> the
> > > > > > ServletTestCase, which fails to load because the junit jar is not
> in
> > > > > > the classpath.
> > > > > >
> > > > > > Is that correct ?
> > > > > > Don't you find it strange that the error about the missing class
> is
> > > > > > reported when calling a class that has nothing to do with the
> problem
> > > > > > ? It gets very hard to catch errors ...
> > > > > >
> > > > > > For example, in MyProxyClass, the code that calls the
> ServletTestCase
> > > > > > method is as follows :
> > > > > >
> > > > > >         ServletTestCase testInstance = null;
> > > > > >         try {
> > > > > >             testClass = Class.forName(theClassName);
> > > > > >             Constructor constructor = testClass.getConstructor(new
> > > > > > Class[] { String.class });
> > > > > >             testInstance =
> > > > > > (ServletTestCase)constructor.newInstance(new Object[] {
> theMethod });
> > > > > >         } catch (Exception e) {
> > > > > >             logger.debug("Error instanciating class [" +
> theClassName
> > > > > > + "]", e);
> > > > > >             e.printStackTrace();
> > > > > >             throw new ServletException("Error instanciating class
> [" +
> > > > > > theClassName + "]", e);
> > > > > >         }
> > > > > > And there is never any exception caught here .... because the
> error
> > > > > > happens earlier in the call stack, when the ServletTestRedirector
> > > > > > instanciates MyProxyClass ...
> > > > > >
> > > > > > ... or am I missing something ? :)
> > > > > >
> > > > > > Thanks
> > > > > > -Vincent Massol
> > > > > >
> > > > >
> > > >
> > > >
> > >
> > >
> > >
> > >
> >
> >
> 
> 
> 
> 



Re: WebappClassLoader question

Posted by Dmitri Colebatch <di...@bigpond.net.au>.
If B uses C and C extends D and D uses junit, then to do whatever it is
that B uses C for you will need the junit.jar.  I wasn't able to find
mention of where junit.jar is... sorry for my late joining of the thread -
is junit.jar in $TOMCAT_HOME/lib or WEB-INF/lib?

cheesr
dim

On Fri, 27 Jul 2001, Vincent Massol wrote:

> Hi Craig,
> 
> Thanks again. I have tried it again and it still doesn't work. I'll narrow a
> bit more my example.
> 
> C.java
> ---
> public class C extend D
> {
> ...
> }
> ---
> 
> D.java
> ---
> import junit.framework.*;
> 
> public class D extends TestCase
> {
> ...
> }
> ---
> 
> and the exact code in class B:
> 
> B.java
> ---
> public class B
> {
>   public B()
>   {
>   }
>   public doSomething()
>   {
>      D testInstance = null;
>       try {
>           testClass = Class.forName("C");
>           Constructor constructor = testClass.getConstructor(new Class[] {
> String.class });
>           testInstance = (D)constructor.newInstance(new Object[] {
> "something" });
>       } catch (Exception e) {
>         log("I would have thought here");
>       }
>   }
> }
> 
> The error I am getting between "before error" and "after error" is
> "java.lang.NoClassDefFoundError: junit/framework/TestCase".
> 
> ... so yes, class D (which depends on JUnit) is explicitely mentionned in
> class B. So, what you said in your first email is that it is normal
> (although yo have to agree it is very hard to debug and track and means I'll
> have to put a try catch around "B myB = new B()" in class A, which looks at
> bit surnatural ... :-) (I'll need to put a good comment to explain it at
> least!).
> 
> ... then in your second email you say it is working fine with the "I would
> have thought here" message printed ... so that's why I'm resending these
> additional details.
> 
> Classes A, B, and D are in a jar under WEB-INF/lib
> Class C is in WEB-INF/classes
> 
> Any idea. If not, I'll try to make the most simple example that reproduces
> what I have and I'll send it over.
> Thanks a lot.
> -Vincent
> 
> ----- Original Message -----
> From: "Craig R. McClanahan" <cr...@apache.org>
> To: <to...@jakarta.apache.org>
> Cc: "Vincent Massol" <vm...@octo.com>
> Sent: Thursday, July 26, 2001 10:08 PM
> Subject: Re: WebappClassLoader question
> 
> 
> 
> 
> On Thu, 26 Jul 2001, Craig R. McClanahan wrote:
> 
> > Hmm, I just tried a case like what you have below.  As long as B does not
> > explicitly reference class C, then it works.  In other words, in my
> > example where you've got the "// call the method by reflection" comment, I
> > added
> >
> >   Object c = myClass.newInstance();
> >
> > and got the class not found exception at "I would have thought here".  On
> > the other hand, if I changed the above line to:
> >
> >   C c = (C) myClass.newInstance();
> >
> > (and compiled with class C on the compiler classpath, but not in the
> > webapp), then I get the error in between "before error" and "after error".
> >
> > This makes sense, because the latter statement makes B explicitly
> > dependent on C, where the former doesn't.
> >
> > I'll mess around some more, playing with JAR-ing up some but not all the
> > classes involved.
> >
> 
> One more follow-up ... this works correctly for me with A and B in a jar
> file under /WEB-INF/lib, and C unpacked under /WEB-INF/classes as well.
> 
> > Craig
> >
> 
> Craig
> 
> 
> >
> > On Thu, 26 Jul 2001, Vincent Massol wrote:
> >
> > > Thanks Craig,
> > >
> > > However I am still not sure this mechanism explains the problem I had.
> It is
> > > not easy to describe in word so I'll write it in java code instead.
> > >
> > > A.java
> > > ----
> > > public class A implements HttpServlet
> > > {
> > >   public void doGet()
> > >   {
> > >     log("before error");
> > >     B myB = new B();
> > >     log("after error");
> > >   }
> > > }
> > > ----
> > >
> > > B.java
> > > ----
> > > public class B
> > > {
> > >   public B()
> > >   {
> > >   }
> > >   public doSomething()
> > >   {
> > >     try {
> > >       Class myClass = Class.forName("C");
> > >       // call the method by reflection ...
> > >     } catch (Exception e) {
> > >       log("I would have thought here");
> > >     }
> > >   }
> > > }
> > > ----
> > >
> > > C.java
> > > ----
> > > import junit.framework.*;
> > >
> > > public class C extends TestCase
> > > {
> > > ...
> > > }
> > > ----
> > >
> > > Now all of this is packaged in a war, classes A and B and in a jar put
> in
> > > WEB-INF/lib and class C is put in WEB-INF/classes. The junit jar is
> *not*
> > > put in WEB-INF/lib.
> > >
> > > Calling the servlet A result in an error occurring between the logs
> "before
> > > error" and "after error" and the log "I would have thought here" is
> never
> > > printed because the error happens _before_ ... This is what I don't
> > > understand.
> > >
> > > Any idea ?
> > > Thanks a lot
> > > -Vincent
> > >
> > > ----- Original Message -----
> > > From: "Craig R. McClanahan" <cr...@apache.org>
> > > To: <to...@jakarta.apache.org>; "Vincent Massol"
> <vm...@octo.com>
> > > Sent: Thursday, July 26, 2001 6:18 PM
> > > Subject: Re: WebappClassLoader question
> > >
> > >
> > > On Thu, 26 Jul 2001, Vincent Massol wrote:
> > >
> > > > Thanks Alex,
> > > >
> > > > I don't think the standard classloader mechanism is involved here. I
> > > believe
> > > > it is a 'feature' of Tomcat and more specifically of it's
> > > WebappClassLoader.
> > > > When you do standard java code and you have the following situation :
> > > >
> > > > 1st -> 2nd --- (using reflection) --> 3rd (not in classpath)
> > > >
> > > > then the error always happen in the 2nd class and you can put the code
> > > > between a try catch block and you'll be able to catch the
> > > > ClassNotFoundException. However what happens here is that the error
> > > happens
> > > > when calling a method on the 1st class that instanciates the 2nd class
> !
> > > > This is what I don't understand.
> > > >
> > > > Am I dreaming or is this a behaviour of Tomcat 4 ?
> > > >
> > >
> > > The web app class loader in Tomcat 4 is based on
> java.net.URLClassLoader,
> > > and has the same basic class loading behavior.  In particular, when a
> > > class A is loaded, all the classes that A directly references (i.e.
> listed
> > > in import statements, used as a variable declaration, and so on) are
> also
> > > loaded.  This is done recursively on the referenced classes, until the
> > > entire tree of references is resolved.  If the load for class A fails,
> you
> > > will get ClassNotFoundException.  However, if the load for one of the
> > > referenced classes fails, you will typically get NoClassDefError instead
> > > (and the class named in the error message may or may not be the one that
> > > is actually missing, which complicates debugging this problem
> tremendously
> > > :-).
> > >
> > > A good rule of thumb to avoid this kind of problem -- if you need to add
> > > classes to your CLASSPATH at compile time (when rebuilding the entire
> > > app), be sure all of those classes are visible to the web app at
> runtime.
> > >
> > > Using reflection, on the other hand, lets you defer loading of
> "dependent"
> > > classes until runtime, and you can deal with ClassNotFoundException
> errors
> > > that you might run into.  Note however that, even if you load a class
> > > dynamically, *that* class still contains direct references to other
> > > classes that must all be resolved in the manner described above.
> > >
> > >
> > > > Thanks
> > > > -Vincent
> > > >
> > >
> > > Craig McClanahan
> > >
> > > PS:  One place where the Tomcat 4 class loader *does* vary from the
> usual
> > > class loader behavior is in the order of places it looks to load a
> > > class.  The usual pattern in Java2 is to delegate to the parent class
> > > loader first, then look locally.  Tomcat 4 does the opposite -- it
> checks
> > > in /WEB-INF/classes and /WEB-INF/lib of your web application *before*
> > > looking up the parent class loader chain.  This means that, if you have
> a
> > > class in the $CATALINA_HOME/lib directory (shared across web apps), and
> a
> > > version of that same class in your web app, the version in your webapp
> > > wins.
> > >
> > >
> > > > ----- Original Message -----
> > > > From: "Alex Fern�ndez" <af...@tid.es>
> > > > To: <to...@jakarta.apache.org>
> > > > Sent: Wednesday, July 25, 2001 3:44 PM
> > > > Subject: Re: WebappClassLoader question
> > > >
> > > >
> > > > > Hi Vincent!
> > > > >
> > > > > I've run into the same situation a couple of times, when one class
> uses
> > > > > a second class, and this second class uses a third one that is not
> > > > > present.
> > > > >
> > > > > 1st -> 2nd -> 3rd (missing)
> > > > >
> > > > > One would think that instantiating the 2nd should give an error, but
> > > > > that loading the 2nd and/or instantiating the 1st should be ok. In
> fact,
> > > > > all of the behaviors raise exceptions.
> > > > >
> > > > > The following paragraph in ClassLoader javadoc might be of help:
> > > > >
> > > > > "The methods and constructors of objects created by a class loader
> may
> > > > > reference other classes. To determine the class(es) referred to, the
> > > > > Java virtual machine calls the loadClass method of the class loader
> that
> > > > > originally created the class."
> > > > >
> > > > > Or, to find out what the JVM is doing, the spec is here:
> > > > > http://java.sun.com/docs/books/vmspec/
> > > > >
> > > > > Hope it helps.
> > > > >
> > > > > Un saludo,
> > > > >
> > > > > Alex.
> > > > >
> > > > > > Vincent Massol wrote:
> > > > > >
> > > > > > Hi,
> > > > > >
> > > > > > Here is the situation :
> > > > > >
> > > > > > * I have a class that makes use of JUnit (by extending the JUnit
> > > > > > TestCase class). Let's call it ServletTestCase
> > > > > > * I have a second class that is used to call a method in
> > > > > > ServletTestCase, let's call it MyProxyClass
> > > > > > * I have a third class (a servlet) that does _not_ make use of
> JUnit.
> > > > > > Let's call it ServletTestRedirector. This class actually
> instanciate
> > > > > > MyProxyClass and calls one of its method.
> > > > > > * I package these classes in a war file and I _don't_ include
> > > > > > junit.jar in this war file
> > > > > >
> > > > > > When I access the servlet, I get a ClassNotFoundException on a
> JUnit
> > > > > > class. So far it is normal ...
> > > > > > When I debugged it, I have actually found that the error was
> happening
> > > > > > when ServletTestRedirector was instancianting MyProxyClass (which
> does
> > > > > > _not_ make use of JUnit) and before it was calling its method.
> > > > > >
> > > > > > Here is the stack trace I got :
> > > > > >
> > > > > > java.lang.NoClassDefFoundError: junit/framework/TestCase
> > > > > >  at java.lang.ClassLoader.defineClass0(Native Method)
> > > > > >  at java.lang.ClassLoader.defineClass(ClassLoader.java:486)
> > > > > >  at
> > > > > >
> > > java.security.SecureClassLoader.defineClass(SecureClassLoader.java:111)
> > > > > >  at
> > > > > >
> > > >
> > >
> org.apache.catalina.loader.WebappClassLoader.findClassInternal(WebappClassLo
> > > > ader.java:1475)
> > > > > >  at
> > > > > >
> > > >
> > >
> org.apache.catalina.loader.WebappClassLoader.findClass(WebappClassLoader.jav
> > > > a:836)
> > > > > >  at
> > > > > >
> > > >
> > >
> org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.jav
> > > > a:1215)
> > > > > >  at
> > > > > >
> > > >
> > >
> org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.jav
> > > > a:1098)
> > > > > >  at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:313)
> > > > > >  at
> > > > > >
> > > >
> > >
> org.apache.commons.cactus.server.ServletTestRedirector.doPost(ServletTestRed
> > > > irector.java:143)
> > > > > > Here is what I imagined is happening (tell me if this correct or
> wrong
> > > > > > !) :
> > > > > >
> > > > > > As MyProxyClass is within the war file, the WebappClassLoader gets
> > > > > > called to load it. The WebappClassLoader, in trying to find out
> the
> > > > > > correct class, actually loads some other class in memory, and thus
> the
> > > > > > ServletTestCase, which fails to load because the junit jar is not
> in
> > > > > > the classpath.
> > > > > >
> > > > > > Is that correct ?
> > > > > > Don't you find it strange that the error about the missing class
> is
> > > > > > reported when calling a class that has nothing to do with the
> problem
> > > > > > ? It gets very hard to catch errors ...
> > > > > >
> > > > > > For example, in MyProxyClass, the code that calls the
> ServletTestCase
> > > > > > method is as follows :
> > > > > >
> > > > > >         ServletTestCase testInstance = null;
> > > > > >         try {
> > > > > >             testClass = Class.forName(theClassName);
> > > > > >             Constructor constructor = testClass.getConstructor(new
> > > > > > Class[] { String.class });
> > > > > >             testInstance =
> > > > > > (ServletTestCase)constructor.newInstance(new Object[] {
> theMethod });
> > > > > >         } catch (Exception e) {
> > > > > >             logger.debug("Error instanciating class [" +
> theClassName
> > > > > > + "]", e);
> > > > > >             e.printStackTrace();
> > > > > >             throw new ServletException("Error instanciating class
> [" +
> > > > > > theClassName + "]", e);
> > > > > >         }
> > > > > > And there is never any exception caught here .... because the
> error
> > > > > > happens earlier in the call stack, when the ServletTestRedirector
> > > > > > instanciates MyProxyClass ...
> > > > > >
> > > > > > ... or am I missing something ? :)
> > > > > >
> > > > > > Thanks
> > > > > > -Vincent Massol
> > > > > >
> > > > >
> > > >
> > > >
> > >
> > >
> > >
> > >
> >
> >
> 
> 
> 
> 


Re: WebappClassLoader question

Posted by Vincent Massol <vm...@octo.com>.
Hi Craig,

Thanks again. I have tried it again and it still doesn't work. I'll narrow a
bit more my example.

C.java
---
public class C extend D
{
...
}
---

D.java
---
import junit.framework.*;

public class D extends TestCase
{
...
}
---

and the exact code in class B:

B.java
---
public class B
{
  public B()
  {
  }
  public doSomething()
  {
     D testInstance = null;
      try {
          testClass = Class.forName("C");
          Constructor constructor = testClass.getConstructor(new Class[] {
String.class });
          testInstance = (D)constructor.newInstance(new Object[] {
"something" });
      } catch (Exception e) {
        log("I would have thought here");
      }
  }
}

The error I am getting between "before error" and "after error" is
"java.lang.NoClassDefFoundError: junit/framework/TestCase".

... so yes, class D (which depends on JUnit) is explicitely mentionned in
class B. So, what you said in your first email is that it is normal
(although yo have to agree it is very hard to debug and track and means I'll
have to put a try catch around "B myB = new B()" in class A, which looks at
bit surnatural ... :-) (I'll need to put a good comment to explain it at
least!).

... then in your second email you say it is working fine with the "I would
have thought here" message printed ... so that's why I'm resending these
additional details.

Classes A, B, and D are in a jar under WEB-INF/lib
Class C is in WEB-INF/classes

Any idea. If not, I'll try to make the most simple example that reproduces
what I have and I'll send it over.
Thanks a lot.
-Vincent

----- Original Message -----
From: "Craig R. McClanahan" <cr...@apache.org>
To: <to...@jakarta.apache.org>
Cc: "Vincent Massol" <vm...@octo.com>
Sent: Thursday, July 26, 2001 10:08 PM
Subject: Re: WebappClassLoader question




On Thu, 26 Jul 2001, Craig R. McClanahan wrote:

> Hmm, I just tried a case like what you have below.  As long as B does not
> explicitly reference class C, then it works.  In other words, in my
> example where you've got the "// call the method by reflection" comment, I
> added
>
>   Object c = myClass.newInstance();
>
> and got the class not found exception at "I would have thought here".  On
> the other hand, if I changed the above line to:
>
>   C c = (C) myClass.newInstance();
>
> (and compiled with class C on the compiler classpath, but not in the
> webapp), then I get the error in between "before error" and "after error".
>
> This makes sense, because the latter statement makes B explicitly
> dependent on C, where the former doesn't.
>
> I'll mess around some more, playing with JAR-ing up some but not all the
> classes involved.
>

One more follow-up ... this works correctly for me with A and B in a jar
file under /WEB-INF/lib, and C unpacked under /WEB-INF/classes as well.

> Craig
>

Craig


>
> On Thu, 26 Jul 2001, Vincent Massol wrote:
>
> > Thanks Craig,
> >
> > However I am still not sure this mechanism explains the problem I had.
It is
> > not easy to describe in word so I'll write it in java code instead.
> >
> > A.java
> > ----
> > public class A implements HttpServlet
> > {
> >   public void doGet()
> >   {
> >     log("before error");
> >     B myB = new B();
> >     log("after error");
> >   }
> > }
> > ----
> >
> > B.java
> > ----
> > public class B
> > {
> >   public B()
> >   {
> >   }
> >   public doSomething()
> >   {
> >     try {
> >       Class myClass = Class.forName("C");
> >       // call the method by reflection ...
> >     } catch (Exception e) {
> >       log("I would have thought here");
> >     }
> >   }
> > }
> > ----
> >
> > C.java
> > ----
> > import junit.framework.*;
> >
> > public class C extends TestCase
> > {
> > ...
> > }
> > ----
> >
> > Now all of this is packaged in a war, classes A and B and in a jar put
in
> > WEB-INF/lib and class C is put in WEB-INF/classes. The junit jar is
*not*
> > put in WEB-INF/lib.
> >
> > Calling the servlet A result in an error occurring between the logs
"before
> > error" and "after error" and the log "I would have thought here" is
never
> > printed because the error happens _before_ ... This is what I don't
> > understand.
> >
> > Any idea ?
> > Thanks a lot
> > -Vincent
> >
> > ----- Original Message -----
> > From: "Craig R. McClanahan" <cr...@apache.org>
> > To: <to...@jakarta.apache.org>; "Vincent Massol"
<vm...@octo.com>
> > Sent: Thursday, July 26, 2001 6:18 PM
> > Subject: Re: WebappClassLoader question
> >
> >
> > On Thu, 26 Jul 2001, Vincent Massol wrote:
> >
> > > Thanks Alex,
> > >
> > > I don't think the standard classloader mechanism is involved here. I
> > believe
> > > it is a 'feature' of Tomcat and more specifically of it's
> > WebappClassLoader.
> > > When you do standard java code and you have the following situation :
> > >
> > > 1st -> 2nd --- (using reflection) --> 3rd (not in classpath)
> > >
> > > then the error always happen in the 2nd class and you can put the code
> > > between a try catch block and you'll be able to catch the
> > > ClassNotFoundException. However what happens here is that the error
> > happens
> > > when calling a method on the 1st class that instanciates the 2nd class
!
> > > This is what I don't understand.
> > >
> > > Am I dreaming or is this a behaviour of Tomcat 4 ?
> > >
> >
> > The web app class loader in Tomcat 4 is based on
java.net.URLClassLoader,
> > and has the same basic class loading behavior.  In particular, when a
> > class A is loaded, all the classes that A directly references (i.e.
listed
> > in import statements, used as a variable declaration, and so on) are
also
> > loaded.  This is done recursively on the referenced classes, until the
> > entire tree of references is resolved.  If the load for class A fails,
you
> > will get ClassNotFoundException.  However, if the load for one of the
> > referenced classes fails, you will typically get NoClassDefError instead
> > (and the class named in the error message may or may not be the one that
> > is actually missing, which complicates debugging this problem
tremendously
> > :-).
> >
> > A good rule of thumb to avoid this kind of problem -- if you need to add
> > classes to your CLASSPATH at compile time (when rebuilding the entire
> > app), be sure all of those classes are visible to the web app at
runtime.
> >
> > Using reflection, on the other hand, lets you defer loading of
"dependent"
> > classes until runtime, and you can deal with ClassNotFoundException
errors
> > that you might run into.  Note however that, even if you load a class
> > dynamically, *that* class still contains direct references to other
> > classes that must all be resolved in the manner described above.
> >
> >
> > > Thanks
> > > -Vincent
> > >
> >
> > Craig McClanahan
> >
> > PS:  One place where the Tomcat 4 class loader *does* vary from the
usual
> > class loader behavior is in the order of places it looks to load a
> > class.  The usual pattern in Java2 is to delegate to the parent class
> > loader first, then look locally.  Tomcat 4 does the opposite -- it
checks
> > in /WEB-INF/classes and /WEB-INF/lib of your web application *before*
> > looking up the parent class loader chain.  This means that, if you have
a
> > class in the $CATALINA_HOME/lib directory (shared across web apps), and
a
> > version of that same class in your web app, the version in your webapp
> > wins.
> >
> >
> > > ----- Original Message -----
> > > From: "Alex Fernández" <af...@tid.es>
> > > To: <to...@jakarta.apache.org>
> > > Sent: Wednesday, July 25, 2001 3:44 PM
> > > Subject: Re: WebappClassLoader question
> > >
> > >
> > > > Hi Vincent!
> > > >
> > > > I've run into the same situation a couple of times, when one class
uses
> > > > a second class, and this second class uses a third one that is not
> > > > present.
> > > >
> > > > 1st -> 2nd -> 3rd (missing)
> > > >
> > > > One would think that instantiating the 2nd should give an error, but
> > > > that loading the 2nd and/or instantiating the 1st should be ok. In
fact,
> > > > all of the behaviors raise exceptions.
> > > >
> > > > The following paragraph in ClassLoader javadoc might be of help:
> > > >
> > > > "The methods and constructors of objects created by a class loader
may
> > > > reference other classes. To determine the class(es) referred to, the
> > > > Java virtual machine calls the loadClass method of the class loader
that
> > > > originally created the class."
> > > >
> > > > Or, to find out what the JVM is doing, the spec is here:
> > > > http://java.sun.com/docs/books/vmspec/
> > > >
> > > > Hope it helps.
> > > >
> > > > Un saludo,
> > > >
> > > > Alex.
> > > >
> > > > > Vincent Massol wrote:
> > > > >
> > > > > Hi,
> > > > >
> > > > > Here is the situation :
> > > > >
> > > > > * I have a class that makes use of JUnit (by extending the JUnit
> > > > > TestCase class). Let's call it ServletTestCase
> > > > > * I have a second class that is used to call a method in
> > > > > ServletTestCase, let's call it MyProxyClass
> > > > > * I have a third class (a servlet) that does _not_ make use of
JUnit.
> > > > > Let's call it ServletTestRedirector. This class actually
instanciate
> > > > > MyProxyClass and calls one of its method.
> > > > > * I package these classes in a war file and I _don't_ include
> > > > > junit.jar in this war file
> > > > >
> > > > > When I access the servlet, I get a ClassNotFoundException on a
JUnit
> > > > > class. So far it is normal ...
> > > > > When I debugged it, I have actually found that the error was
happening
> > > > > when ServletTestRedirector was instancianting MyProxyClass (which
does
> > > > > _not_ make use of JUnit) and before it was calling its method.
> > > > >
> > > > > Here is the stack trace I got :
> > > > >
> > > > > java.lang.NoClassDefFoundError: junit/framework/TestCase
> > > > >  at java.lang.ClassLoader.defineClass0(Native Method)
> > > > >  at java.lang.ClassLoader.defineClass(ClassLoader.java:486)
> > > > >  at
> > > > >
> > java.security.SecureClassLoader.defineClass(SecureClassLoader.java:111)
> > > > >  at
> > > > >
> > >
> >
org.apache.catalina.loader.WebappClassLoader.findClassInternal(WebappClassLo
> > > ader.java:1475)
> > > > >  at
> > > > >
> > >
> >
org.apache.catalina.loader.WebappClassLoader.findClass(WebappClassLoader.jav
> > > a:836)
> > > > >  at
> > > > >
> > >
> >
org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.jav
> > > a:1215)
> > > > >  at
> > > > >
> > >
> >
org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.jav
> > > a:1098)
> > > > >  at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:313)
> > > > >  at
> > > > >
> > >
> >
org.apache.commons.cactus.server.ServletTestRedirector.doPost(ServletTestRed
> > > irector.java:143)
> > > > > Here is what I imagined is happening (tell me if this correct or
wrong
> > > > > !) :
> > > > >
> > > > > As MyProxyClass is within the war file, the WebappClassLoader gets
> > > > > called to load it. The WebappClassLoader, in trying to find out
the
> > > > > correct class, actually loads some other class in memory, and thus
the
> > > > > ServletTestCase, which fails to load because the junit jar is not
in
> > > > > the classpath.
> > > > >
> > > > > Is that correct ?
> > > > > Don't you find it strange that the error about the missing class
is
> > > > > reported when calling a class that has nothing to do with the
problem
> > > > > ? It gets very hard to catch errors ...
> > > > >
> > > > > For example, in MyProxyClass, the code that calls the
ServletTestCase
> > > > > method is as follows :
> > > > >
> > > > >         ServletTestCase testInstance = null;
> > > > >         try {
> > > > >             testClass = Class.forName(theClassName);
> > > > >             Constructor constructor = testClass.getConstructor(new
> > > > > Class[] { String.class });
> > > > >             testInstance =
> > > > > (ServletTestCase)constructor.newInstance(new Object[] {
theMethod });
> > > > >         } catch (Exception e) {
> > > > >             logger.debug("Error instanciating class [" +
theClassName
> > > > > + "]", e);
> > > > >             e.printStackTrace();
> > > > >             throw new ServletException("Error instanciating class
[" +
> > > > > theClassName + "]", e);
> > > > >         }
> > > > > And there is never any exception caught here .... because the
error
> > > > > happens earlier in the call stack, when the ServletTestRedirector
> > > > > instanciates MyProxyClass ...
> > > > >
> > > > > ... or am I missing something ? :)
> > > > >
> > > > > Thanks
> > > > > -Vincent Massol
> > > > >
> > > >
> > >
> > >
> >
> >
> >
> >
>
>




Re: WebappClassLoader question

Posted by "Craig R. McClanahan" <cr...@apache.org>.

On Thu, 26 Jul 2001, Craig R. McClanahan wrote:

> Hmm, I just tried a case like what you have below.  As long as B does not
> explicitly reference class C, then it works.  In other words, in my
> example where you've got the "// call the method by reflection" comment, I
> added
> 
>   Object c = myClass.newInstance();
> 
> and got the class not found exception at "I would have thought here".  On
> the other hand, if I changed the above line to:
> 
>   C c = (C) myClass.newInstance();
> 
> (and compiled with class C on the compiler classpath, but not in the
> webapp), then I get the error in between "before error" and "after error".
> 
> This makes sense, because the latter statement makes B explicitly
> dependent on C, where the former doesn't.
> 
> I'll mess around some more, playing with JAR-ing up some but not all the
> classes involved.
> 

One more follow-up ... this works correctly for me with A and B in a jar
file under /WEB-INF/lib, and C unpacked under /WEB-INF/classes as well.

> Craig
> 

Craig


> 
> On Thu, 26 Jul 2001, Vincent Massol wrote:
> 
> > Thanks Craig,
> > 
> > However I am still not sure this mechanism explains the problem I had. It is
> > not easy to describe in word so I'll write it in java code instead.
> > 
> > A.java
> > ----
> > public class A implements HttpServlet
> > {
> >   public void doGet()
> >   {
> >     log("before error");
> >     B myB = new B();
> >     log("after error");
> >   }
> > }
> > ----
> > 
> > B.java
> > ----
> > public class B
> > {
> >   public B()
> >   {
> >   }
> >   public doSomething()
> >   {
> >     try {
> >       Class myClass = Class.forName("C");
> >       // call the method by reflection ...
> >     } catch (Exception e) {
> >       log("I would have thought here");
> >     }
> >   }
> > }
> > ----
> > 
> > C.java
> > ----
> > import junit.framework.*;
> > 
> > public class C extends TestCase
> > {
> > ...
> > }
> > ----
> > 
> > Now all of this is packaged in a war, classes A and B and in a jar put in
> > WEB-INF/lib and class C is put in WEB-INF/classes. The junit jar is *not*
> > put in WEB-INF/lib.
> > 
> > Calling the servlet A result in an error occurring between the logs "before
> > error" and "after error" and the log "I would have thought here" is never
> > printed because the error happens _before_ ... This is what I don't
> > understand.
> > 
> > Any idea ?
> > Thanks a lot
> > -Vincent
> > 
> > ----- Original Message -----
> > From: "Craig R. McClanahan" <cr...@apache.org>
> > To: <to...@jakarta.apache.org>; "Vincent Massol" <vm...@octo.com>
> > Sent: Thursday, July 26, 2001 6:18 PM
> > Subject: Re: WebappClassLoader question
> > 
> > 
> > On Thu, 26 Jul 2001, Vincent Massol wrote:
> > 
> > > Thanks Alex,
> > >
> > > I don't think the standard classloader mechanism is involved here. I
> > believe
> > > it is a 'feature' of Tomcat and more specifically of it's
> > WebappClassLoader.
> > > When you do standard java code and you have the following situation :
> > >
> > > 1st -> 2nd --- (using reflection) --> 3rd (not in classpath)
> > >
> > > then the error always happen in the 2nd class and you can put the code
> > > between a try catch block and you'll be able to catch the
> > > ClassNotFoundException. However what happens here is that the error
> > happens
> > > when calling a method on the 1st class that instanciates the 2nd class !
> > > This is what I don't understand.
> > >
> > > Am I dreaming or is this a behaviour of Tomcat 4 ?
> > >
> > 
> > The web app class loader in Tomcat 4 is based on java.net.URLClassLoader,
> > and has the same basic class loading behavior.  In particular, when a
> > class A is loaded, all the classes that A directly references (i.e. listed
> > in import statements, used as a variable declaration, and so on) are also
> > loaded.  This is done recursively on the referenced classes, until the
> > entire tree of references is resolved.  If the load for class A fails, you
> > will get ClassNotFoundException.  However, if the load for one of the
> > referenced classes fails, you will typically get NoClassDefError instead
> > (and the class named in the error message may or may not be the one that
> > is actually missing, which complicates debugging this problem tremendously
> > :-).
> > 
> > A good rule of thumb to avoid this kind of problem -- if you need to add
> > classes to your CLASSPATH at compile time (when rebuilding the entire
> > app), be sure all of those classes are visible to the web app at runtime.
> > 
> > Using reflection, on the other hand, lets you defer loading of "dependent"
> > classes until runtime, and you can deal with ClassNotFoundException errors
> > that you might run into.  Note however that, even if you load a class
> > dynamically, *that* class still contains direct references to other
> > classes that must all be resolved in the manner described above.
> > 
> > 
> > > Thanks
> > > -Vincent
> > >
> > 
> > Craig McClanahan
> > 
> > PS:  One place where the Tomcat 4 class loader *does* vary from the usual
> > class loader behavior is in the order of places it looks to load a
> > class.  The usual pattern in Java2 is to delegate to the parent class
> > loader first, then look locally.  Tomcat 4 does the opposite -- it checks
> > in /WEB-INF/classes and /WEB-INF/lib of your web application *before*
> > looking up the parent class loader chain.  This means that, if you have a
> > class in the $CATALINA_HOME/lib directory (shared across web apps), and a
> > version of that same class in your web app, the version in your webapp
> > wins.
> > 
> > 
> > > ----- Original Message -----
> > > From: "Alex Fernández" <af...@tid.es>
> > > To: <to...@jakarta.apache.org>
> > > Sent: Wednesday, July 25, 2001 3:44 PM
> > > Subject: Re: WebappClassLoader question
> > >
> > >
> > > > Hi Vincent!
> > > >
> > > > I've run into the same situation a couple of times, when one class uses
> > > > a second class, and this second class uses a third one that is not
> > > > present.
> > > >
> > > > 1st -> 2nd -> 3rd (missing)
> > > >
> > > > One would think that instantiating the 2nd should give an error, but
> > > > that loading the 2nd and/or instantiating the 1st should be ok. In fact,
> > > > all of the behaviors raise exceptions.
> > > >
> > > > The following paragraph in ClassLoader javadoc might be of help:
> > > >
> > > > "The methods and constructors of objects created by a class loader may
> > > > reference other classes. To determine the class(es) referred to, the
> > > > Java virtual machine calls the loadClass method of the class loader that
> > > > originally created the class."
> > > >
> > > > Or, to find out what the JVM is doing, the spec is here:
> > > > http://java.sun.com/docs/books/vmspec/
> > > >
> > > > Hope it helps.
> > > >
> > > > Un saludo,
> > > >
> > > > Alex.
> > > >
> > > > > Vincent Massol wrote:
> > > > >
> > > > > Hi,
> > > > >
> > > > > Here is the situation :
> > > > >
> > > > > * I have a class that makes use of JUnit (by extending the JUnit
> > > > > TestCase class). Let's call it ServletTestCase
> > > > > * I have a second class that is used to call a method in
> > > > > ServletTestCase, let's call it MyProxyClass
> > > > > * I have a third class (a servlet) that does _not_ make use of JUnit.
> > > > > Let's call it ServletTestRedirector. This class actually instanciate
> > > > > MyProxyClass and calls one of its method.
> > > > > * I package these classes in a war file and I _don't_ include
> > > > > junit.jar in this war file
> > > > >
> > > > > When I access the servlet, I get a ClassNotFoundException on a JUnit
> > > > > class. So far it is normal ...
> > > > > When I debugged it, I have actually found that the error was happening
> > > > > when ServletTestRedirector was instancianting MyProxyClass (which does
> > > > > _not_ make use of JUnit) and before it was calling its method.
> > > > >
> > > > > Here is the stack trace I got :
> > > > >
> > > > > java.lang.NoClassDefFoundError: junit/framework/TestCase
> > > > >  at java.lang.ClassLoader.defineClass0(Native Method)
> > > > >  at java.lang.ClassLoader.defineClass(ClassLoader.java:486)
> > > > >  at
> > > > >
> > java.security.SecureClassLoader.defineClass(SecureClassLoader.java:111)
> > > > >  at
> > > > >
> > >
> > org.apache.catalina.loader.WebappClassLoader.findClassInternal(WebappClassLo
> > > ader.java:1475)
> > > > >  at
> > > > >
> > >
> > org.apache.catalina.loader.WebappClassLoader.findClass(WebappClassLoader.jav
> > > a:836)
> > > > >  at
> > > > >
> > >
> > org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.jav
> > > a:1215)
> > > > >  at
> > > > >
> > >
> > org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.jav
> > > a:1098)
> > > > >  at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:313)
> > > > >  at
> > > > >
> > >
> > org.apache.commons.cactus.server.ServletTestRedirector.doPost(ServletTestRed
> > > irector.java:143)
> > > > > Here is what I imagined is happening (tell me if this correct or wrong
> > > > > !) :
> > > > >
> > > > > As MyProxyClass is within the war file, the WebappClassLoader gets
> > > > > called to load it. The WebappClassLoader, in trying to find out the
> > > > > correct class, actually loads some other class in memory, and thus the
> > > > > ServletTestCase, which fails to load because the junit jar is not in
> > > > > the classpath.
> > > > >
> > > > > Is that correct ?
> > > > > Don't you find it strange that the error about the missing class is
> > > > > reported when calling a class that has nothing to do with the problem
> > > > > ? It gets very hard to catch errors ...
> > > > >
> > > > > For example, in MyProxyClass, the code that calls the ServletTestCase
> > > > > method is as follows :
> > > > >
> > > > >         ServletTestCase testInstance = null;
> > > > >         try {
> > > > >             testClass = Class.forName(theClassName);
> > > > >             Constructor constructor = testClass.getConstructor(new
> > > > > Class[] { String.class });
> > > > >             testInstance =
> > > > > (ServletTestCase)constructor.newInstance(new Object[] { theMethod });
> > > > >         } catch (Exception e) {
> > > > >             logger.debug("Error instanciating class [" + theClassName
> > > > > + "]", e);
> > > > >             e.printStackTrace();
> > > > >             throw new ServletException("Error instanciating class [" +
> > > > > theClassName + "]", e);
> > > > >         }
> > > > > And there is never any exception caught here .... because the error
> > > > > happens earlier in the call stack, when the ServletTestRedirector
> > > > > instanciates MyProxyClass ...
> > > > >
> > > > > ... or am I missing something ? :)
> > > > >
> > > > > Thanks
> > > > > -Vincent Massol
> > > > >
> > > >
> > >
> > >
> > 
> > 
> > 
> > 
> 
> 


Re: WebappClassLoader question

Posted by "Craig R. McClanahan" <cr...@apache.org>.
Hmm, I just tried a case like what you have below.  As long as B does not
explicitly reference class C, then it works.  In other words, in my
example where you've got the "// call the method by reflection" comment, I
added

  Object c = myClass.newInstance();

and got the class not found exception at "I would have thought here".  On
the other hand, if I changed the above line to:

  C c = (C) myClass.newInstance();

(and compiled with class C on the compiler classpath, but not in the
webapp), then I get the error in between "before error" and "after error".

This makes sense, because the latter statement makes B explicitly
dependent on C, where the former doesn't.

I'll mess around some more, playing with JAR-ing up some but not all the
classes involved.

Craig


On Thu, 26 Jul 2001, Vincent Massol wrote:

> Thanks Craig,
> 
> However I am still not sure this mechanism explains the problem I had. It is
> not easy to describe in word so I'll write it in java code instead.
> 
> A.java
> ----
> public class A implements HttpServlet
> {
>   public void doGet()
>   {
>     log("before error");
>     B myB = new B();
>     log("after error");
>   }
> }
> ----
> 
> B.java
> ----
> public class B
> {
>   public B()
>   {
>   }
>   public doSomething()
>   {
>     try {
>       Class myClass = Class.forName("C");
>       // call the method by reflection ...
>     } catch (Exception e) {
>       log("I would have thought here");
>     }
>   }
> }
> ----
> 
> C.java
> ----
> import junit.framework.*;
> 
> public class C extends TestCase
> {
> ...
> }
> ----
> 
> Now all of this is packaged in a war, classes A and B and in a jar put in
> WEB-INF/lib and class C is put in WEB-INF/classes. The junit jar is *not*
> put in WEB-INF/lib.
> 
> Calling the servlet A result in an error occurring between the logs "before
> error" and "after error" and the log "I would have thought here" is never
> printed because the error happens _before_ ... This is what I don't
> understand.
> 
> Any idea ?
> Thanks a lot
> -Vincent
> 
> ----- Original Message -----
> From: "Craig R. McClanahan" <cr...@apache.org>
> To: <to...@jakarta.apache.org>; "Vincent Massol" <vm...@octo.com>
> Sent: Thursday, July 26, 2001 6:18 PM
> Subject: Re: WebappClassLoader question
> 
> 
> On Thu, 26 Jul 2001, Vincent Massol wrote:
> 
> > Thanks Alex,
> >
> > I don't think the standard classloader mechanism is involved here. I
> believe
> > it is a 'feature' of Tomcat and more specifically of it's
> WebappClassLoader.
> > When you do standard java code and you have the following situation :
> >
> > 1st -> 2nd --- (using reflection) --> 3rd (not in classpath)
> >
> > then the error always happen in the 2nd class and you can put the code
> > between a try catch block and you'll be able to catch the
> > ClassNotFoundException. However what happens here is that the error
> happens
> > when calling a method on the 1st class that instanciates the 2nd class !
> > This is what I don't understand.
> >
> > Am I dreaming or is this a behaviour of Tomcat 4 ?
> >
> 
> The web app class loader in Tomcat 4 is based on java.net.URLClassLoader,
> and has the same basic class loading behavior.  In particular, when a
> class A is loaded, all the classes that A directly references (i.e. listed
> in import statements, used as a variable declaration, and so on) are also
> loaded.  This is done recursively on the referenced classes, until the
> entire tree of references is resolved.  If the load for class A fails, you
> will get ClassNotFoundException.  However, if the load for one of the
> referenced classes fails, you will typically get NoClassDefError instead
> (and the class named in the error message may or may not be the one that
> is actually missing, which complicates debugging this problem tremendously
> :-).
> 
> A good rule of thumb to avoid this kind of problem -- if you need to add
> classes to your CLASSPATH at compile time (when rebuilding the entire
> app), be sure all of those classes are visible to the web app at runtime.
> 
> Using reflection, on the other hand, lets you defer loading of "dependent"
> classes until runtime, and you can deal with ClassNotFoundException errors
> that you might run into.  Note however that, even if you load a class
> dynamically, *that* class still contains direct references to other
> classes that must all be resolved in the manner described above.
> 
> 
> > Thanks
> > -Vincent
> >
> 
> Craig McClanahan
> 
> PS:  One place where the Tomcat 4 class loader *does* vary from the usual
> class loader behavior is in the order of places it looks to load a
> class.  The usual pattern in Java2 is to delegate to the parent class
> loader first, then look locally.  Tomcat 4 does the opposite -- it checks
> in /WEB-INF/classes and /WEB-INF/lib of your web application *before*
> looking up the parent class loader chain.  This means that, if you have a
> class in the $CATALINA_HOME/lib directory (shared across web apps), and a
> version of that same class in your web app, the version in your webapp
> wins.
> 
> 
> > ----- Original Message -----
> > From: "Alex Fernández" <af...@tid.es>
> > To: <to...@jakarta.apache.org>
> > Sent: Wednesday, July 25, 2001 3:44 PM
> > Subject: Re: WebappClassLoader question
> >
> >
> > > Hi Vincent!
> > >
> > > I've run into the same situation a couple of times, when one class uses
> > > a second class, and this second class uses a third one that is not
> > > present.
> > >
> > > 1st -> 2nd -> 3rd (missing)
> > >
> > > One would think that instantiating the 2nd should give an error, but
> > > that loading the 2nd and/or instantiating the 1st should be ok. In fact,
> > > all of the behaviors raise exceptions.
> > >
> > > The following paragraph in ClassLoader javadoc might be of help:
> > >
> > > "The methods and constructors of objects created by a class loader may
> > > reference other classes. To determine the class(es) referred to, the
> > > Java virtual machine calls the loadClass method of the class loader that
> > > originally created the class."
> > >
> > > Or, to find out what the JVM is doing, the spec is here:
> > > http://java.sun.com/docs/books/vmspec/
> > >
> > > Hope it helps.
> > >
> > > Un saludo,
> > >
> > > Alex.
> > >
> > > > Vincent Massol wrote:
> > > >
> > > > Hi,
> > > >
> > > > Here is the situation :
> > > >
> > > > * I have a class that makes use of JUnit (by extending the JUnit
> > > > TestCase class). Let's call it ServletTestCase
> > > > * I have a second class that is used to call a method in
> > > > ServletTestCase, let's call it MyProxyClass
> > > > * I have a third class (a servlet) that does _not_ make use of JUnit.
> > > > Let's call it ServletTestRedirector. This class actually instanciate
> > > > MyProxyClass and calls one of its method.
> > > > * I package these classes in a war file and I _don't_ include
> > > > junit.jar in this war file
> > > >
> > > > When I access the servlet, I get a ClassNotFoundException on a JUnit
> > > > class. So far it is normal ...
> > > > When I debugged it, I have actually found that the error was happening
> > > > when ServletTestRedirector was instancianting MyProxyClass (which does
> > > > _not_ make use of JUnit) and before it was calling its method.
> > > >
> > > > Here is the stack trace I got :
> > > >
> > > > java.lang.NoClassDefFoundError: junit/framework/TestCase
> > > >  at java.lang.ClassLoader.defineClass0(Native Method)
> > > >  at java.lang.ClassLoader.defineClass(ClassLoader.java:486)
> > > >  at
> > > >
> java.security.SecureClassLoader.defineClass(SecureClassLoader.java:111)
> > > >  at
> > > >
> >
> org.apache.catalina.loader.WebappClassLoader.findClassInternal(WebappClassLo
> > ader.java:1475)
> > > >  at
> > > >
> >
> org.apache.catalina.loader.WebappClassLoader.findClass(WebappClassLoader.jav
> > a:836)
> > > >  at
> > > >
> >
> org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.jav
> > a:1215)
> > > >  at
> > > >
> >
> org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.jav
> > a:1098)
> > > >  at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:313)
> > > >  at
> > > >
> >
> org.apache.commons.cactus.server.ServletTestRedirector.doPost(ServletTestRed
> > irector.java:143)
> > > > Here is what I imagined is happening (tell me if this correct or wrong
> > > > !) :
> > > >
> > > > As MyProxyClass is within the war file, the WebappClassLoader gets
> > > > called to load it. The WebappClassLoader, in trying to find out the
> > > > correct class, actually loads some other class in memory, and thus the
> > > > ServletTestCase, which fails to load because the junit jar is not in
> > > > the classpath.
> > > >
> > > > Is that correct ?
> > > > Don't you find it strange that the error about the missing class is
> > > > reported when calling a class that has nothing to do with the problem
> > > > ? It gets very hard to catch errors ...
> > > >
> > > > For example, in MyProxyClass, the code that calls the ServletTestCase
> > > > method is as follows :
> > > >
> > > >         ServletTestCase testInstance = null;
> > > >         try {
> > > >             testClass = Class.forName(theClassName);
> > > >             Constructor constructor = testClass.getConstructor(new
> > > > Class[] { String.class });
> > > >             testInstance =
> > > > (ServletTestCase)constructor.newInstance(new Object[] { theMethod });
> > > >         } catch (Exception e) {
> > > >             logger.debug("Error instanciating class [" + theClassName
> > > > + "]", e);
> > > >             e.printStackTrace();
> > > >             throw new ServletException("Error instanciating class [" +
> > > > theClassName + "]", e);
> > > >         }
> > > > And there is never any exception caught here .... because the error
> > > > happens earlier in the call stack, when the ServletTestRedirector
> > > > instanciates MyProxyClass ...
> > > >
> > > > ... or am I missing something ? :)
> > > >
> > > > Thanks
> > > > -Vincent Massol
> > > >
> > >
> >
> >
> 
> 
> 
> 


Re: WebappClassLoader question

Posted by Vincent Massol <vm...@octo.com>.
Thanks Craig,

However I am still not sure this mechanism explains the problem I had. It is
not easy to describe in word so I'll write it in java code instead.

A.java
----
public class A implements HttpServlet
{
  public void doGet()
  {
    log("before error");
    B myB = new B();
    log("after error");
  }
}
----

B.java
----
public class B
{
  public B()
  {
  }
  public doSomething()
  {
    try {
      Class myClass = Class.forName("C");
      // call the method by reflection ...
    } catch (Exception e) {
      log("I would have thought here");
    }
  }
}
----

C.java
----
import junit.framework.*;

public class C extends TestCase
{
...
}
----

Now all of this is packaged in a war, classes A and B and in a jar put in
WEB-INF/lib and class C is put in WEB-INF/classes. The junit jar is *not*
put in WEB-INF/lib.

Calling the servlet A result in an error occurring between the logs "before
error" and "after error" and the log "I would have thought here" is never
printed because the error happens _before_ ... This is what I don't
understand.

Any idea ?
Thanks a lot
-Vincent

----- Original Message -----
From: "Craig R. McClanahan" <cr...@apache.org>
To: <to...@jakarta.apache.org>; "Vincent Massol" <vm...@octo.com>
Sent: Thursday, July 26, 2001 6:18 PM
Subject: Re: WebappClassLoader question


On Thu, 26 Jul 2001, Vincent Massol wrote:

> Thanks Alex,
>
> I don't think the standard classloader mechanism is involved here. I
believe
> it is a 'feature' of Tomcat and more specifically of it's
WebappClassLoader.
> When you do standard java code and you have the following situation :
>
> 1st -> 2nd --- (using reflection) --> 3rd (not in classpath)
>
> then the error always happen in the 2nd class and you can put the code
> between a try catch block and you'll be able to catch the
> ClassNotFoundException. However what happens here is that the error
happens
> when calling a method on the 1st class that instanciates the 2nd class !
> This is what I don't understand.
>
> Am I dreaming or is this a behaviour of Tomcat 4 ?
>

The web app class loader in Tomcat 4 is based on java.net.URLClassLoader,
and has the same basic class loading behavior.  In particular, when a
class A is loaded, all the classes that A directly references (i.e. listed
in import statements, used as a variable declaration, and so on) are also
loaded.  This is done recursively on the referenced classes, until the
entire tree of references is resolved.  If the load for class A fails, you
will get ClassNotFoundException.  However, if the load for one of the
referenced classes fails, you will typically get NoClassDefError instead
(and the class named in the error message may or may not be the one that
is actually missing, which complicates debugging this problem tremendously
:-).

A good rule of thumb to avoid this kind of problem -- if you need to add
classes to your CLASSPATH at compile time (when rebuilding the entire
app), be sure all of those classes are visible to the web app at runtime.

Using reflection, on the other hand, lets you defer loading of "dependent"
classes until runtime, and you can deal with ClassNotFoundException errors
that you might run into.  Note however that, even if you load a class
dynamically, *that* class still contains direct references to other
classes that must all be resolved in the manner described above.


> Thanks
> -Vincent
>

Craig McClanahan

PS:  One place where the Tomcat 4 class loader *does* vary from the usual
class loader behavior is in the order of places it looks to load a
class.  The usual pattern in Java2 is to delegate to the parent class
loader first, then look locally.  Tomcat 4 does the opposite -- it checks
in /WEB-INF/classes and /WEB-INF/lib of your web application *before*
looking up the parent class loader chain.  This means that, if you have a
class in the $CATALINA_HOME/lib directory (shared across web apps), and a
version of that same class in your web app, the version in your webapp
wins.


> ----- Original Message -----
> From: "Alex Fernández" <af...@tid.es>
> To: <to...@jakarta.apache.org>
> Sent: Wednesday, July 25, 2001 3:44 PM
> Subject: Re: WebappClassLoader question
>
>
> > Hi Vincent!
> >
> > I've run into the same situation a couple of times, when one class uses
> > a second class, and this second class uses a third one that is not
> > present.
> >
> > 1st -> 2nd -> 3rd (missing)
> >
> > One would think that instantiating the 2nd should give an error, but
> > that loading the 2nd and/or instantiating the 1st should be ok. In fact,
> > all of the behaviors raise exceptions.
> >
> > The following paragraph in ClassLoader javadoc might be of help:
> >
> > "The methods and constructors of objects created by a class loader may
> > reference other classes. To determine the class(es) referred to, the
> > Java virtual machine calls the loadClass method of the class loader that
> > originally created the class."
> >
> > Or, to find out what the JVM is doing, the spec is here:
> > http://java.sun.com/docs/books/vmspec/
> >
> > Hope it helps.
> >
> > Un saludo,
> >
> > Alex.
> >
> > > Vincent Massol wrote:
> > >
> > > Hi,
> > >
> > > Here is the situation :
> > >
> > > * I have a class that makes use of JUnit (by extending the JUnit
> > > TestCase class). Let's call it ServletTestCase
> > > * I have a second class that is used to call a method in
> > > ServletTestCase, let's call it MyProxyClass
> > > * I have a third class (a servlet) that does _not_ make use of JUnit.
> > > Let's call it ServletTestRedirector. This class actually instanciate
> > > MyProxyClass and calls one of its method.
> > > * I package these classes in a war file and I _don't_ include
> > > junit.jar in this war file
> > >
> > > When I access the servlet, I get a ClassNotFoundException on a JUnit
> > > class. So far it is normal ...
> > > When I debugged it, I have actually found that the error was happening
> > > when ServletTestRedirector was instancianting MyProxyClass (which does
> > > _not_ make use of JUnit) and before it was calling its method.
> > >
> > > Here is the stack trace I got :
> > >
> > > java.lang.NoClassDefFoundError: junit/framework/TestCase
> > >  at java.lang.ClassLoader.defineClass0(Native Method)
> > >  at java.lang.ClassLoader.defineClass(ClassLoader.java:486)
> > >  at
> > >
java.security.SecureClassLoader.defineClass(SecureClassLoader.java:111)
> > >  at
> > >
>
org.apache.catalina.loader.WebappClassLoader.findClassInternal(WebappClassLo
> ader.java:1475)
> > >  at
> > >
>
org.apache.catalina.loader.WebappClassLoader.findClass(WebappClassLoader.jav
> a:836)
> > >  at
> > >
>
org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.jav
> a:1215)
> > >  at
> > >
>
org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.jav
> a:1098)
> > >  at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:313)
> > >  at
> > >
>
org.apache.commons.cactus.server.ServletTestRedirector.doPost(ServletTestRed
> irector.java:143)
> > > Here is what I imagined is happening (tell me if this correct or wrong
> > > !) :
> > >
> > > As MyProxyClass is within the war file, the WebappClassLoader gets
> > > called to load it. The WebappClassLoader, in trying to find out the
> > > correct class, actually loads some other class in memory, and thus the
> > > ServletTestCase, which fails to load because the junit jar is not in
> > > the classpath.
> > >
> > > Is that correct ?
> > > Don't you find it strange that the error about the missing class is
> > > reported when calling a class that has nothing to do with the problem
> > > ? It gets very hard to catch errors ...
> > >
> > > For example, in MyProxyClass, the code that calls the ServletTestCase
> > > method is as follows :
> > >
> > >         ServletTestCase testInstance = null;
> > >         try {
> > >             testClass = Class.forName(theClassName);
> > >             Constructor constructor = testClass.getConstructor(new
> > > Class[] { String.class });
> > >             testInstance =
> > > (ServletTestCase)constructor.newInstance(new Object[] { theMethod });
> > >         } catch (Exception e) {
> > >             logger.debug("Error instanciating class [" + theClassName
> > > + "]", e);
> > >             e.printStackTrace();
> > >             throw new ServletException("Error instanciating class [" +
> > > theClassName + "]", e);
> > >         }
> > > And there is never any exception caught here .... because the error
> > > happens earlier in the call stack, when the ServletTestRedirector
> > > instanciates MyProxyClass ...
> > >
> > > ... or am I missing something ? :)
> > >
> > > Thanks
> > > -Vincent Massol
> > >
> >
>
>




Re: WebappClassLoader question

Posted by "Craig R. McClanahan" <cr...@apache.org>.
On Thu, 26 Jul 2001, Vincent Massol wrote:

> Thanks Alex,
> 
> I don't think the standard classloader mechanism is involved here. I believe
> it is a 'feature' of Tomcat and more specifically of it's WebappClassLoader.
> When you do standard java code and you have the following situation :
> 
> 1st -> 2nd --- (using reflection) --> 3rd (not in classpath)
> 
> then the error always happen in the 2nd class and you can put the code
> between a try catch block and you'll be able to catch the
> ClassNotFoundException. However what happens here is that the error happens
> when calling a method on the 1st class that instanciates the 2nd class !
> This is what I don't understand.
> 
> Am I dreaming or is this a behaviour of Tomcat 4 ?
> 

The web app class loader in Tomcat 4 is based on java.net.URLClassLoader,
and has the same basic class loading behavior.  In particular, when a
class A is loaded, all the classes that A directly references (i.e. listed
in import statements, used as a variable declaration, and so on) are also
loaded.  This is done recursively on the referenced classes, until the
entire tree of references is resolved.  If the load for class A fails, you
will get ClassNotFoundException.  However, if the load for one of the
referenced classes fails, you will typically get NoClassDefError instead
(and the class named in the error message may or may not be the one that
is actually missing, which complicates debugging this problem tremendously
:-).

A good rule of thumb to avoid this kind of problem -- if you need to add
classes to your CLASSPATH at compile time (when rebuilding the entire
app), be sure all of those classes are visible to the web app at runtime.

Using reflection, on the other hand, lets you defer loading of "dependent"
classes until runtime, and you can deal with ClassNotFoundException errors
that you might run into.  Note however that, even if you load a class
dynamically, *that* class still contains direct references to other
classes that must all be resolved in the manner described above.


> Thanks
> -Vincent
> 

Craig McClanahan

PS:  One place where the Tomcat 4 class loader *does* vary from the usual
class loader behavior is in the order of places it looks to load a
class.  The usual pattern in Java2 is to delegate to the parent class
loader first, then look locally.  Tomcat 4 does the opposite -- it checks
in /WEB-INF/classes and /WEB-INF/lib of your web application *before*
looking up the parent class loader chain.  This means that, if you have a
class in the $CATALINA_HOME/lib directory (shared across web apps), and a
version of that same class in your web app, the version in your webapp
wins.


> ----- Original Message -----
> From: "Alex Fernández" <af...@tid.es>
> To: <to...@jakarta.apache.org>
> Sent: Wednesday, July 25, 2001 3:44 PM
> Subject: Re: WebappClassLoader question
> 
> 
> > Hi Vincent!
> >
> > I've run into the same situation a couple of times, when one class uses
> > a second class, and this second class uses a third one that is not
> > present.
> >
> > 1st -> 2nd -> 3rd (missing)
> >
> > One would think that instantiating the 2nd should give an error, but
> > that loading the 2nd and/or instantiating the 1st should be ok. In fact,
> > all of the behaviors raise exceptions.
> >
> > The following paragraph in ClassLoader javadoc might be of help:
> >
> > "The methods and constructors of objects created by a class loader may
> > reference other classes. To determine the class(es) referred to, the
> > Java virtual machine calls the loadClass method of the class loader that
> > originally created the class."
> >
> > Or, to find out what the JVM is doing, the spec is here:
> > http://java.sun.com/docs/books/vmspec/
> >
> > Hope it helps.
> >
> > Un saludo,
> >
> > Alex.
> >
> > > Vincent Massol wrote:
> > >
> > > Hi,
> > >
> > > Here is the situation :
> > >
> > > * I have a class that makes use of JUnit (by extending the JUnit
> > > TestCase class). Let's call it ServletTestCase
> > > * I have a second class that is used to call a method in
> > > ServletTestCase, let's call it MyProxyClass
> > > * I have a third class (a servlet) that does _not_ make use of JUnit.
> > > Let's call it ServletTestRedirector. This class actually instanciate
> > > MyProxyClass and calls one of its method.
> > > * I package these classes in a war file and I _don't_ include
> > > junit.jar in this war file
> > >
> > > When I access the servlet, I get a ClassNotFoundException on a JUnit
> > > class. So far it is normal ...
> > > When I debugged it, I have actually found that the error was happening
> > > when ServletTestRedirector was instancianting MyProxyClass (which does
> > > _not_ make use of JUnit) and before it was calling its method.
> > >
> > > Here is the stack trace I got :
> > >
> > > java.lang.NoClassDefFoundError: junit/framework/TestCase
> > >  at java.lang.ClassLoader.defineClass0(Native Method)
> > >  at java.lang.ClassLoader.defineClass(ClassLoader.java:486)
> > >  at
> > > java.security.SecureClassLoader.defineClass(SecureClassLoader.java:111)
> > >  at
> > >
> org.apache.catalina.loader.WebappClassLoader.findClassInternal(WebappClassLo
> ader.java:1475)
> > >  at
> > >
> org.apache.catalina.loader.WebappClassLoader.findClass(WebappClassLoader.jav
> a:836)
> > >  at
> > >
> org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.jav
> a:1215)
> > >  at
> > >
> org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.jav
> a:1098)
> > >  at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:313)
> > >  at
> > >
> org.apache.commons.cactus.server.ServletTestRedirector.doPost(ServletTestRed
> irector.java:143)
> > > Here is what I imagined is happening (tell me if this correct or wrong
> > > !) :
> > >
> > > As MyProxyClass is within the war file, the WebappClassLoader gets
> > > called to load it. The WebappClassLoader, in trying to find out the
> > > correct class, actually loads some other class in memory, and thus the
> > > ServletTestCase, which fails to load because the junit jar is not in
> > > the classpath.
> > >
> > > Is that correct ?
> > > Don't you find it strange that the error about the missing class is
> > > reported when calling a class that has nothing to do with the problem
> > > ? It gets very hard to catch errors ...
> > >
> > > For example, in MyProxyClass, the code that calls the ServletTestCase
> > > method is as follows :
> > >
> > >         ServletTestCase testInstance = null;
> > >         try {
> > >             testClass = Class.forName(theClassName);
> > >             Constructor constructor = testClass.getConstructor(new
> > > Class[] { String.class });
> > >             testInstance =
> > > (ServletTestCase)constructor.newInstance(new Object[] { theMethod });
> > >         } catch (Exception e) {
> > >             logger.debug("Error instanciating class [" + theClassName
> > > + "]", e);
> > >             e.printStackTrace();
> > >             throw new ServletException("Error instanciating class [" +
> > > theClassName + "]", e);
> > >         }
> > > And there is never any exception caught here .... because the error
> > > happens earlier in the call stack, when the ServletTestRedirector
> > > instanciates MyProxyClass ...
> > >
> > > ... or am I missing something ? :)
> > >
> > > Thanks
> > > -Vincent Massol
> > >
> >
> 
> 


Re: WebappClassLoader question

Posted by Vincent Massol <vm...@octo.com>.
Thanks Alex,

I don't think the standard classloader mechanism is involved here. I believe
it is a 'feature' of Tomcat and more specifically of it's WebappClassLoader.
When you do standard java code and you have the following situation :

1st -> 2nd --- (using reflection) --> 3rd (not in classpath)

then the error always happen in the 2nd class and you can put the code
between a try catch block and you'll be able to catch the
ClassNotFoundException. However what happens here is that the error happens
when calling a method on the 1st class that instanciates the 2nd class !
This is what I don't understand.

Am I dreaming or is this a behaviour of Tomcat 4 ?

Thanks
-Vincent

----- Original Message -----
From: "Alex Fernández" <af...@tid.es>
To: <to...@jakarta.apache.org>
Sent: Wednesday, July 25, 2001 3:44 PM
Subject: Re: WebappClassLoader question


> Hi Vincent!
>
> I've run into the same situation a couple of times, when one class uses
> a second class, and this second class uses a third one that is not
> present.
>
> 1st -> 2nd -> 3rd (missing)
>
> One would think that instantiating the 2nd should give an error, but
> that loading the 2nd and/or instantiating the 1st should be ok. In fact,
> all of the behaviors raise exceptions.
>
> The following paragraph in ClassLoader javadoc might be of help:
>
> "The methods and constructors of objects created by a class loader may
> reference other classes. To determine the class(es) referred to, the
> Java virtual machine calls the loadClass method of the class loader that
> originally created the class."
>
> Or, to find out what the JVM is doing, the spec is here:
> http://java.sun.com/docs/books/vmspec/
>
> Hope it helps.
>
> Un saludo,
>
> Alex.
>
> > Vincent Massol wrote:
> >
> > Hi,
> >
> > Here is the situation :
> >
> > * I have a class that makes use of JUnit (by extending the JUnit
> > TestCase class). Let's call it ServletTestCase
> > * I have a second class that is used to call a method in
> > ServletTestCase, let's call it MyProxyClass
> > * I have a third class (a servlet) that does _not_ make use of JUnit.
> > Let's call it ServletTestRedirector. This class actually instanciate
> > MyProxyClass and calls one of its method.
> > * I package these classes in a war file and I _don't_ include
> > junit.jar in this war file
> >
> > When I access the servlet, I get a ClassNotFoundException on a JUnit
> > class. So far it is normal ...
> > When I debugged it, I have actually found that the error was happening
> > when ServletTestRedirector was instancianting MyProxyClass (which does
> > _not_ make use of JUnit) and before it was calling its method.
> >
> > Here is the stack trace I got :
> >
> > java.lang.NoClassDefFoundError: junit/framework/TestCase
> >  at java.lang.ClassLoader.defineClass0(Native Method)
> >  at java.lang.ClassLoader.defineClass(ClassLoader.java:486)
> >  at
> > java.security.SecureClassLoader.defineClass(SecureClassLoader.java:111)
> >  at
> >
org.apache.catalina.loader.WebappClassLoader.findClassInternal(WebappClassLo
ader.java:1475)
> >  at
> >
org.apache.catalina.loader.WebappClassLoader.findClass(WebappClassLoader.jav
a:836)
> >  at
> >
org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.jav
a:1215)
> >  at
> >
org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.jav
a:1098)
> >  at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:313)
> >  at
> >
org.apache.commons.cactus.server.ServletTestRedirector.doPost(ServletTestRed
irector.java:143)
> > Here is what I imagined is happening (tell me if this correct or wrong
> > !) :
> >
> > As MyProxyClass is within the war file, the WebappClassLoader gets
> > called to load it. The WebappClassLoader, in trying to find out the
> > correct class, actually loads some other class in memory, and thus the
> > ServletTestCase, which fails to load because the junit jar is not in
> > the classpath.
> >
> > Is that correct ?
> > Don't you find it strange that the error about the missing class is
> > reported when calling a class that has nothing to do with the problem
> > ? It gets very hard to catch errors ...
> >
> > For example, in MyProxyClass, the code that calls the ServletTestCase
> > method is as follows :
> >
> >         ServletTestCase testInstance = null;
> >         try {
> >             testClass = Class.forName(theClassName);
> >             Constructor constructor = testClass.getConstructor(new
> > Class[] { String.class });
> >             testInstance =
> > (ServletTestCase)constructor.newInstance(new Object[] { theMethod });
> >         } catch (Exception e) {
> >             logger.debug("Error instanciating class [" + theClassName
> > + "]", e);
> >             e.printStackTrace();
> >             throw new ServletException("Error instanciating class [" +
> > theClassName + "]", e);
> >         }
> > And there is never any exception caught here .... because the error
> > happens earlier in the call stack, when the ServletTestRedirector
> > instanciates MyProxyClass ...
> >
> > ... or am I missing something ? :)
> >
> > Thanks
> > -Vincent Massol
> >
>


Re: WebappClassLoader question

Posted by Alex Fernández <af...@tid.es>.
Hi Vincent!

I've run into the same situation a couple of times, when one class uses
a second class, and this second class uses a third one that is not
present.

1st -> 2nd -> 3rd (missing)

One would think that instantiating the 2nd should give an error, but
that loading the 2nd and/or instantiating the 1st should be ok. In fact,
all of the behaviors raise exceptions.

The following paragraph in ClassLoader javadoc might be of help:

"The methods and constructors of objects created by a class loader may
reference other classes. To determine the class(es) referred to, the
Java virtual machine calls the loadClass method of the class loader that
originally created the class."

Or, to find out what the JVM is doing, the spec is here:
http://java.sun.com/docs/books/vmspec/

Hope it helps.

Un saludo,

Alex.

> Vincent Massol wrote:
> 
> Hi,
> 
> Here is the situation :
> 
> * I have a class that makes use of JUnit (by extending the JUnit
> TestCase class). Let's call it ServletTestCase
> * I have a second class that is used to call a method in
> ServletTestCase, let's call it MyProxyClass
> * I have a third class (a servlet) that does _not_ make use of JUnit.
> Let's call it ServletTestRedirector. This class actually instanciate
> MyProxyClass and calls one of its method.
> * I package these classes in a war file and I _don't_ include
> junit.jar in this war file
> 
> When I access the servlet, I get a ClassNotFoundException on a JUnit
> class. So far it is normal ...
> When I debugged it, I have actually found that the error was happening
> when ServletTestRedirector was instancianting MyProxyClass (which does
> _not_ make use of JUnit) and before it was calling its method.
> 
> Here is the stack trace I got :
> 
> java.lang.NoClassDefFoundError: junit/framework/TestCase
>  at java.lang.ClassLoader.defineClass0(Native Method)
>  at java.lang.ClassLoader.defineClass(ClassLoader.java:486)
>  at
> java.security.SecureClassLoader.defineClass(SecureClassLoader.java:111)
>  at
> org.apache.catalina.loader.WebappClassLoader.findClassInternal(WebappClassLoader.java:1475)
>  at
> org.apache.catalina.loader.WebappClassLoader.findClass(WebappClassLoader.java:836)
>  at
> org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1215)
>  at
> org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1098)
>  at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:313)
>  at
> org.apache.commons.cactus.server.ServletTestRedirector.doPost(ServletTestRedirector.java:143)
> Here is what I imagined is happening (tell me if this correct or wrong
> !) :
> 
> As MyProxyClass is within the war file, the WebappClassLoader gets
> called to load it. The WebappClassLoader, in trying to find out the
> correct class, actually loads some other class in memory, and thus the
> ServletTestCase, which fails to load because the junit jar is not in
> the classpath.
> 
> Is that correct ?
> Don't you find it strange that the error about the missing class is
> reported when calling a class that has nothing to do with the problem
> ? It gets very hard to catch errors ...
> 
> For example, in MyProxyClass, the code that calls the ServletTestCase
> method is as follows :
> 
>         ServletTestCase testInstance = null;
>         try {
>             testClass = Class.forName(theClassName);
>             Constructor constructor = testClass.getConstructor(new
> Class[] { String.class });
>             testInstance =
> (ServletTestCase)constructor.newInstance(new Object[] { theMethod });
>         } catch (Exception e) {
>             logger.debug("Error instanciating class [" + theClassName
> + "]", e);
>             e.printStackTrace();
>             throw new ServletException("Error instanciating class [" +
> theClassName + "]", e);
>         }
> And there is never any exception caught here .... because the error
> happens earlier in the call stack, when the ServletTestRedirector
> instanciates MyProxyClass ...
> 
> ... or am I missing something ? :)
> 
> Thanks
> -Vincent Massol
>