On Sun, 2005-05-08 at 01:06 +1200, Simon Kitching wrote: > Hi All, > > I've been working for quite a while now to try to figure out why users > of JCL have been getting the "Log4JLogger does not implement Log" > message. > > I think I've finally really understood the cause. If this is really > obvious to everyone, please let me down gently :-)
one thing which i have learnt from working with JCL over the years is that nothing is ever obvious :) more than anything we need clear explanations of what happens and why. > The problem is not the *presence* of JCL in the parent or child > classloader paths. The problem is the presence of code that *calls* > LogFactory.getLog in classes in the parent classpath. When webapp code > calls into such a class with the context classloader set, that triggers > the problem. +1 now that the demonstration code has been fixed, it is clear that (for conventional classloader setups anyway) there's only one substantive case that is missing. that's a lot better than things appeared a while ago. > The problem only ever occurs when child-first classloading is selected > (ironic, considering my recent arguments in favour of this feature). > > 1. > > A class in the webapp has a symbolic reference to a method on a class > that is present only in the parent classpath. That class is therefore > located by the parent classloader. Java's resolution mechanism then > requires that the classes referenced by it (LogFactory, LogFactoryImpl, > Log) are loaded via the same classloader. +1 > 2. > > The webapp code then actually calls the above method. Context > classloader is set to the webapp classloader. Control flows through the > called method to LogFactory, to LogFactoryImpl. LogFactoryImpl then > dynamically loads Log4JLogger from the context classpath, ending up > with an implementation from the child classloader. > > However Log4JLogger has a symbolic reference to the Log class (because > it implements it). And the java rules specify that such references are > resolved via the classloader that loaded the referencing classes. So the > Log class that Log4JLogger references is loaded via the child > classloader. +1 > 3. > > The LogFactoryImpl class then tries to cast Log4JLogger to its version > of Log and fails, because the Log class was loaded via different > classloaders. +1 there is actually no reason why the cast actually needs to be performed to diagnose this condition: all that's required is to check the classloader used to load the implementation class. > === > A minor variant is where the object in the parent classloader that calls > LogFactory.getLog is created by the container and passed as a parameter > to the webapp (eg an implementation of ServletContext). The basic > behaviour is the same, though. > === > > Ok, so what is the solution? i would like to split this question into two. i believe (as indicated in the analysis document) that the correct behaviour in these cases is for JCL to recognise that log4j is not viable (for this configuration) and default to java.util logging. this is a little unsatisfactory but i don't see a reasonable technical solution for these configuration. the other question (which i think is what simon addresses below) is how can JCL provide a solution for a user who needs a similar configuration but who is willing to choose to deploy JCL slightly differently. > (1) > Well, ensuring that the logging adapters are deployed via the parent > classloader *only* is one. That fixes the problem, as Log4JLogger et. > al. always bind to the same Log interface that LogFactory binds to. > > The downside is: > * It means that the logging library must also be deployed via the > parent classloader, even when there *are* no other classes in the > parent classloader that trigger this problem (ie ones that use JCL > and that the webapp is going to call into). > > Having the logging library loaded via the parent classloader means > that dumb logging libraries may not be able to find their config > files. > > Some logging libs may be smart enough to look for their > resource files via the context classloader. And in some cases the > logging *adapter* class might be able to tell the logging lib > where the config file is. Assuming we can rely on (or trick) the > logging lib to correctly handle per-context-classloader location > of their config files, all should be well. -1 seems like giving away too much for a corner case > (2) > I think we could simply change the distribution bundles. The root > problem appears to be to me that the adapters (Log4JLogger et al) are > bundled with a Log implementation. If we produced a jar that included > Log4JLogger et al. but *without* the Log class, the problem should be > solved. The rule would then be: > * if the parent loader has commons-logging.jar or > commons-logging-api.jar, then deploy > commons-logging-adapters.jar in the child, together with the > actual logging library. > * otherwise, deploy commons-logging.jar in the child > (or commons-logging-api.jar + commons-logging-adapters.jar) this approach to these kinds of configuration is the one i was thinking of earlier when i suggested that we might need an implementations only distribution. a reasonable user could then adjust the deployment so that the implementations were in the child and the api in the parent. > (3) > Maybe we should just do away with LogFactoryImpl's attemp to load a log > adapter via the context classloader. It does enable the setup of having > commons-logging-api.jar in the parent and commons-logging.jar in the > child, but it fails badly if any other class in the parent classloader > uses JCL and is called by the webapp. Is this benefit worth the pain? no but there is a variation (that i have discussed with richard previously). the particular problem situation can be diagnosed (a log class loaded from the context should have an incompatible classloader). when the implementation is not viable, rather than throw an exception JCL could try to load the class with the same name from it's classloader. i think that brian's patch does something which though implemented differently has the same net result. however, i don't think that either of these approaches would actually work in this case: log4j is not visible to the parent classloader. i do think that something along these lines will be needed for the exotic classloader configurations (which haven't been analysed yet). > BTW, Brian proposed a splitting-up of the JCL jars a few months ago, as > experiments showed it resolved some issues. I can't find the emails just > now. Is it related to the solution (2) proposed above? i think so. maybe brian could repost his link. - robert --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]