I'm a newbie to Java and just starting to figure out the concept of class loaders. Right now I am having some issues with log4j regarding its use of the thread context classloader.
I'm getting the following errors: A "org.apache.log4j.ConsoleAppender" object is not assignable to a "org.apache.log4j.Appender" variable. The class "org.apache.log4j.Appender" was loaded by [java.net.URLClassLoader@105691e] whereas object of type "org.apache.log4j.ConsoleAppender" was loaded by [sun.misc.Launcher$AppClassLoader@16930e2]. Could not instantiate appender named "CONSOLE".
My application works roughly this way: On init URLClassLoader #1 is constructed and loads some classes, these classes use log4j. Later on URLClassLoader #2 is constructed (which has URLClassLoader #1 as it's parent) and loads some more classes, these classes also use log4j. When URLClassLoader #2 is used to load these classes the above error message appears (there are a couple more with the same issue).
The current workaround I did was to set the current thread context classloader to URLClassLoader #2 before loading the problematic classes, and resetting it to the old one afterwards:
ClassLoader urlClassLoader; // this is URLClassLoader #2
Thread thread = Thread.currentThread();
ClassLoader loader = thread.getContextClassLoader();
thread.setContextClassLoader(urlClassLoader);
try {
urlClassLoader.loadClass(...)
} finally {
thread.setContextClassLoader(loader);
}
While this works, I am not sure if it's the right approach.
Any insight on this matter will be appreciated. Also, why is log4j forcing me to mess with the thread context classloader? Why not let me pass in a class loader (and use a default one when I don't) instead of using the thread's one?
The ThreadContext allows applications to store information either in a Map or a Stack. The MDC is managed on a per thread basis. To enable automatic inheritance of copies of the MDC to newly created threads, enable the "isThreadContextMapInheritable" Log4j system property. See Also: Thread Context Manual.
That is, the context class loader can load the classes that the application can load. This loader is used by the Java runtime such as the RMI (Java Remote Method Invocation) to load classes and resources on behalf of the user application.
3. MDC in Log4j. MDC in Log4j allows us to fill a map-like structure with pieces of information that are accessible to the appender when the log message is actually written. The MDC structure is internally attached to the executing thread in the same way a ThreadLocal variable would be.
A ThreadContext is a map of string headers and a transient map of keyed objects that are associated with a thread. It allows to store and retrieve header information across method calls, network calls as well as threads spawned from a thread that has a ThreadContext associated with.
You appear to have stumbled upon the major problem with log4j (and the Apache Commons Logging library), namely that they have a ridiculously hard time discovering and interacting with the right classloaders as they're being used. There's a very dense explanation, complete with examples, here; the take-home message is that one of the primary driving forces for the new logging framework SLF4J was to eliminate these issues entirely. You might want to swap it in and see if your life is made any easier.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With