At 05:23 10.01.2003 -0700, you wrote:
I'm running into a problem with log4j configuration when running junit tests from the junit.swingui.TestRunner class.
Here is the error message I get to the console:

log4j:ERROR A "org.apache.log4j.ConsoleAppender" object is not assignable to a "org.apache.log4j.Appender" variable.
log4j:ERROR The class "org.apache.log4j.Appender" was loaded by
log4j:ERROR [junit.runner.TestCaseClassLoader@a9255c] whereas object of type
log4j:ERROR "org.apache.log4j.ConsoleAppender" was loaded by [sun.misc.Launcher$AppClassLoader@12f6684].
log4j:ERROR Could not instantiate appender named "A1".
log4j:ERROR A "org.apache.log4j.ConsoleAppender" object is not assignable to a "org.apache.log4j.Appender" variable.
log4j:ERROR The class "org.apache.log4j.Appender" was loaded by
log4j:ERROR [junit.runner.TestCaseClassLoader@a9255c] whereas object of type
log4j:ERROR "org.apache.log4j.ConsoleAppender" was loaded by [sun.misc.Launcher$AppClassLoader@12f6684].
log4j:ERROR Could not instantiate appender named "A1".
log4j:WARN No appenders could be found for logger (TestOmaha).
log4j:WARN Please initialize the log4j system properly.

These errors go away completely if I specify -Dlog4j.ignoreTCL on the command line like this:

java -Dlog4j.ignoreTCL junit.swingui.TestRunner

This, of course, causes log4j to use Class.forName() to load org.apache.log4j.ConsoleAppender.

The junit swing test runner has a default option to reload classes everytime you click the "run" button. This is a good thing so you can avoid restarting the gui. The problem with log4j though, is that this is causing log4j to load the classes "org.apache.log4j.Appender" and "org.apache.log4j.ConsoleAppender" with different classloaders as seen in the error message. The Appender class seems to be loaded as a regular java class (probably via new), but the ConsoleAppender is being loaded by this special Thread Context Classloader. This Appender class is being loaded via this special junit class loader, "junit.runner.TestCaseClassLoader@a9255c". ConsoleAppender needs to be loaded by either the same classloader used to load the Appender class or a child of that classloader. It appears that log4j is using attempting to use a parent of the classloader used to load Appender here.

What is the motivation is for using this Thread Context Classloader by default anyway?

I haven't worked much with this Thread Context Classloader much, but accourding to the docs, getContextClassLoader() will return the the ClassLoader context of the parent Thread if setContextClassLoader() is not used. In fact, you can see this behavior by clicking the "run" button with the junit swing TestRunner. The Appender class is always loaded from a NEW classloader, but the ConsoleAppender class is always attempted to be loaded by the SAME classloader each time - i.e. the ConsoleAppender is being loaded by this one Thread Context Classloader that knows nothing about new classloaders being created by junit.

Does this make sense to anyone?

It certainly does. It very much looks like you have the log4j.jar loaded twice by two distinct class loaders. As the log4j warning messages indicate, most probably you have log4j.jar in the classpath and at the same time log4j.jar is available to the junit.runner.TestCaseClassLoader .

According to the Java Language Spec two classes loaded by different class loaders, even if bit-wise identical, are considered incompatible.


--
Ceki



--
To unsubscribe, e-mail: <mailto:[EMAIL PROTECTED]>
For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>



Reply via email to