Re: JCL problems

2005-02-14 Thread Ceki Gülcü
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

2005-02-14 Thread robert burrell donkin
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

2005-02-13 Thread robert burrell donkin
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

2005-02-08 Thread Ceki Gülcü
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

2005-02-07 Thread Richard Sitze
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