Richard Sitze
2005-01-27 22:52:02 UTC
I'd like to begin to identify the ClassLoader problems with the
current JCL discovery mechanism. If you are aware of additional
issues, please respond and let's get them all out on the table.
I believe the following two scenarios summarize the specific issues,
as well as more general problems.
A. Parent / Child ClassLoaders, General
- commons-logging.jar#org.apache.commons.logging.Log is
loaded/loadable by Parent.
- Child is the thread context ClassLoader.
- Parent defines a LogAWrapper for LogAImpl
- Child defines a LogAImpl
Problem:
1. Discovery finds Child[LogAImpl], and attempts to instantiate
LogAWrapper in Parent. Fails, because Parent cannot see child.
B. Parent / Child ClassLoaders, Child does not defer to Parent first.
- commons-logging.jar#org.apache.commons.logging.Log is
loaded/loadable by Parent.
- Child is the thread context ClassLoader.
- commons-logging.jar#org.apache.commons.logging.Log is
loaded by the Child.
- It is clear that Parent[Log] and Child[Log] are different Classes.
Problems:
1. The discovery process allows a Log implementation defined by the
Parent to be discovered by the Child as the child executes
Child[LogFactory]. This does NOT happen by way of the
relationship between the Log and the LogImpl, because the
Parent[Log] and the Child[Log] are not the same class.
It happens via classes named in configuration files:
commons-logging.properties and
META-INF/Services/org.apache.commons.logging.Log.
2. The discovery process allows a Log implementation defined by the
Child to be discovered by the Parent, as the parent executes
Parent[LogFactory], via the thread context class loader.
Examples include dropping Log4J into the child, and the [old]
behavior that favors Log4J forcing it and it's JCL wrapper
to be loaded via the Child and exposed to the parent.
C. Host / Sub
- commons-logging.jar#org.apache.commons.logging.Log is
loaded/loadable by Host.
- A host, such as JUnit, creates and manages an independent Sub
ClassLoader
- Sub does NOT reference Host as a parent.
- Sub is set as the thread context ClassLoader.
- Execution is within code belonging to Host.
Problems:
1. The discovery process may *fail* altogether as it starts with the
thread context class loader, and cannot reach the Host loader.
2. The discovery process allows a Log implementation defined by the
Sub to be discovered by the Host, as the host executes
Host[LogFactory], via the thread context class loader.
Consider the case where the *Sub* defines
commons-logging.properties
or META-INF/Services/org.apache.commons.logging.Log.
SUMMARY of PROBLEM:
There are ONE general problem at work here:
- Disrespect for proper ISOLATION as defined by ClassLoaders
The fundamental problem is dealing with the thread context classloader
in [common] situations where it represents an "isolated" or
"isolating" mechanism. For scenario A, we are simply loading
when we should be. For scenario B, by setting "child first"
search behavior, we are granting the child a degree of independence.
For scenario C, by creating a Sub that isn't related to the
"parent" at all.
In both cases, JCL behavior incorrectly *assumes* that a more
traditional "Parent/Child" relationship exists with the thread context
classloader.
While the current mechanism *is* generally useful, I'm realizing
that we've only addressed one corner case, and have broken other
*very* reasonable classloader configurations.
WHAT CAN BE DONE:
- For A.1, For auto configuration of for predefined wrapper impls,
any "impl" class should be located using the ClassLoader of the
wrapper, never the thread context class loader.
Detect "independent" thread context class loaders and respect the
boundries. By independent, I mean any classloader that gives
preference to it's own "internal" classes.
- The trivial case is a thread context class loader that doesn't
include the current ClassLoader [used to load 'this' class, i.e.
LogFactory] in it's hierarchy. In this case, the discovery
mechanism simply has *NO* business crossing the boundry, it should
revert back to using the ClassLoader used to load the [currently
executing] LogFactory. This should cover scenario C.
- Scenario B is a bit more difficult to resolve.
For B.1, the child "resource manager" [LogFactory] is in
control. We can presume during discovery that we should NOT
look any higher up the hierarchy than the level at which our
interface [Log or LogFactory] was discovered for configuration
or implementation resources. [various games can be played
to determine which loader in a hierarchy was used to obtain
any particular resource].
For B.2, the parent "resouce manager" [LogFactory] is in
control. We can "check" by requesting our base interface [Log or
LogFactory] from the thread context class loader. If the class
returned is not the same as what we have in the Parent, then
we should treat this as the trivial case: use the LogFactory
classloader and do not cross that boundry.
<ras>
*******************************************
Richard A. Sitze
IBM WebSphere WebServices Development
current JCL discovery mechanism. If you are aware of additional
issues, please respond and let's get them all out on the table.
I believe the following two scenarios summarize the specific issues,
as well as more general problems.
A. Parent / Child ClassLoaders, General
- commons-logging.jar#org.apache.commons.logging.Log is
loaded/loadable by Parent.
- Child is the thread context ClassLoader.
- Parent defines a LogAWrapper for LogAImpl
- Child defines a LogAImpl
Problem:
1. Discovery finds Child[LogAImpl], and attempts to instantiate
LogAWrapper in Parent. Fails, because Parent cannot see child.
B. Parent / Child ClassLoaders, Child does not defer to Parent first.
- commons-logging.jar#org.apache.commons.logging.Log is
loaded/loadable by Parent.
- Child is the thread context ClassLoader.
- commons-logging.jar#org.apache.commons.logging.Log is
loaded by the Child.
- It is clear that Parent[Log] and Child[Log] are different Classes.
Problems:
1. The discovery process allows a Log implementation defined by the
Parent to be discovered by the Child as the child executes
Child[LogFactory]. This does NOT happen by way of the
relationship between the Log and the LogImpl, because the
Parent[Log] and the Child[Log] are not the same class.
It happens via classes named in configuration files:
commons-logging.properties and
META-INF/Services/org.apache.commons.logging.Log.
2. The discovery process allows a Log implementation defined by the
Child to be discovered by the Parent, as the parent executes
Parent[LogFactory], via the thread context class loader.
Examples include dropping Log4J into the child, and the [old]
behavior that favors Log4J forcing it and it's JCL wrapper
to be loaded via the Child and exposed to the parent.
C. Host / Sub
- commons-logging.jar#org.apache.commons.logging.Log is
loaded/loadable by Host.
- A host, such as JUnit, creates and manages an independent Sub
ClassLoader
- Sub does NOT reference Host as a parent.
- Sub is set as the thread context ClassLoader.
- Execution is within code belonging to Host.
Problems:
1. The discovery process may *fail* altogether as it starts with the
thread context class loader, and cannot reach the Host loader.
2. The discovery process allows a Log implementation defined by the
Sub to be discovered by the Host, as the host executes
Host[LogFactory], via the thread context class loader.
Consider the case where the *Sub* defines
commons-logging.properties
or META-INF/Services/org.apache.commons.logging.Log.
SUMMARY of PROBLEM:
There are ONE general problem at work here:
- Disrespect for proper ISOLATION as defined by ClassLoaders
The fundamental problem is dealing with the thread context classloader
in [common] situations where it represents an "isolated" or
"isolating" mechanism. For scenario A, we are simply loading
when we should be. For scenario B, by setting "child first"
search behavior, we are granting the child a degree of independence.
For scenario C, by creating a Sub that isn't related to the
"parent" at all.
In both cases, JCL behavior incorrectly *assumes* that a more
traditional "Parent/Child" relationship exists with the thread context
classloader.
While the current mechanism *is* generally useful, I'm realizing
that we've only addressed one corner case, and have broken other
*very* reasonable classloader configurations.
WHAT CAN BE DONE:
- For A.1, For auto configuration of for predefined wrapper impls,
any "impl" class should be located using the ClassLoader of the
wrapper, never the thread context class loader.
Detect "independent" thread context class loaders and respect the
boundries. By independent, I mean any classloader that gives
preference to it's own "internal" classes.
- The trivial case is a thread context class loader that doesn't
include the current ClassLoader [used to load 'this' class, i.e.
LogFactory] in it's hierarchy. In this case, the discovery
mechanism simply has *NO* business crossing the boundry, it should
revert back to using the ClassLoader used to load the [currently
executing] LogFactory. This should cover scenario C.
- Scenario B is a bit more difficult to resolve.
For B.1, the child "resource manager" [LogFactory] is in
control. We can presume during discovery that we should NOT
look any higher up the hierarchy than the level at which our
interface [Log or LogFactory] was discovered for configuration
or implementation resources. [various games can be played
to determine which loader in a hierarchy was used to obtain
any particular resource].
For B.2, the parent "resouce manager" [LogFactory] is in
control. We can "check" by requesting our base interface [Log or
LogFactory] from the thread context class loader. If the class
returned is not the same as what we have in the Parent, then
we should treat this as the trivial case: use the LogFactory
classloader and do not cross that boundry.
<ras>
*******************************************
Richard A. Sitze
IBM WebSphere WebServices Development