[Summary] Re: WebappClassLoader question
Thanks to all who helped on this. Andrew was right, and Craig too and I was wrong ! Here is what happened. My code was written like this : 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) { As Andrew pointed out, this would never have caught the NoClassDefFoundError error because it is not a subclass of Exception. Modifying it to Throwable did not help. However the error was that ServletTestCase was defined outside the try catch block and thus the error was thrown there and would be caught in the first class because I was catching Throwable there ... So it is a stupid error. However I learnt how the Tomcat class loader worked which I didn't know. I also learned that the error raised when a class is not found by the class loader was NoClassDefFoundError and not (as I thought initially) ClassNotFoundException ! So I'll to rewrite my code as follows : Object dummy; <-- cannot use ServletTestCase here as it might raise a NoClassDefFoundError! try { testClass = Class.forName(theClassName); Constructor constructor = testClass.getConstructor(new Class[] { String.class }); dummy = (ServletTestCase)constructor.newInstance(new Object[] { theMethod }); <--- need the cast to catch NoClassDefFoundError } catch (Exception e) { ... } ServletTestCase testInstance = (ServletTestCase)dummy; ... which I don't find that easy to write at first ... (I'll need to put some comments to explain the trick) ! You might say, why catch this error and not let it be thrown by the container itself ... However the problem with this is that the error that gets displayed does not give any clue that the initial problem was a NoClassDefFoundError, it just prints the following in the console : [java] StandardWrapperValve[ServletRedirector]: Servlet.service() for servlet ServletRedirector threw exception [java] javax.servlet.ServletException: Servlet execution threw an exception [java] javax.servlet.ServletException: Servlet execution threw an exception [java] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(Application FilterChain.java:269) which is absolutely not explicit ! Wouldn't it be possible to improve Tomcat so that it chains the exception and also prints the original excpetion in the console ? Thanks again. -Vincent - Original Message - From: "Andrew Inggs" <[EMAIL PROTECTED]> To: <[EMAIL PROTECTED]> Sent: Friday, July 27, 2001 10:01 AM Subject: RE: WebappClassLoader question > Hi Vincent > > You seem to be missing at least two things here: > > 1. In your original post you said: > > > 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. > > This does not look right. You say MyProxyClass does not make > use of JUnit, but it *does*. This is the line that causes it to: > > > testInstance = (ServletTestCase)constructor.newInstance(new Object[] { > theMethod }); > > You see here you are explicitly referencing ServletTestCase which > extends junit.framework.TestCase, this introduces the dependency. > > 2. You seem to be assuming that NoClassDefFoundError extends > Exception, but it doesn't. If you want to catch a > NoClassDefFoundError you'll have to catch it directly, or one of > its superclasses (java.lang.LinkageError, java.lang.Error, or > java.lang.Throwable). This is why "I would have thought here" > never gets printed out: > > > } catch (Exception e) { > > log("I would have thought here"); > > } > > As an aside, I'm not sure what you are trying to achieve here? > You don't have junit.jar in your classpath, and then you're say > you get this strange error. The details aside, isn't that what > you'd expect :-)? Even if you hadn't referenced ServletTestCase > directly, you still wouldn't have been able to instantiate it > using reflection without junit.jar in the classpath. What are > you really trying to achieve? > > -- Andrew >
Re: WebappClassLoader question
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" <[EMAIL PROTECTED]> > To: <[EMAIL PROTECTED]> > Cc: "Vincent Massol" <[EMAIL PROTECTED]> > 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
RE: WebappClassLoader question
> > > > The problem is, Cactus (Jakarta's J2EE testing package) raises an > > exception if junit.jar is not found; it should catch the exception and > > kindly explain the situation to the user. > > > > Right now, the exception is raised in a strange place, and > > thus the user > > is faced with an abstruse error. That's what Vincent is > > trying to solve. > > > >A, Many Thanks.. i was really very lost here :) It was my problem to start with ;) Still, glad I had it as this has been an informative thread... > > [Si los dos somos españoles, qué hacemos hablando en inglés? :] > >Para que estos pobreticos se enteren de algo, mientras aprenden Español, >que todo llegara :) Anda, que sois vosotros dos los guiris aquí! Salu2 Jim. -- * Jim Cheesman * Trabajo: [EMAIL PROTECTED] - (34)(91) 724 9200 x 2360 The shortest distance between two points is how far apart they are.
RE: WebappClassLoader question
> -Mensaje original- > De: Alex Fernández [mailto:[EMAIL PROTECTED]] > Enviado el: viernes 27 de julio de 2001 11:17 > Para: [EMAIL PROTECTED] > Asunto: Re: WebappClassLoader question > > > The problem is, Cactus (Jakarta's J2EE testing package) raises an > exception if junit.jar is not found; it should catch the exception and > kindly explain the situation to the user. > > Right now, the exception is raised in a strange place, and > thus the user > is faced with an abstruse error. That's what Vincent is > trying to solve. > A, Many Thanks.. i was really very lost here :) > [Si los dos somos españoles, qué hacemos hablando en inglés? :] Para que estos pobreticos se enteren de algo, mientras aprenden Español, que todo llegara :) Saludos , Ignacio J. Ortega
Re: WebappClassLoader question
- Original Message - From: "Andrew Inggs" <[EMAIL PROTECTED]> To: <[EMAIL PROTECTED]> Sent: Friday, July 27, 2001 10:01 AM Subject: RE: WebappClassLoader question > Hi Vincent > > You seem to be missing at least two things here: > > 1. In your original post you said: > > > 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. > > This does not look right. You say MyProxyClass does not make > use of JUnit, but it *does*. This is the line that causes it to: > > > testInstance = (ServletTestCase)constructor.newInstance(new Object[] { > theMethod }); > > You see here you are explicitly referencing ServletTestCase which > extends junit.framework.TestCase, this introduces the dependency. > That's true. I may have used the wrong word. What I meant is that the MyProxyClass did not have a JUnit import statement, meaning it did not make a _direct_ use of JUnit. But as you point out, ServletTestCase does make a direct use of JUnit. > 2. You seem to be assuming that NoClassDefFoundError extends > Exception, but it doesn't. If you want to catch a > NoClassDefFoundError you'll have to catch it directly, or one of > its superclasses (java.lang.LinkageError, java.lang.Error, or > java.lang.Throwable). This is why "I would have thought here" > never gets printed out: > > > } catch (Exception e) { > > log("I would have thought here"); > > } > That's also true but that's the question I am asking ... ! Why do I get a NoClassDefFoundError whereas I was expecting a ClassNotFoundException ! Please see Craig's answer and my other posts for a better description of what error I am getting and what I am trying to achieve. > As an aside, I'm not sure what you are trying to achieve here? > You don't have junit.jar in your classpath, and then you're say > you get this strange error. The details aside, isn't that what > you'd expect :-)? Even if you hadn't referenced ServletTestCase > directly, you still wouldn't have been able to instantiate it > using reflection without junit.jar in the classpath. What are > you really trying to achieve? > As Alex kindly answered on the list, I would like to catch this error nicely (when junit.jar is not in the classpath) so that I can display a good and relevant message for the end user, telling him to put the class on the class path ... When I debugged the code to know why I wasn't catching the exception, I noticed that the error happened in a strange place, i.e. the first class in the chain of dependencies and that's what I find strange. I have had no previous experience of this kind of class loading mechanism and I find it very very hard to debug (It means you have to put try catch blocks everywhere in your first class being called in your webapp if you want to catch for possible error resulting from classes not in the classapth !). > -- Andrew Thanks Andrew for your help! -Vincent
Re: WebappClassLoader question
Hola Nacho, "Ignacio J. Ortega" wrote: > I'm lost here, where you put the Junit.jar? , because it will be on some > place? no? to allow you load classes from , no?, i think you need the > entire inheritance chain for resolving dependencies, so you need > Junit.jar in some place in classpath AFAIK.. this is the simple dumb > explanation i know, but is what i'm seeing on your example.. You're absolutely right here, Vincent's exception is raised because he doesn't have junit.jar in his classpath. The problem is, Cactus (Jakarta's J2EE testing package) raises an exception if junit.jar is not found; it should catch the exception and kindly explain the situation to the user. Right now, the exception is raised in a strange place, and thus the user is faced with an abstruse error. That's what Vincent is trying to solve. [Si los dos somos españoles, qué hacemos hablando en inglés? :] Un saludo, Alex. > Can you make a simple main example trying to do what you want to do, > without involving a servlet container.. i dont know how you can load ( > using reflection for it doesnt matter ) a class without his entire > inheritance chain present ... > > Only a wild thought, i'm not an expert on anything so... take it me too > seriously .. :) > > Saludos , > Ignacio J. Ortega
RE: WebappClassLoader question
Hi Vincent You seem to be missing at least two things here: 1. In your original post you said: > 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. This does not look right. You say MyProxyClass does not make use of JUnit, but it *does*. This is the line that causes it to: > testInstance = (ServletTestCase)constructor.newInstance(new Object[] { theMethod }); You see here you are explicitly referencing ServletTestCase which extends junit.framework.TestCase, this introduces the dependency. 2. You seem to be assuming that NoClassDefFoundError extends Exception, but it doesn't. If you want to catch a NoClassDefFoundError you'll have to catch it directly, or one of its superclasses (java.lang.LinkageError, java.lang.Error, or java.lang.Throwable). This is why "I would have thought here" never gets printed out: > } catch (Exception e) { > log("I would have thought here"); > } As an aside, I'm not sure what you are trying to achieve here? You don't have junit.jar in your classpath, and then you're say you get this strange error. The details aside, isn't that what you'd expect :-)? Even if you hadn't referenced ServletTestCase directly, you still wouldn't have been able to instantiate it using reflection without junit.jar in the classpath. What are you really trying to achieve? -- Andrew
Re: WebappClassLoader question
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" <[EMAIL PROTECTED]> > To: <[EMAIL PROTECTED]> > Cc: "Vincent Massol" <[EMAIL PROTECTED]> > 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"); &g
Re: WebappClassLoader question
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" <[EMAIL PROTECTED]> To: <[EMAIL PROTECTED]> Cc: "Vincent Massol" <[EMAIL PROTECTED]> 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: &quo
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" <[EMAIL PROTECTED]> > > To: <[EMAIL PROTECTED]>; "Vincent Massol" <[EMAIL PROTECTED]> > > 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 No
Re: WebappClassLoader question
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" <[EMAIL PROTECTED]> > To: <[EMAIL PROTECTED]>; "Vincent Massol" <[EMAIL PROTECTED]> > 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. &
RE: WebappClassLoader question
Hola Vincent: > -Mensaje original- > De: Vincent Massol [mailto:[EMAIL PROTECTED]] > Enviado el: jueves 26 de julio de 2001 22:20 > Para: Craig R. McClanahan; [EMAIL PROTECTED] > Asunto: Re: WebappClassLoader question > > 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. > I'm lost here, where you put the Junit.jar? , because it will be on some place? no? to allow you load classes from , no?, i think you need the entire inheritance chain for resolving dependencies, so you need Junit.jar in some place in classpath AFAIK.. this is the simple dumb explanation i know, but is what i'm seeing on your example.. Can you make a simple main example trying to do what you want to do, without involving a servlet container.. i dont know how you can load ( using reflection for it doesnt matter ) a class without his entire inheritance chain present ... Only a wild thought, i'm not an expert on anything so... take it me too seriously .. :) Saludos , Ignacio J. Ortega > 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" <[EMAIL PROTECTED]> > To: <[EMAIL PROTECTED]>; "Vincent Massol" > <[EMAIL PROTECTED]> > 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 >
Re: WebappClassLoader question
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" <[EMAIL PROTECTED]> To: <[EMAIL PROTECTED]>; "Vincent Massol" <[EMAIL PROTECTED]> 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" <[EMAIL PROTECTED]> > To: <[EMAIL PROTECTED]> > 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
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" <[EMAIL PROTECTED]> > To: <[EMAIL PROTECTED]> > 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
Re: WebappClassLoader question
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" <[EMAIL PROTECTED]> To: <[EMAIL PROTECTED]> 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
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(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 >