Re: JCL problems
Robert, I am a bit surprised by your question. As the current release manager for JCL, I suppose that you are aware that LogFactoryImpl only uses the thread context class loader (TCCL) to search for classes. You were aware of that, weren't you? In theory, this is supposed to let a copy of commons-logging.jar placed in a parent class loader to discover another logging API, say log4j, whose classes are visible to a child class loader with the child loader also set to be the TCCL. However, this dynamic discovery does not work. The copy of log4j.jar must be placed next to commons-logging.jar. Moreover, the copy of commons-logging.jar must be placed very high in the class loader tree, say in the system class loader or the application server's class loader, but that is not all. There must not be other copies of commons-logging.jar in war files of web-apps. To cut a long story short, do to anything meaningful reliably, JCL is a lot more complicated to set and even when set up correctly brings no benefits when compared to static API binding as found in UGLI. I urge you run though the examples in [1] which should demonstrate to you how badly and irreparably JCL is broken. The source code is all there. Even if you don't trust my opinion, at least trust your java compiler and your JVM. [1] http://www.qos.ch/logging/classloader.jsp At 08:50 PM 2/13/2005, robert burrell donkin wrote: (i'm not sure whether i'm missing your point.) it seems to me that it matters not whether the discovery is dynamic or static: the log4j library needs to be available in the classloader. static discovery would break in a similar fashion if a required library is not present. from a practical perspective, i don't really see having to put a number of libraries together so that they are loaded by the same classloader is really a restriction. what matters is that there are ways to achieve the required results and definite instructions are available which are easy for deployers to understand. - robert -- Ceki Gülcü The complete log4j manual: http://www.qos.ch/log4j/ - To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
Re: JCL problems
On Mon, 2005-02-14 at 12:14, Ceki Gülcü wrote: Robert, I am a bit surprised by your question. i needed to make sure that i hadn't misunderstood what you were trying to say... As the current release manager for JCL, I suppose that you are aware that LogFactoryImpl only uses the thread context class loader (TCCL) to search for classes. You were aware of that, weren't you? of course In theory, this is supposed to let a copy of commons-logging.jar placed in a parent class loader to discover another logging API, say log4j, whose classes are visible to a child class loader with the child loader also set to be the TCCL. that's untrue and a little misleading: the reason why those who wrote the classloading code relied on the context classloader is that this is the classloader that should be used by well behaved components running in J2EE containers. this is what correct isolation in a container environment means. there are many consequences which spring from this design decision. some of these are a little unfortunate. i think i agree with richard that a more pragmatic (rather than dogmatic) approach would be wiser. it would better to ensure that the configuration was appropriately isolated and then load a compatible class which satisfies the configuration. i suppose some thought would need to be put into whether there are any realistic security implementations if this approach were adopted. However, this dynamic discovery does not work. The copy of log4j.jar must be placed next to commons-logging.jar. this is a basic feature of delegating classloaders. (i even looked this one up to make sure my memory wasn't playing any tricks: http://java.sun.com/docs/books/vmspec/2nd-edition/html/ConstantPool.doc.html#72007) for example, suppose class Alpha is linked to class Beta which is linked to class Gamma, that classloader C is a child of classloader P, that C defines Alpha and Gamma and P Beta but not Gamma or Alpha. (apologies that my diagrams aren't up to richard's standards) Classloader Defines --- --- PBeta ^ | CAlpha Gamma Links - Alpha - Beta - Gamma now consider what happens what Alpha is loaded by C. C defines Alpha which is linked to Beta. therefore C is used to load Beta. C delegates to P which defines Beta. Beta links to Gamma. P defines Beta and is therefore used to load Gamma. However, P cannot load Gamma and so everything blows up. a practical example occurs when jars containing Alpha and Gamma is placed as a utility library in an EJB container and a jar containing Beta is placed in the container base loader. the usual rule (when delegating classloaders are involved) is that any dependencies need to be placed together or higher in the classloader hierarchy. since commons-logging-api by default has a runtime dependency upon commons-logging and commons-logging (a slightly fuzzy one) upon log4j when commons-logging-api is placed in the root classloader, so must the other jars. this would be equally true for a static arrangement (UGLI say). all dependent jars would need to be placed together or higher in the hierarchy. so, i'm not sure how this pertains to the static-verses-dynamic debate. IMHO, from a practical perspective having to ensure that libraries are in the right places comes with the territory: it's something that anyone involved with deployer J2EE applications gets used to doing. Moreover, the copy of commons-logging.jar must be placed very high in the class loader tree, say in the system class loader or the application server's class loader, but that is not all. There must not be other copies of commons-logging.jar in war files of web-apps. i'm not sure how true that is. the stock advice arises from the fact that JCL is used very widely and has a habit of turning up unexpected high in classloader hierarchy. To cut a long story short, do to anything meaningful reliably, JCL is a lot more complicated to set and even when set up correctly brings no benefits when compared to static API binding as found in UGLI. there are several use cases when static binding is admittedly superior. (for those i would prefer byte code engineering over multiple distributions since it allows selective doping of particular components but let's keep focused on compile time solutions). however, the price of deploying a statically bound API high in the hierarchy (where JCL is expected to be able to function) is that applications can no longer be configured in isolation. so, there is price to pay. I urge you run though the examples in [1] which should demonstrate to you how badly and irreparably JCL is broken. The source code is all there. Even if you don't trust my opinion, i'm very reluctant to take anyone's opinion on trust when it comes to JCL. the gestation of JCL was very difficult and the classloading code was developed over a period by senior tomcat folks. JCL also has a huge
Re: JCL problems
On Tue, 2005-02-08 at 11:30, Ceki Gülcü wrote: Richard et al., Before discussing relatively complex cases you mention, I think it would benefit everyone to realize that even in the most basic cases JCL will not work as expected. In your examples you refer to commons-logging-api.jar but commons-logging-api.jar and commons-logging.jar are not equivalent. The former does *not* include the classes org.apache.commons.logging.impl.Log4JLogger nor org.apache.commons.logging.impl.AvalonLogger. Thus, commons-logging-api.jar cannot bridge to log4j, for the simple reason that the bridge, Log4JLogger, is not there. Consider the following simple set up Parent [commons-logging.jar] ^ | Child [log4j.jar] Depending on how the thread context class loader is set,and the delegation model followed by the child loader, the above configuration, in the best case, will ignore log4j and in many other cases will throw a LogConfigurationException. The following set up will not fair much better (regardless of the delegation model): Parent [commons-logging.jar] ^ | Child [commons-logging.jar, log4j.jar] The only setup that will not blow in your face is the following: Parent [commons-logging-api.jar] ^ | Child [commons-logging.jar, log4j.jar] Note this is the set up used by Tomcat. Also note that it precludes the use of log4j by Tomcat itself as commons-logging-api.jar includes support for java.util.logging but not log4j. If you replace commons-logging-api.jar with commons-logging.jar, then you must place log4j.jar next to it unless you enjoy JCL throwing LogConfigurationExceptions at you. The bottom lines is that when compared with any statically bound bridging mechanism found in UGLI, JCL's dynamic discovery brings no added value but has a significant cost in aggravation. Richard, is there a point discussing complex cases when even the simplest ones don't work? Were you aware of problems in the above cases? (i'm not sure whether i'm missing your point.) it seems to me that it matters not whether the discovery is dynamic or static: the log4j library needs to be available in the classloader. static discovery would break in a similar fashion if a required library is not present. from a practical perspective, i don't really see having to put a number of libraries together so that they are loaded by the same classloader is really a restriction. what matters is that there are ways to achieve the required results and definite instructions are available which are easy for deployers to understand. - robert - To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
Re: JCL problems
Richard et al., Before discussing relatively complex cases you mention, I think it would benefit everyone to realize that even in the most basic cases JCL will not work as expected. In your examples you refer to commons-logging-api.jar but commons-logging-api.jar and commons-logging.jar are not equivalent. The former does *not* include the classes org.apache.commons.logging.impl.Log4JLogger nor org.apache.commons.logging.impl.AvalonLogger. Thus, commons-logging-api.jar cannot bridge to log4j, for the simple reason that the bridge, Log4JLogger, is not there. Consider the following simple set up Parent [commons-logging.jar] ^ | Child [log4j.jar] Depending on how the thread context class loader is set,and the delegation model followed by the child loader, the above configuration, in the best case, will ignore log4j and in many other cases will throw a LogConfigurationException. The following set up will not fair much better (regardless of the delegation model): Parent [commons-logging.jar] ^ | Child [commons-logging.jar, log4j.jar] The only setup that will not blow in your face is the following: Parent [commons-logging-api.jar] ^ | Child [commons-logging.jar, log4j.jar] Note this is the set up used by Tomcat. Also note that it precludes the use of log4j by Tomcat itself as commons-logging-api.jar includes support for java.util.logging but not log4j. If you replace commons-logging-api.jar with commons-logging.jar, then you must place log4j.jar next to it unless you enjoy JCL throwing LogConfigurationExceptions at you. The bottom lines is that when compared with any statically bound bridging mechanism found in UGLI, JCL's dynamic discovery brings no added value but has a significant cost in aggravation. Richard, is there a point discussing complex cases when even the simplest ones don't work? Were you aware of problems in the above cases? At 09:52 PM 2/7/2005, Richard Sitze wrote: One problem that we encounter, in one form or another, is when we look for a resource that may or may not be visible. For example, a configuration file. As delivered, the JCL jar files do *not* include a configuration file. Parent [commons-logging-api.jar, Log4J.jar] ^ | Child This works as expected, for the most part [other ClassLoader problems aside]. So how do you configure the child application, in this case, to use a different logger? You might introduce a configuration file. Parent [commons-logging-api.jar, Log4J.jar] ^ | Child [commons-logging.properties, LogKit.jar] [cut] -- Ceki Gülcü The complete log4j manual: http://www.qos.ch/log4j/ - To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
Re: JCL problems
Ceki Gülcü [EMAIL PROTECTED] wrote on 02/07/2005 01:32:26 PM: snip The idea of having separate jar files has been floated about for a few years, for various reasons. I believe it's only recently been associated with the idea that such would help alleviate classloader problems in the hierarchy [but only if certain rules are followed, as discussed in the original thread you referenced]. Sorry for my obtuseness but which rules are you referring to? Oh boy... ask a simple question... One problem that we encounter, in one form or another, is when we look for a resource that may or may not be visible. For example, a configuration file. As delivered, the JCL jar files do *not* include a configuration file. Parent [commons-logging-api.jar, Log4J.jar] ^ | Child This works as expected, for the most part [other ClassLoader problems aside]. So how do you configure the child application, in this case, to use a different logger? You might introduce a configuration file. Parent [commons-logging-api.jar, Log4J.jar] ^ | Child [commons-logging.properties, LogKit.jar] So... what happens if/when the parent changes to move from default behavior to specific configuration behavior? Parent [commons-logging-api.jar, commons-logging.properties, MyLocalLoggerImpl.jar] ^ | Child [commons-logging.properties, LogKit.jar] Naturally, that drives down into the child, the parent effectively mandates the logger for the children. This is a very natural scenario. So, of course... sooner or later in our not-so-mythical universe, we get a user who wants to return control back to the client application [EJB's]. They would typically do this by setting [or the system is preset] the ClassLoader to do child-first resource resolution: Parent [commons-logging-api.jar, commons-logging.properties, MyLocalLoggerImpl.jar] ^ | Child, child-first loading [commons-logging.properties, LogKit.jar] Now we locate the proper config for the child, and all works as expected. But equally viable and frequently seen in this situation is a self contained EAR file: the EAR file contains a copy of the commons-logging jar file, and depends on the default behavior built into the LogFactory, i.e. NO config file: Parent [commons-logging-api.jar, commons-logging.properties, MyLocalLoggerImpl.jar] ^ | Child, child-first loading [commons-logging-api.jar, LogKit.jar] In this case, the child's LogFactory is going to locate the commons-logging.properties file from the parent, and attempt to load MyLocalLoggerImpl [which extends Parent's Log interface] and return it through the Child's LogFactory as a Child Log interface... and of course we get an Exception. A guideline ['rule'] that was floated about was that a parent class loader must never contain a configuration file, whether it be commons-logging.properties or META-INF/services/org.apache.commons.logging.Log*. This is not something we enforce today, and it's really counter to the expected use/configuration of JCL. So instead, we try the guideline that all children *must* provide a configuration file, noting that it may be ignored with parent-first search behavior. This is a small improvement, but again unenforcable. However, I've put more thought into this since my proposal of a few months back. :-) There are really THREE independent things that need to be done: a. Based on the discussion of ClassLoading issues from the other day, this is more properly handled by a discovery process that does NOT look higher in the hierarchy than the ClassLoader used to load the target interface (Log, in this case). This would prevent discovery of a configuration that does not apply to the local Log. b. Step completely away from default behavior [auto discovery]. Everything should be configured explicitely, either by placing a config file in every variant of the distributable jar files or possibly by hard-coding the logger impl for each variant [needs more thought]. c. Configuration discovery must enforce the rule that only ONE configuration may exist in any one ClassLoader. This doesn't mean we can't have diff configs in each ClassLoader in a hierarchy, or different *ways* to configure within a ClassLoader instance: it means that we must *must* have a clear understanding of which configuration is appropriate at a single *point* in the hierarchy. For example, the following *could* legal [though we could take a hard-line stance against it also]: Parent [A.jar(commons-logging.properties)] ^ | Child [B.jar(commons-logging.properties), C.jar(META-INF/services/org.apache.commons.logging.Log)] This might be considered legal because the discovery process places a precidence on the two