Skip to content

Commit 65d794c

Browse files
committed
Merge branch 'xml-woes'
This topic branch adds a rather ugly, but required, work-around when 3rd-party libraries provide XML components that can easily lead to class path problems when processing XML data via XPath. The symptom is invariable an exception with a trace like this one: > java.lang.NoSuchMethodError: org.apache.xpath.XPathContext.<init>(Z)V > at org.apache.xpath.jaxp.XPathImpl.eval(XPathImpl.java:207) > at org.apache.xpath.jaxp.XPathImpl.evaluate(XPathImpl.java:281) > at org.apache.xpath.jaxp.XPathImpl.evaluate(XPathImpl.java:371) > at org.scijava.util.XML.<init>(XML.java:141) > [...] Such errors are caused by whatever implementation of the XPathFactory finds to be claiming the DocumentBuilderFactoryImpl service in the corresponding META-INF/services/. For example, the Batik component in Fiji ships a part of xalan (which is actually not needed for Java 1.6 and later, because Xalan is now bundled with Java), and likewise does OMERO insight ship xalan, but an *incompatible* version of it. As a work-around, we use the fact that the XPathFactory asks the current thread's context class loader to discover the services. If it discovers a non-functional XPath implementation, we set the context class loader to the parent class loader until we find a working XPath implementation. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
2 parents 7015070 + 18cf9d9 commit 65d794c

File tree

1 file changed

+48
-1
lines changed

1 file changed

+48
-1
lines changed

src/main/java/org/scijava/util/XML.java

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,9 @@ public class XML {
7676
/** XPath evaluation mechanism. */
7777
private final XPath xpath;
7878

79+
private final boolean debug =
80+
"debug".equals(System.getProperty("scijava.log.level"));
81+
7982
/** Parses XML from the given file. */
8083
public XML(final File file) throws ParserConfigurationException,
8184
SAXException, IOException
@@ -113,7 +116,51 @@ public XML(final Document doc) {
113116
public XML(final String path, final Document doc) {
114117
this.path = path;
115118
this.doc = doc;
116-
xpath = XPathFactory.newInstance().newXPath();
119+
120+
// Protect against class skew: some ImageJ projects find it funny to ship
121+
// outdated xalan, sometimes causing problems due to incompatible
122+
// xalan/xerces combinations.
123+
//
124+
// We work around that by letting the XPathFactory try with the current
125+
// context class loader, and fall back onto its parent until it succeeds
126+
// (because the XPathFactory will ask the context class loader to find the
127+
// configured services, including the
128+
// com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl).
129+
if (debug) {
130+
System.err.println(ClassUtils.getLocation(XPathFactory.class));
131+
}
132+
133+
XPath xpath = null;
134+
final Thread thread = Thread.currentThread();
135+
final ClassLoader contextClassLoader = thread.getContextClassLoader();
136+
try {
137+
ClassLoader loader = contextClassLoader;
138+
for (;;) try {
139+
xpath = XPathFactory.newInstance().newXPath();
140+
try {
141+
// make sure that the current xalan/xerces pair can evaluate
142+
// expressions (i.e. *not* throw NoSuchMethodErrors).
143+
xpath.evaluate("//dummy", doc);
144+
} catch (Throwable t) {
145+
if (debug) {
146+
System.err.println("There was a problem with " +
147+
xpath.getClass() + " in " +
148+
ClassUtils.getLocation(xpath.getClass()) + ":");
149+
t.printStackTrace();
150+
}
151+
throw new Error(t);
152+
}
153+
break;
154+
} catch (Error e) {
155+
if (debug) e.printStackTrace();
156+
loader = loader.getParent();
157+
if (loader == null) throw e;
158+
thread.setContextClassLoader(loader);
159+
}
160+
this.xpath = xpath;
161+
} finally {
162+
thread.setContextClassLoader(contextClassLoader);
163+
}
117164
}
118165

119166
// -- XML methods --

0 commit comments

Comments
 (0)