重新使用DOM模板时,Saxon不是线程安全的

时间:2015-08-20 14:57:19

标签: java saxon

我正在尝试使用Saxon HE作为内置JAXP实现的替代品,并且我遇到了各种各样的线程问题。

出于性能原因,我将模板缓存为DOM文档,并在这样的线程中重用它们

        TransformerFactory factory = TransformerFactory.newInstance();
        Document template = getFromCache(...);
        Transformer transformer = factory.newTransformer(new DOMSource(template));
        transformer.transform(new DOMSource(document), streamResult);

我知道DOM并不保证是线程安全的,但是当Saxon完全崩溃时,JAXP XSLT实现工作正常。怎么会这样?

有没有办法让它工作但是为了提高性能而保持缓存?

撒克逊人的例外发生在不同的地方,但最常见的是:

java.lang.NullPointerException: null
at com.sun.org.apache.xerces.internal.dom.ParentNode.nodeListItem(ParentNode.java:786) ~[na:1.7.0_71]
at com.sun.org.apache.xerces.internal.dom.ParentNode.item(ParentNode.java:800) ~[na:1.7.0_71]
at net.sf.saxon.dom.DOMSender.walkNode(DOMSender.java:154) ~[Saxon-HE-9.6.0-7.jar:na]
at net.sf.saxon.dom.DOMSender.outputElement(DOMSender.java:243) ~[Saxon-HE-9.6.0-7.jar:na]
at net.sf.saxon.dom.DOMSender.walkNode(DOMSender.java:162) ~[Saxon-HE-9.6.0-7.jar:na]
at net.sf.saxon.dom.DOMSender.outputElement(DOMSender.java:243) ~[Saxon-HE-9.6.0-7.jar:na]
at net.sf.saxon.dom.DOMSender.walkNode(DOMSender.java:162) ~[Saxon-HE-9.6.0-7.jar:na]
at net.sf.saxon.dom.DOMSender.outputElement(DOMSender.java:243) ~[Saxon-HE-9.6.0-7.jar:na]
at net.sf.saxon.dom.DOMSender.walkNode(DOMSender.java:162) ~[Saxon-HE-9.6.0-7.jar:na]
at net.sf.saxon.dom.DOMSender.outputElement(DOMSender.java:243) ~[Saxon-HE-9.6.0-7.jar:na]
at net.sf.saxon.dom.DOMSender.walkNode(DOMSender.java:162) ~[Saxon-HE-9.6.0-7.jar:na]
at net.sf.saxon.dom.DOMSender.outputElement(DOMSender.java:243) ~[Saxon-HE-9.6.0-7.jar:na]
at net.sf.saxon.dom.DOMSender.walkNode(DOMSender.java:162) ~[Saxon-HE-9.6.0-7.jar:na]
at net.sf.saxon.dom.DOMSender.send(DOMSender.java:92) ~[Saxon-HE-9.6.0-7.jar:na]
at net.sf.saxon.dom.DOMObjectModel.sendSource(DOMObjectModel.java:250) ~[Saxon-HE-9.6.0-7.jar:na]
at net.sf.saxon.event.Sender.send(Sender.java:221) ~[Saxon-HE-9.6.0-7.jar:na]
at net.sf.saxon.style.StylesheetModule.loadStylesheetModule(StylesheetModule.java:128) ~[Saxon-HE-9.6.0-7.jar:na]
at net.sf.saxon.style.Compilation.compilePackage(Compilation.java:131) ~[Saxon-HE-9.6.0-7.jar:na]
at net.sf.saxon.style.Compilation.compileSingletonPackage(Compilation.java:94) ~[Saxon-HE-9.6.0-7.jar:na]
at net.sf.saxon.s9api.XsltCompiler.compile(XsltCompiler.java:543) ~[Saxon-HE-9.6.0-7.jar:na]
at net.sf.saxon.jaxp.SaxonTransformerFactory.newTemplates(SaxonTransformerFactory.java:152) ~[Saxon-HE-9.6.0-7.jar:na]
at net.sf.saxon.jaxp.SaxonTransformerFactory.newTransformer(SaxonTransformerFactory.java:108) ~[Saxon-HE-9.6.0-7.jar:na]

2 个答案:

答案 0 :(得分:2)

不知道导致您所描述错误的原因,但javax.xml.transform具有Templates类,这是以线程安全方式重用样式表的官方方式,并避免创建和解析它们的开销对于每一次转变。

  1. 使用Templates为模板源制作缓存商店TransformerFactory.newTemplates(Source)而不是DOM文档。
  2. 在缓存的Templates对象上调用Templates.newTransformer()以获取单个转换的Transformer。
  3. 这甚至应该比您当前的解决方案更快。

    编辑:

    NPE似乎源于Xerces中的一行:

    fNodeListCache.fChildIndex = i;
    

    在我看来,这是一个强有力的线索,即对DOM的多线程访问导致错误,而不是Saxon。不知道为什么默认的TransformerFactory能够幸存下来。

答案 1 :(得分:1)

你说:“出于性能原因,我将模板缓存为DOM文档,并跨线程重用它们”。但这根本不会有助于提高性能。它只为您节省了I / O和XML解析成本,这只是编译样式表的一小部分成本。您应该缓存Templates对象,它是样式表的编译表示。这不仅可以提供更有效的缓存,而且还恰好是线程安全的。

也许内置的JAXP实现通过自己的同步来弥补DOM中缺少线程安全性。这当然是可能的,但这不是一个明智的用例,所以几乎没有必要。

你还应该知道,如果你关注性能,正如你所说的那样,那么以DOM形式提供实例文档对Saxon来说是非常低效的 - 它通常比允许Saxon使用它的内部慢5-10倍树格式。