ant-user mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From jim otoole <>
Subject Class Loading Problems
Date Tue, 31 Jul 2001 16:09:59 GMT
Hello, I'm using Ant 1.3 to run JUnit 3.7 tests for an
application that uses Xerces 1.4.1 and Xalan 2.1.
However, my tests fail on trying to parse a DOM
document because of a problem with a class loader (I'm
not sure which loader). java.lang.LinkageError
"loader constraints violated when linking
org/xml/sax/InputSource class" I've had the same
problem when running the same tests
on the same application using the Swing or AWT JUnit
test runner (and NOT using Ant). I managed to fix
this problem thanks to a suggestion on the JUnit
mailing list from Scott Stirling. He suggests a change
to the file in the JUnit jar.
This file tells the JUnit TestCaseClassLoader which
classes it should not try to load. Instead these
classes should be left to the JVM class loader.
His suggestion was to supplement the list of excluded
classes to exclude all org.w3c.dom.* and or.xml.sax.*
classes. However, this workaround does not fix the
problem of
running JUnit tests from Ant where the tests (or the
application being tested) involve org.xml.sax.*
classes. Has anyone else experienced this problem?
Does anyone
know of any other fix that could solve this problem?
Perhaps, it's a flaw in the AntClassLoader but as I
don't know much about class loading matters I need
some assistance in pin pointing the problem. Does
anyone have any suggestions? Incidentally, I tried
removing the JAXP jars (jaxp.jar
and parser.jar) that come with Ant and replacing them
with the Xalan and Xerces jars that my application
uses. Ant continues to work fine but the tests still
won't run. The reason I made this change was that I
was under the impression (from reading the Java Spec
that there would be no problem having two class
loaders loading the same class. I also tried adding
org.apache.* to the file (so that junit
wouldn't try loading Ant/Xerces/Xalan classes!) but
this didn't work either. Thanks,
Jim. P.S. This is what Scott Stirling had to say about
matter: The JUnit TestCaseClassLoader (the reloadable
one) has
a fundamental problem that results in frequent
LinkageErrors under the following conditions: 1.  You
use the JUnit Swing UI.
2.  You use JAXP in your test cases or any classes
referenced in your test cases, or even any
"instanceof" expressions that reference JAXP classes.
(By JAXP I mean the whole set of javax.* classes plus
the org.w3c.dom.* and org.xml.sax.* classes) I can go
into this in depth, but the bottom line
problems are that JUnit loads classes from the same
place as the JVM's system loader; the classpath.
This necessitates the use of an exclusion list to
filter out certain class names that either must be
loaded by the system loader, or you would like to
have loaded by the system loader.  The JUnit loader
does not always delegate to the system loader when it
should, particularly in the case of JAXP, which
is a weird mix of classes whose names begin with the
filtered "javax.*" and the unfiltered "org.w3c.*" and
org.xml.*". JAXP is a special case because it is based
on a set of
javax.* classes.  All javax.* classes are excluded
from the JUnit loader by default (in the
default file) in junit.jar.  But
JAXP, as shipped from Sun, comes with a bunch of other
classes in org.w3c.* and org.xml.*.  The
interesting thing is a direct dependency between
javax.* classes and some other classes not in the
usual exclusion list of com.sun.*, javax.*, etc. So
what can happen, and frequently does when using the
JUnit Swing UI with test cases or other classes, such
as Log4J, that use JAXP, is that the JUnit
class loader (properly) delegates javax.xml.* classes
it "sees" to the system loader.  But then the system
loader loads up a bunch of org.w3c.dom
and/or org.xml.sax classes as the result of
initializing and loading that JAXP class.  Later, if
the JUnit loader comes across some org.w3c/xml class
that it's never seen before, it tries to load it
because the classname doesn't match one of the
patterns in the exclude list. But it's already been
loaded through the "backdoor" as
the result of some other class loaded by the system
loader (remember, the JVM keeps classes in
their own namespace by identifying them by their fully
qualified name plus the instance of their _defining_
(not initiating) loader, AND, the JVM will
attempt to assign all unloaded classes referenced by a
defined class to that defining class's loader).  The
JVM's classresolver routine keeps track of
all these class loading events and "sees" that the
JUnit loader is attempting to define a class that has
already been defined by the system loader.  That's
wrong because according to the rules of loader
constraints, JUnit should delegate this load to the
system loader. You can hack around this (I did) by
catching the
LinkageError in TestCaseClassLoader's loadClass()
method and then making a recovery call to
findSystemClass() -- thereby delegating to the system
loader after the fact (which is OK).  This hack only
works some of the time, though, because now
you can have the reverse problem where the JUnit
loader loads a host of org.*.* classes, and then the
system loader violates the loader contraints
at some point when it tries to do exactly what I
described above with JAXP because it doesn't ever
delegate to its child (the JUnit loader).
Inevitably, if your test cases use many JAXP and
related XML classes, one or the other ClassLoader will
end violating the constraints whatever you do. So the
solution in the existing JUnit is to definitely
add org.w3c.dom.* and org.xml.sax.* to your if you're using any JAXP stuff.
What can we learn from this?  Well, one thing is that
it's a good idea to have your custom class loaders
load classes from repositories other than the
system classpath.  Note that the JVM's built-in
classloaders work that way (one for the jre/ext dirs,
another for the java.class.path). Anyway, the fix is just a matter
of time before it becomes standard in JUnit, since
JAXP will be a standard part of the 1.4
JDK.  It'll be just like having org.omg.* excluded.

Do You Yahoo!?
Make international calls for as low as $.04/minute with Yahoo! Messenger

View raw message