I've been thinking about "JCL requirements" again, in the light of my new understanding of the root cause of the "Log4JLogger does not implement Log" issue. See http://marc.theaimsgroup.com/?l=jakarta-commons-dev&m=111547472006496&w=2 Please read that email before the rest of this document. Here's the core of the requirements as I see it: (a) What do we want to happen when webapp code binds to a logging library in the parent? (b) What do we want to happen when: * webapp code binds to a logging library in the child, and * the parent path contains class X which uses logging, and the webapp calls X (c) What do we want to happen in all other cases? (a) What do we want to happen when webapp code binds to a logging library in the parent? This can happen when parent-first is selected, or when child-first is selected but there is no logging library in the child path. NB: Actually, (1) and (2) are really one design from JCL's point of view, with different assumptions about the behaviour of the underlying logging library. (1) We can assume that all logging configuration is done by the container administrator via a file in the parent classpath, and that no two webapps use the same categories (so that the config file can control things). If the logging lib contains any kind of global state, then webapps might end up interacting with each other. There might be minor problems when a webapp is reloaded. State associated with specific log categories (if any) will be retained from the previous webapp (eg log4j NDCs?) unless something "flushes" them. Trivial to implement; JCL just passes calls straight through to the discovered logging lib. Implementable using either dynamic or static binding I think. All in all, a rather ugly solution. I hesitate to even call this a solution. (2) We can trust the logging library to "do the right thing" by looking at the context classloader, loading its config resources via that classloader and setting up logging "hierarchies" that are distinct for different context classloader. The JCL LogFactory can then do away with its map keyed by classloader, and just call (via the appropriate adapter) directly into the logging library. Trivial to implement; JCL just passes calls straight through to the discovered logging lib. Implementable using either dynamic or static binding I think. Note that where the logging lib doesn't actually implement all of this internally, but does provide an API that *allows* these operations to be done, we can implement this functionality within the JCL adapter for that logging library. Here's an example for Log4J: class Log4JFactory { LoggerRepository repo; public Log4JFactory() { ClassLoader contextLoader = getContextClassLoader(); repo = new Hierarchy(new RootLogger(Level.DEBUG)); URL url = contextLoader.getResource(configuratonFile); OptionConverter.selectAndConfigure(url, null, repo); } public Log4JLogger getLogger(String name) { return repo.getLogger(name); } public void release() { // optional but nice. repo.shutdown(); } } Because Log4JLogger instances are distinct per context-classloader, we therefore automatically have logging separation per context classloader. Note that it was necessary to reintroduce the distinction between the "factory" and the "logger", ie there would be a 1:1 between LogFactoryImpl and a logging-library-specific factory class. There's no guarantee, however, that every logging library will provide the equivalent of Log4j's "hierarchies". I suspect that logging libraries tend to have the habit of using singletons in various places and providing no bypasses to allow multiple separate instances. Even log4j doesn't really appear to have been designed with the idea of outside code creating and manipulating Hierarchy objects directly. Note also that it seems to me we must assume that *something* ensures the webapp-specific logging info is cleaned up. It's a cleaner task to do so than (1) above where the webapp-specific logging config was not partitioned into a distinct "Hierarchy" object per webapp, but it still needs doing. I've assumed that LogFactory.release is modified to invoke a release method on the corresponding lib-specific factory class, and that all user code will call LogFactory.release on unload. (b) What do we want to happen when: * webapp code binds to a logging library in the child * the parent path contains class X which uses logging, and the webapp calls X This implies: * child-first is selected, or the parent contains no logging lib of equal or higher priority. * the parent path must contain LogFactory and Log, as class X will not load without this. (1) We can ensure that LogFactory never tries to use a logging lib that is not accessable via its own classloader. The implication is that logging from class X will use a totally different logging lib from the one used by code in the child. Configuration of logging from class X will need to be done according to the rules in (a) above, and will interact poorly with logging done by the child. ** THIS IS THE DEFAULT AND ONLY OPTION PROVIDED BY STATIC BINDING FOR SCENARIO (b) ** (2) We can simply not support this option. People must remove all logging libs from the child path, moving the sitation out of scenario (b) into scenario (a). Failing to do so results in an error message from the logging system, and the application failing to start. This is the advice JCL users have (indirectly) been given in the past. When they strike the dreaded "Log4JLogger does not implement Log" they have been told to ensure only one copy of the logging lib exists on the classpath. But removing it from the parent causes other classes in the classpath to fail to load with missing dependencies, so they are forced to remove it from the child path instead. Where this discussion differs, though, is that I'm suggesting we pay much more attention to scenario (a) to help them once they have taken this option. (3) We can simply not support this option. People must put a copy of X into the webapp if they use child-first, moving the situation out of scenario (b) into scenario (c). Failing to do so results in an error message from the logging system, and the application failing to start. This is probably not a viable option. There are containers that use JCL internally, and pass instances of those objects to webapps. We simply can't stop supporting all such containers (WebSphere, Tomcat, ...) (4) We can try to delegate to a logging implementation accessable via the child classloader. If there is such a thing available, then we automatically solve the issues of separation of logging, because the logging lib is loaded via a classloader that gives it its own singleton+static variables, and (presumably) a config file in the webapp. Of course this *only* works when: * dynamic binding is used (**OPTION NOT AVAILABLE FOR STATIC BINDING**) * the logging adapter is also in the child path, and * the discovered adapter implements Log via the parent classloader (ie Log is not defined in the child classloader). And this really requires that (i) parent-first loading is used, or (ii) the Log class is not present at all in the child classpath. The above criteria aren't too hard to satisfy, provided JCL provides the appropriate jar files, ie * commons-logging-api.jar (LogFactory, LogFactoryImpl, Log but no adapters) * commons-logging-adapters.jar (Log4JLogger, JDK14Logger, etc. but no Log class) and these jar files are properly deployed (api.jar + anything else in parent, *only* adapters.jar in child). However this situation leads to a memory leak on unload that *cannot* be fixed via weak references. Users *must* call LogFactory.release on webapp unload -- well, until we get proper per-webapp singleton support anyway. (c) All other scenarios -- work fine with existing JCL approach (and with static binding).