Syntax error in javax.xml.xpath.XPathFactory provider-configuration file of Saxon-HE 9.3

Ludovic Kuty picture Ludovic Kuty · Oct 27, 2011 · Viewed 13k times · Source

I am using Java SE 6 on Mac OS X and Saxon-HE 9.3.0.5. The ServiceLoader is not able to find the Saxon implementation of javax.xml.xpath.XPathFactory.

mac:test2 ludo$ java -version
java version "1.6.0_26"
Java(TM) SE Runtime Environment (build 1.6.0_26-b03-383-11A511)
Java HotSpot(TM) 64-Bit Server VM (build 20.1-b02-383, mixed mode)

The javadoc of the newInstance method of javax.xml.xpath.XPathFactory states in point 3 of the lookup procedure to localize an implementation that:

The class loader is asked for service provider provider-configuration files matching javax.xml.xpath.XPathFactory in the resource directory META-INF/services. See the JAR File Specification for file format and parsing rules.

The Service Provider section of the JAR File Specification states that:

The file should contain a newline-separated list of unique concrete provider-class names.

But if I extract the saxon9he.jar file and look into the META-INF directory, I see:

mac:Java ludo$ mkdir test
mac:Java ludo$ cd test
mac:test ludo$ jar fx ../saxon9he.jar 
mac:test ludo$ cat META-INF/services/javax.xml.xpath.XPathFactory 
net.sf.saxon.xpath.XPathFactoryImpl
http\://java.sun.com/jaxp/xpath/dom:    net.sf.saxon.xpath.XPathFactoryImpl
http\://saxon.sf.net/jaxp/xpath/om:     net.sf.saxon.xpath.XPathFactoryImpl

The first line is correct but I can't see why there are two extra lines and it looks like those lines are causing trouble to ServiceLoader. I saw the problem with a test example that I wrote the understand the mecanism used to find a provider. We can see that saxon9he.jar is in the CLASSPATH.

mac:services ludo$ java ServicesTest
CLASSPATH = ..., /Users/ludo/Library/Java/saxon9he.jar, ...
Service XPathFactory: java.util.ServiceLoader[javax.xml.xpath.XPathFactory]
ServiceConfigurationError: javax.xml.xpath.XPathFactory: jar:file:/Users/ludo/Library/Java/saxon9he.jar!/META-INF/services/javax.xml.xpath.XPathFactory:2: Illegal configuration-file syntax

The line of interest is:

jar:file:/Users/ludo/Library/Java/saxon9he.jar!/META-INF/services/javax.xml.xpath.XPathFactory:2: Illegal configuration-file syntax

Is it a bug of Saxon or an extended syntax not supported by my system ? What could i do to solve the issue ?

Note that if I explicitly choose the class for the implementation, I can get a factory. But I want to use the Services mechanism. The following code works:

XPathFactory xpf = XPathFactory.newInstance(
  XPathFactory.DEFAULT_OBJECT_MODEL_URI,
  "net.sf.saxon.xpath.XPathFactoryImpl",
  ClassLoader.getSystemClassLoader());

I have added the whole Java test program below.

import java.net.URL;
import java.net.URLClassLoader;
import java.util.Iterator;
import java.util.ServiceConfigurationError;
import java.util.ServiceLoader;
import javax.xml.xpath.XPathFactory;

public class ServicesTest {
    public static String getClasspathString() {
        StringBuilder classpath = new StringBuilder();
        ClassLoader classLoader = ClassLoader.getSystemClassLoader();
        URL[] urls = ((URLClassLoader) classLoader).getURLs();
        for (int i = 0; i < urls.length - 1; i++) {
            classpath.append(urls[i].getFile()).append(", ");
        }
        if (urls.length > 0) {
            classpath.append(urls[urls.length - 1].getFile());
        }

        return classpath.toString();
    }

    public static void availableProviders(ServiceLoader sl) {
        Iterator it = sl.iterator();
        int index = 0;
        for (;;) {
            try {
                if (!it.hasNext()) {
                    break;
                }
                index++;
                Object o = it.next();
                System.out.printf("%03d Concrete class name: %s\n", index, o.getClass().getName());
            } catch (ServiceConfigurationError e) {
                System.err.printf("ServiceConfigurationError: %s\n", e.getMessage());
            }
        }
    }

    public static void main(String[] args) {
        System.out.printf("CLASSPATH = %s\n", getClasspathString());
        System.out.println();

        ServiceLoader<XPathFactory> slXPathFactory = ServiceLoader.load(XPathFactory.class);
        System.out.printf("Service XPathFactory: %s\n", slXPathFactory.toString());
        availableProviders(slXPathFactory);
    }
}

Answer

Ludovic Kuty picture Ludovic Kuty · Oct 28, 2011

Michael Kay answered the question on a SourceForge forum. He said that:

The format of the file was chosen to circumvent a JDK5 bug.

And also that:

Actually, I wouldn't recommend using the JAXP search mechanism anyway. It's very slow, and it delivers an XPath engine that won't necessarily work with your application. You have no way of knowing whether you get an XPath 1.0 or 2.0 implementation back, and the API is so weakly defined that there's very little chance your application will work with a particular provider unless you have tested it with that provider first. So even without this bug, I would steer clear of it.

I think it answers the question even if it does not provide an explicit fix for the problem. So we could chose the implementation by writing:

XPathFactory xpf = XPathFactory.newInstance(
  XPathFactory.DEFAULT_OBJECT_MODEL_URI,
  "net.sf.saxon.xpath.XPathFactoryImpl",
  ClassLoader.getSystemClassLoader());