[ https://issues.apache.org/jira/browse/JENA-1260?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=15670611#comment-15670611 ]
Dave Reynolds commented on JENA-1260: ------------------------------------- The TransitiveEngine is a bit of special case because it has a (hacky) implementation of rdfs:subPropertyOf aliases. The caches in question are only used when you have subPropertyOf alias (which isa corner case) but adding a triple which introduces a new alias causes those caches to get flagged for rebuild. So it's hard to provoke the very specific situation that will be affected without triggering a cache rebuild. I don't think this is performance issue because it's only when you add triples like ":p rdfs:subPropertyOf rdfs:subPropertyOf" that this cache rebuild is triggered and those are very rare outside of test cases! However, having thought about it more I realised that using a TransitiveReasoner directly, rather than an RDFS reasoner, gives more control and have generated a deterministic test case that failed before the fix and now passes. I'll submit that as a PR (partly as a check that I'm appropriately set up for doing PR's for Jena). > NullPointerException when using an inferencing model under multithreaded load > with query and delete > --------------------------------------------------------------------------------------------------- > > Key: JENA-1260 > URL: https://issues.apache.org/jira/browse/JENA-1260 > Project: Apache Jena > Issue Type: Bug > Components: Jena > Affects Versions: Jena 3.0.0 > Reporter: Stephen Owens > Assignee: Dave Reynolds > Attachments: TransitiveEngineFailureTest.java > > > {noformat} > java.lang.NullPointerException > at java.util.HashSet.contains(HashSet.java:203) > at > org.apache.jena.reasoner.transitiveReasoner.TransitiveEngine.triage(TransitiveEngine.java:194) > at > org.apache.jena.reasoner.transitiveReasoner.TransitiveEngine.add(TransitiveEngine.java:234) > at > org.apache.jena.reasoner.rulesys.FBRuleInfGraph.prepare(FBRuleInfGraph.java:431) > at > org.apache.jena.reasoner.BaseInfGraph.requirePrepared(BaseInfGraph.java:530) > at > org.apache.jena.reasoner.rulesys.FBRuleInfGraph.findWithContinuation(FBRuleInfGraph.java:557) > at > org.apache.jena.reasoner.rulesys.FBRuleInfGraph.graphBaseFind(FBRuleInfGraph.java:587) > at org.apache.jena.graph.impl.GraphBase.find(GraphBase.java:255) > at > org.apache.jena.sparql.engine.iterator.QueryIterTriplePattern$TripleMapper.<init>(QueryIterTriplePattern.java:75) > at > org.apache.jena.sparql.engine.iterator.QueryIterTriplePattern.nextStage(QueryIterTriplePattern.java:49) > at > org.apache.jena.sparql.engine.iterator.QueryIterRepeatApply.makeNextStage(QueryIterRepeatApply.java:108) > at > org.apache.jena.sparql.engine.iterator.QueryIterRepeatApply.hasNextBinding(QueryIterRepeatApply.java:65) > at > org.apache.jena.sparql.engine.iterator.QueryIteratorBase.hasNext(QueryIteratorBase.java:111) > at > org.apache.jena.sparql.engine.iterator.QueryIterBlockTriples.hasNextBinding(QueryIterBlockTriples.java:63) > at > org.apache.jena.sparql.engine.iterator.QueryIteratorBase.hasNext(QueryIteratorBase.java:111) > at > org.apache.jena.sparql.engine.iterator.QueryIteratorWrapper.hasNextBinding(QueryIteratorWrapper.java:39) > at > org.apache.jena.sparql.engine.iterator.QueryIteratorBase.hasNext(QueryIteratorBase.java:111) > at > org.apache.jena.sparql.engine.iterator.QueryIteratorWrapper.hasNextBinding(QueryIteratorWrapper.java:39) > at > org.apache.jena.sparql.engine.iterator.QueryIteratorBase.hasNext(QueryIteratorBase.java:111) > at org.apache.jena.atlas.iterator.Iter$4.hasNext(Iter.java:264) > at > org.apache.jena.ext.com.google.common.collect.Iterators$5.hasNext(Iterators.java:547) > at > org.apache.jena.test.TransitiveEngineFailureTest$FailureTask.execQuery(TransitiveEngineFailureTest.java:109) > at > org.apache.jena.test.TransitiveEngineFailureTest$FailureTask.call(TransitiveEngineFailureTest.java:75) > at > org.apache.jena.test.TransitiveEngineFailureTest$FailureTask.call(TransitiveEngineFailureTest.java:1) > at java.util.concurrent.FutureTask.run(FutureTask.java:266) > at > java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) > at > java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) > at java.lang.Thread.run(Thread.java:745) > {noformat} > This failure is due to the > {{org.apache.jena.reasoner.transitiveReasoner.TransitiveEngine}} class > declaring the {{subClassAliases}} > member variable (of type HashSet<Node>) as static. The prepare method > re-creates this shared variable whenever {{isPrepared}} > is false: > {noformat} > private void prepare() { > if (isPrepared) return; > subClassAliases = new HashSet<>(); > . . . > isPrepared = true; > } > {noformat} > In particular, the static {{subClassAliases}} variable will be re-created > whenever a {{TransitiveEngine}} object is created > ({{isPrepared}} is false at creation time). Note that HashSet is neither > thread-safe nor being accessed in a thread-safe manner. > The combination of attempting to remove triples from the model and attempting > to execute queries on the model leads > to unsafe accesses to {{subClassAliases}}, which eventually leads to a null > pointer exception. -- This message was sent by Atlassian JIRA (v6.3.4#6332)