Hi folks,
I've been having a peek at the Tomcat main CVS branch in respect of its classloading
behaviour. Apologies if I have missed any discussion - I have only just subscribed,
and I did not find much in the tomcat-dev archives a couple of days ago.
It appears to me that ClassLoading is not quite right in a couple of respects ( ie.
not working 'as advertised' ), and if you're willing, I'd like to have a go at a
couple of patches for it.
Here's the list of things that I consider to be broken, based on the understandings
that:
A) the hierarchy should go:
SystemCL
| <-- LAYER 1
lib/common CL
/ \ <-- LAYER 2
lib/container lib/apps CL
| <-- LAYER 3
WEB-INF/lib << same CL, but with URLs
WEB-INF/classes from both dirs, in this
order...
B) a classloader which failed to find a class
will delegate to it's parent a'la JDK 2 style,
with the delegation behaviour in:
JDK 1.2+ java.net.URLClassLoader
JDK 1.1 org.apache.tomcat.util.compat.SimpleClassLoader
List of broken behaviours:
1) LAYER 1 and LAYER 2 are broken ( do not delegate to parent )
org.apache.tomcat.modules.config.ProfileLoader [ -r ]
line 320: commonLoader is set to be its own parent, if there
was previously a commonLoader, or the System
CL if not.
line 331: ( same, with appLoader )
line 343: ( same, with containerLoader )
this is also a potential memory leak after a while,
if initClassLoaders() is called more than once.
2) LAYER 3 bypasses straight to the System CL:
there is a piece of code in
org.apache.tomcat.modules.config.LoaderInterceptor11 [
( line 197 - 207 )
if( useAL && !context.isTrusted() ) {
if( debug > 0 ) log( "Using webapp loader " + context.isTrusted());
parent=cm.getParentLoader();
} else if( useNoParent ) {
if( debug > 0 ) log( "Using no parent loader ");
parent=null;
} else {
if( debug > 0 ) log( "Using container loader ");
parent=this.getClass().getClassLoader();
}
I am not really sure why this piece of code is as it is.
note:
- useAL is set by setUseApplicationLoader( boolean ).
I could find no callers of this method in the source,
but the method is still public.
- same story for useNoParent, which is set by
setUseNoParent( boolean )
- since useAL is initialised in the constructor
to false, it would appear that if(1) is never called.
- since useNoParent also defaults to false, it
would appear that if(2) is never called
- so it must always default to if(3), and this line
does NOT gurarantee delegation to the
container loader! ( expected
Context.getContextManager().getContainerLoader()
instead )
3) Inconsistent directory-scanning behaviours
org.apache.tomcat.modules.config.ProfileLoader [ -r ]
makes use of IntrospectionUtils, but
orb.apache.tomcat.core.LoaderInterceptor11 [ -r ]
does not ...
as a result of this, there are different behaviours
in classloading with respect to the recursion of
directories.
Here's a list of questions I need answered to make some improvements:
0) Are there good historical reasons why the above behaviours are as they are?
1) is it OK to not use the IntrospectionUtils when constructing classpaths? I find
its API a little awkward. ( Would factor out classpath-ish utilities into their own
Class, deprecate existing classpath-ish methods on IntrospectionUtils ).
1b) assuming yes, what package should that class be placed into?
2) is it OK to introduce the following new behaviour that allows the user to specify
order in directories where classloading occurs?
IF there is a file called 'classpath.properties' ( call it what you like ), in the
root directory of a classpath location, or in any of its descended directories,
AND that file contains a line
'org.apache.tomcat.classpath.order=<comma_separated_list_of_files>'
THAT for each item in <comma_separated_list_of_files> which is actually an
immediate child of the directory according to the filesystem, it is placed into the
classpath in that particular order. Unorded entries would be appended to to the end
of the list for that directory.
[ I already have an implementation of this if anyone wants to see it first. Also,
I am prepared to writup the user-documentation before actually doing it. ]
3) where is the appropriate place to put user-documentation about classloading
behaviour?
4) is there a test ( eg. JUnit test? ) or test-suite ( eg. WatchDog? ) or test
web-app, that verifies contracts about classloading behaviour with respect to the
Servlet 2.2 specification?
regards,
David.
----
David Bullock - http://www.lisasoft.com/
Sun Certified Programmer for Java 2
"The key ingredients of success are a crystal-clear goal, a realistic attack plan to
achieve that goal, and consistent, daily action to reach that goal."
Steve Maguire, "Debugging the Development Process".