0

我们的生产系统(Java7 运行时)中的 XSLT 转换时常会失败,并出现下面提供的 NullPointerException。据我从 Saxon9 源码中了解到,LRUCache 类是 AnyURIValue 类中的一个静态类成员。我对此进行了一些研究,并读到这可能是由于底层 LinkedHashMap 上的并发和非同步调用,这可以解释问题似乎出现在我们的随机时间点。

The [java.lang.NullPointerException] occurred during XSLT transformation:  java.lang.NullPointerException
    at com.tibco.plugin.xml.XMLTransformActivity.eval(Unknown Source)
    at com.tibco.pe.plugin.Activity.eval(Unknown Source)
    at com.tibco.pe.core.TaskImpl.eval(Unknown Source)
    at com.tibco.pe.core.Job.a(Unknown Source)
    at com.tibco.pe.core.Job.k(Unknown Source)
    at com.tibco.pe.core.JobDispatcher$JobCourier.a(Unknown Source)
    at com.tibco.pe.core.JobDispatcher$JobCourier.run(Unknown Source)
caused by: java.lang.NullPointerException at java.util.LinkedHashMap$Entry.remove(Unknown Source)
    at java.util.LinkedHashMap$Entry.recordRemoval(Unknown Source)
    at java.util.HashMap.removeEntryForKey(Unknown Source)
    at java.util.LinkedHashMap.addEntry(Unknown Source)
    at java.util.HashMap.put(Unknown Source)
    at net.sf.saxon.sort.LRUCache.put(LRUCache.java:47)
    at net.sf.saxon.value.AnyURIValue.isValidURI(AnyURIValue.java:103)
    at net.sf.saxon.instruct.Namespace.checkPrefixAndUri(Namespace.java:184)
    at net.sf.saxon.instruct.Namespace.processLeavingTail(Namespace.java:144)
    at net.sf.saxon.instruct.Block.processLeavingTail(Block.java:399)
    at net.sf.saxon.instruct.Instruction.process(Instruction.java:94)
    at net.sf.saxon.instruct.ElementCreator.processLeavingTail(ElementCreator.java:298)
    at net.sf.saxon.instruct.Template.applyLeavingTail(Template.java:175)
    at net.sf.saxon.instruct.ApplyTemplates.applyTemplates(ApplyTemplates.java:343)
    at net.sf.saxon.instruct.ApplyTemplates.defaultAction(ApplyTemplates.java:376)
    at net.sf.saxon.instruct.ApplyTemplates.applyTemplates(ApplyTemplates.java:331)
    at net.sf.saxon.Controller.transformDocument(Controller.java:1735)
    at net.sf.saxon.Controller.transform(Controller.java:1559)
    at com.tibco.plugin.xml.XMLTransformActivity.new(Unknown Source)
    at com.tibco.plugin.xml.XMLTransformActivity.eval(Unknown Source)
    at com.tibco.pe.plugin.Activity.eval(Unknown Source)
    at com.tibco.pe.core.TaskImpl.eval(Unknown Source)
    at com.tibco.pe.core.Job.a(Unknown Source)
    at com.tibco.pe.core.Job.k(Unknown Source)
    at com.tibco.pe.core.JobDispatcher$JobCourier.a(Unknown Source)
    at com.tibco.pe.core.JobDispatcher$JobCourier.run(Unknown Source)
  • 有人在 Saxon9 上遇到过同样的问题吗?
  • 尝试在 Eclipse 中重现该问题的最佳方法是什么?
4

2 回答 2

1

Saxon9 不是一个版本,而是一系列 6 个主要版本(9.0 到 9.5)以及在大约 7 年内发布的更多维护版本。您需要更准确地了解您使用的版本。

多年来,已经对涉及 LRUCache 的问题进行了一些修复,例如

https://saxonica.plan.io/issues/1481 https://saxonica.plan.io/issues/1619

尽管这些都没有将 NullPointerException 描述为观察到的症状,但它们可以反映相同的根本原因。

Saxon 9.5 代码在需要时使用 ConcurrentHashMap,并且 AnyURIValue 类不再使用 LRUCache。

于 2013-09-20T13:07:32.880 回答
0

重现该行为,这似乎是 Saxon9 的命名空间 LRUCache 中的并发问题。缓存上的 put 和 get 操作都会发生错误。下面的代码模拟了这个问题。LRUCache 是 Saxon 的 AnyURIValue.class 中的静态类成员,对底层 LinkedHashMap 的访问是不同步的。

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class Main {

private static final int NTHREDS = 100;

private static final int EXECUTION_COUNT = 10000;

public static void main(String[] args) throws InterruptedException {
    ExecutorService executor = Executors.newFixedThreadPool(NTHREDS);
    for (int i = 0; i < EXECUTION_COUNT; i++) {
        Runnable worker = new TestRunnable(i);
        executor.execute(worker);
    }

    executor.shutdown();
    executor.awaitTermination(10, TimeUnit.SECONDS);
}
}



import net.sf.saxon.sort.LRUCache;

public class TestRunnable implements Runnable {

private static LRUCache cache = new LRUCache(20); // The LRUCache is a static member in Saxon's AnyURIValue.class!

private int number;

public TestRunnable(int number){
    this.number = number;
}

@Override
public void run() {
    /**
     *  Access elements in ascending order to trigger the call to LinkedHashMap$Entry.remove;  
     */
    for(int i = 0; i < number; i++){
        cache.get(new Integer(i));
    }

    /**
     *  Add the new element to the cache. 
     *  As soon as the cacheSize exceeds 20, the cache is starting to remove the last accessed element.
     */
    cache.put(new Integer(number), new Integer(number));

}
}
于 2013-09-20T10:34:14.190 回答