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.  Why all the complicated classloading in the commons-logging?
Is there a tomcat bug?  The last 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 MAY be a tomcat issue, but, why not just use
Class.forName()? (it works properly)
 
==============================================
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"
==============================================
 
 
==============================================
This last 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 shouldn't it!?  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.
 
 
 
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


Reply via email to