I successfully started over again with a fresh installation of Tomcat 4.1.12-LE, uncovering another side effect of the tomcat classloader. I find that the ClassLoader-HOWTO is very vague in explaining the exact boundaries of the classloaders...here's why:
To make it work: moved commons-beanutils, and commons-logging to common/lib from server/lib, these were duplicated before because I didn't understand (more like pay attention) the server can use resources from there. I believe this caused come kind of mismatch in the classloader when testing isAssignableFrom(). Copied the commons-digester to my web-app (my app code uses it and the other commons packages). The problem: I shouldn't need to put the commons-digester.jar in BOTH the server/lib, and my webapp/WEB-INF/lib, when I can place it in the commons/lib. But, doing just that, tomcat fails to start. Though the writeup on the classloaders helps a lot, it doesn't explain why the commons-digester.jar has to be in the server/lib ONLY for catalina to start. I assume this is a bug? If so, please let me know and I'll put it in bugzilla. Kevin Ross Kevin Ross wrote: > Environment: > > commons-logging-1.0.2.jar > log4j-1.2.6.jar > Apache Tomcat/4.1.12-LE-jdk14 > > Situation: > > I have my own realm implementation that is stable in Tomcat 4.0.3-LE, > and I'm moving up to 4.1.12. In addition, I am also moving all of my > codebase to the commons-logging package. After moving to > commons-logging, everything in my codebase works in a simple JVM, i.e. > JUnit tests and Java Applications. When this is inside the tomcat > classloader heirarchy though, NOTHING works. Each of the following > scenarios describe the things I've done, with somewhat different > results, but all leading back to incompatible class loading schemes. > Is there a tomcat bug? The first scenario describes a situation where > a server realm component is finding the log4j classes using the method > from the LogFactoryImpl.isLog4JAvailable() method...it returns 'true' > when the log4j classes ARE NOT in the server/lib, but all the way down > in one of my webapp/lib. I believe this is a tomcat issue. > > ============================================== > This scenario is debug output I placed in the methods that the > LogFactory uses to determine if log4j is available. I copied > loadClass(), isLog4JAvailable(), getContextClassLoader() from the > LogFactory and LogFactoryImpl into my Realm, and here is the output > when I put log4j.jar in my 'operations' webapp > and made sure that the log4j.jar is not available anywhere else in the > tomcat heirarchy: > > ->loadClass(org.apache.log4j.Category) > ->trying the context classloader from Thread.getContextClassLoader() > ->got the context classloader from Thread.getContextClassLoader() > ->contextClassLoader worked. returning class org.apache.log4j.Category > ->trying Class.forName(org.apache.log4j.Category) > ->Class.forName RETURNING an exception. > ****isLog4JAvailable(): true > > This should be false! Why should a server component be able to access > my webapp's jars??? Is the commons-logging package unsafely assuming > how a classloader will work, or is the Tomcat-Catalina configuration > of tiered classloaders incorrectly allowing access to that class via > the thread context classLoader? Nonetheless, even if the tomcat class > loader isn't doing it's job exactly, the commons-logging package tests > for that class via the contextClassLoader, but it then cannot > instantiate the class. That would mean that it's not using the > context class loader to instantiate...wouldn't it? If so, that is > inconsistent as well. > > > ============================================== > When I put the log4j-1.2.6.jar in with the latest > commons-logging-1.0.2.jar in catalina\server\lib: > > Using CATALINA_BASE: .. > Using CATALINA_HOME: .. > Using CATALINA_TMPDIR: ..\temp > Using JAVA_HOME: C:\Development\lib\jdk\j2sdk1.4.0 > Exception during startup processing > java.lang.reflect.InvocationTargetException > at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) > at > sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.jav > a:39) > at > sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessor > Impl.java:25) > at java.lang.reflect.Method.invoke(Method.java:324) > at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:203) > Caused by: java.lang.NoClassDefFoundError: org/apache/log4j/Layout > at > org.apache.commons.logging.impl.Log4jFactory.getInstance(Log4jFactory.ja > va:153) > at > org.apache.commons.logging.impl.LogFactoryImpl.getInstance(LogFactoryImp > l.java:285) > at > org.apache.commons.logging.LogFactory.getLog(LogFactory.java:409) > at org.apache.commons.digester.Digester.<init>(Digester.java:281) > at > org.apache.catalina.startup.Catalina.createStartDigester(Catalina.java:2 > 80) > at org.apache.catalina.startup.Catalina.start(Catalina.java:441) > at org.apache.catalina.startup.Catalina.execute(Catalina.java:400) > at org.apache.catalina.startup.Catalina.process(Catalina.java:180) > ... 5 more > ============================================== > > > ============================================== > When I have NO log4j-1.2.6.jar (anywhere in the catalina heirarchy), The > tomcat system will startup, but when > I execute the Realm (via a request), i get this: > > java.lang.ExceptionInInitializerError > at > com.iverticalleap.security.module.tomcat.Realm.authenticate(Realm.java:1 > 51) > at > org.apache.catalina.authenticator.FormAuthenticator.authenticate(FormAut > henticator.java:263) > at > org.apache.catalina.authenticator.AuthenticatorBase.invoke(Authenticator > Base.java:458) > at > org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.i > nvokeNext(StandardPipeline.java:641) > at > org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:4 > 80) > at > org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:995) > at > org.apache.catalina.core.StandardContext.invoke(StandardContext.java:239 > 6) > at > org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java > :180) > at > org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.i > nvokeNext(StandardPipeline.java:643) > at > org.apache.catalina.valves.ErrorDispatcherValve.invoke(ErrorDispatcherVa > lve.java:170) > at > org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.i > nvokeNext(StandardPipeline.java:641) > at > org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java > :172) > at > org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.i > nvokeNext(StandardPipeline.java:641) > at > org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:4 > 80) > at > org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:995) > at > org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve. > java:174) > at > org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.i > nvokeNext(StandardPipeline.java:643) > at > org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:4 > 80) > at > org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:995) > at > org.apache.coyote.tomcat4.CoyoteAdapter.service(CoyoteAdapter.java:223) > at > org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:40 > 5) > at > org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.processC > onnection(Http11Protocol.java:380) > at > org.apache.tomcat.util.net.TcpWorkerThread.runIt(PoolTcpEndpoint.java:50 > 8) > at > org.apache.tomcat.util.threads.ThreadPool$ControlRunnable.run(ThreadPool > .java:533) > at java.lang.Thread.run(Thread.java:536) > Caused by: org.apache.commons.logging.LogConfigurationException: > org.apache.commons.logging.LogConfigurationException: o > rg.apache.commons.logging.LogConfigurationException: Class > org.apache.commons.logging.impl.Jdk14Logger does not implemen > t Log > at > org.apache.commons.logging.impl.LogFactoryImpl.newInstance(LogFactoryImp > l.java:555) > at > org.apache.commons.logging.impl.LogFactoryImpl.getInstance(LogFactoryImp > l.java:289) > at > org.apache.commons.logging.impl.LogFactoryImpl.getInstance(LogFactoryImp > l.java:259) > at > org.apache.commons.logging.LogFactory.getLog(LogFactory.java:390) > at > com.iverticalleap.util.logging.AbstractLoggable.getLog(AbstractLoggable. > java:28) > at > com.iverticalleap.util.logging.AbstractLoggable.logDebug(AbstractLoggabl > e.java:189) > at > com.iverticalleap.util.pool.AbstractBroker.<init>(AbstractBroker.java:39 > ) > at > com.iverticalleap.util.pool.AbstractBroker.<init>(AbstractBroker.java:44 > ) > at > com.iverticalleap.service.ServiceBroker.<init>(ServiceBroker.java:27) > at > com.iverticalleap.service.ServiceBroker.<clinit>(ServiceBroker.java:23) > ... 25 more > Caused by: org.apache.commons.logging.LogConfigurationException: > org.apache.commons.logging.LogConfigurationException: C > lass org.apache.commons.logging.impl.Jdk14Logger does not implement Log > at > org.apache.commons.logging.impl.LogFactoryImpl.getLogConstructor(LogFact > oryImpl.java:420) > at > org.apache.commons.logging.impl.LogFactoryImpl.newInstance(LogFactoryImp > l.java:548) > ... 34 more > Caused by: org.apache.commons.logging.LogConfigurationException: Class > org.apache.commons.logging.impl.Jdk14Logger does > not implement Log > at > org.apache.commons.logging.impl.LogFactoryImpl.getLogConstructor(LogFact > oryImpl.java:416) > ... 35 more > > > With log4j in the server/lib dir, I get a similar message stating > "Log4JCategoryLog does not implement Log" > ============================================== > > > > In conclusion, I've spent a great deal of time looking into this, and I > believe it may be a manifestation of a > combination of problems. I may be able to help figure this out if the > following questions can be answered: > > >From the commons-logging developers: ---- > Why is the class loading done this way? Is it necessary? Why are both > the JDK14 and log4j loggers failing to test as > assignableFrom(Log) in the catalina server-side (not webapp-side) > runtime environment? > > >From the catalina developers: ---- > Why can a server component (Realm) find a class from a webapp's lib > directory via the > Thread.currentThread().getContextClassLoader().loadClass() without a > ClassNotFoundException? I'm not even sure this is possible, but if > catalina allows a class to be > loaded or even instantiated via a particular classloader (thread > context), but the typical classloader is used to determine > MyClassThatImplementsLogFromThreadContextClassLoader.assignableFrom(Log. > class), this would explain why nothing passes the implementation test in > the LogFactory.class. > > > > Last conclusion, am I crazy? ;) Any thoughts are welcome and we come to > a solution, I'll be glad to submit a patch. > > > -Kevin Ross > > > > > -- > To unsubscribe, e-mail: > <mailto:[EMAIL PROTECTED]> > For additional commands, e-mail: > <mailto:[EMAIL PROTECTED]> > -- To unsubscribe, e-mail: <mailto:[EMAIL PROTECTED]> For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>