Saxon-HE 9.3中javax.xml.xpath.XPathFactory提供程序配置文件中的语法错误

14

我在Mac OS X上使用Java SE 6和Saxon-HE 9.3.0.5。 ServiceLoader无法找到javax.xml.xpath.XPathFactory的Saxon实现。

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)

javax.xml.xpath.XPathFactorynewInstance方法的javadoc在查找实现过程的第3点中指出:

  

将为匹配资源目录META-INF / services中的javax.xml.xpath.XPathFactory请求服务提供程序provider-configuration文件的类加载器。有关文件格式和解析规则,请参见JAR文件规范。

JAR文件规范的服务提供者部分指出:

  

该文件应包含唯一的具体提供程序类名称的换行符分隔列表。

但是,如果我提取saxon9he.jar文件并查看META-INF目录,则会看到:

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

第一行是正确的,但我不知道为什么会有两行额外的内容,看起来这些内容会对ServiceLoader造成问题。我用一个测试例子发现了这个问题,并且了解了用于查找提供程序的机制。我们可以看到saxon9he.jar在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

关注的是这一行:

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

这是Saxon的一个bug还是我的系统不支持的扩展语法?我该怎么解决这个问题?

请注意,如果我显式选择实现类,我可以得到一个工厂。但我想使用Services机制。以下代码可以工作:

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

下面是完整的Java测试程序。

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);
    }
}

您可以使用'unzip -p /path/to/lib/jarfile.jar META-INF/services/javax.xml.xpath.XPathFactory'一步将文件直接提取到命令行。 - andrew lorien
2个回答

16

迈克尔·凯在SourceForge论坛上回答了这个问题。他说:

选择该文件格式是为了绕过JDK5的一个bug。

此外,他还表示:

实际上,我不建议使用JAXP搜索机制。它非常慢,而且提供的XPath引擎不一定适用于您的应用程序。您无法知道是否返回了XPath 1.0或2.0实现,并且API定义非常弱,除非您首先使用该提供程序对其进行了测试,否则很少有机会使您的应用程序与特定提供程序一起工作。因此,即使没有这个bug,我也会避免使用它。

我认为这回答了这个问题,即使它没有直接为问题提供解决方案。因此,我们可以通过编写以下内容来选择实现:

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

9
在Saxon 9.5及更高版本中,此问题已得到解决。如果有人发现自己需要使用旧版本来使其工作,并且没有访问调用newInstance的代码,则可以通过从上述JAR文件中删除services目录来解决该问题。这是在错误线程上的注释之后进行的。 - stav
在Saxon 9.6中,XPath的JAXP解析已被删除 - 请参见https://saxonica.plan.io/issues/1944#note-5 - Philip Helger

3

我知道这是一个较旧的帖子,但我的这篇文章可能会为问题提供一些启示。它使用了一个-D参数来找到XPathFactory,这是我从未见过文档记录的。


无论如何,感谢您的跟进。 - Ludovic Kuty

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接