Here's the problem I mentioned earlier in more detail:
LogFactory always keeps a map of LogFactoryImpl instances keyed by context
classloader so that when deployed in a shared classloader each webapp gets an
independent logging setup.
When webapp B makes an inter-app call to C. Code in C calls LogFactory.getLog.
tries to look up a LogFactoryImpl by TCCL in its map. There isn't one, so it
goes into its discovery process. This is what is currently causing problems, but
assuming we fix it somehow [as per your suggestion] a LogFactoryImpl object will
be returned. LogFactory then caches it using the current TCCL.
Later someone undeploys webapp B. Normally, the reference count for the TCCL for
B will drop to zero at that point, and all the classes associated with B will be
garbage-collected (including all the static member variables, etc). However
because the map in the LogFactory class loaded by C has a key that references B
this won't happen.
Log4j's default setup works because it effectively has two modes: "server" and
"app". The default is "app", where it doesn't care about TCCL and always uses
the default hierarchy. However it can be configured to use a
ContextHierarchySelector or somesuch name; when deployed via a shared classpath
the user needs to enable this selector in order to get independent logging for
each app. Note that if you enabled log4j's ContextHierarchySelector in
classloader C you would get the same memory leakage problem as JCL suffers.
JCL is effectively always in "server" mode, with the assumption that if it was
actually deployed in a webapp's classpath then it is a server with only one
client TCCL - the app. However the "inter-app" stuff screws this up because JCL
deployed via a webapp ends up seeing multiple client TCCLs.
So I guess we could provide a config option for LogFactory which tells it to act
in server or client mode, and if in client then it avoids using the TCCL.
This will be a nuisance for every system that deploys logging libs via a shared
classloader (and that is a lot of them). And I don't know how we would provide
any kind of "backward compatibility". Maybe there is some way to do reasonable
auto-detection of client vs server mode? Like walk the classloader tree looking
for a copy of JCL at a higher level and assume client mode if one is found? Not
sure that's reliable enough...
Maybe the answer is two different JCL jars: a client jar and a server jar? That
sounds nicer. webapps bundle the client jar, and that only ever has one
LogFactoryImpl. In this case we also know that adapters should be located via
LogFactoryImpl's classloader so that fixes your reported problem too. The server
jar has an implementation that uses a Map keyed by TCCL and locates adapters via
TCCL. This is a significant change, though, with API breakage.