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]>

Reply via email to