Uploaded image for project: 'Apache Jena'
  1. Apache Jena
  2. JENA-2310

NPE with geospatial property function due to query rewrite index issue

    XMLWordPrintableJSON

Details

    • Bug
    • Status: Closed
    • Major
    • Resolution: Fixed
    • Jena 4.4.0
    • Jena 4.5.0
    • GeoSPARQL
    • None

    Description

      Using a GeoSPARQL query with a geospatial property function, e.g.

      SELECT * {
      :x geo:hasGeometry ?geo1 .
      ?s2 geo:hasGeometry ?geo2 .
      ?geo1 geo:sfContains ?geo2
      }

      leads to an nondeterministic NPE when we're doing this query a a larger dataset.

      The reason is explained here:

      • evaluation of the property function leads to GenericPropertyFunction class with
       private QueryIterator bothBound(Binding binding, Node subject, Node predicate, Node object, ExecutionContext execCxt) {
      
              Graph graph = execCxt.getActiveGraph();
              QueryRewriteIndex queryRewriteIndex = QueryRewriteIndex.retrieve(execCxt);
      
              Boolean isPositiveResult = queryRewrite(graph, subject, predicate, object, queryRewriteIndex);
              if (isPositiveResult) {
      

       
      which leads to the query rewrite part and in QueryRewriteIndex class we have

       if (index.containsKey(key)) {
               result = index.get(key);
       } else {
               result = propertyFunction.testFilterFunction(subjectGeometryLiteral, objectGeometryLiteral);
               index.put(key, result);
      }
      

      index is an ExpiringMap which extends ConcurrentHashMap with an additional TimerTask clearing the map periodically - and this is why we get an NPE sometimes. Have a look at the lines above, in particular

      if (index.containsKey(key)){
         result = index.get(key); 
      }
      

       that part isn't atomic, thus, while containsKey could be true, the TimerTask might clear the map before we go to get - this will lead to a null value returned which will lead to an NPE in the check

      Since Java 8 the atomic way is to use computeIfAbsent, i.e. we can do

      Boolean result = index.computeIfAbsent(key, k -> propertyFunction.testFilterFunction(subjectGeometryLiteral, objectGeometryLiteral));

       

      Attachments

        Issue Links

          Activity

            People

              GregAlbiston Greg Albiston
              LorenzB Lorenz Bühmann
              Votes:
              0 Vote for this issue
              Watchers:
              2 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved: