Fix offset retrieval in concurrent getMatchInfo requests

Change-Id: I018b45e99b81387889268b2b524ebcd6928727a1
diff --git a/Changes b/Changes
index 3eebcdd..0fca990 100644
--- a/Changes
+++ b/Changes
@@ -1,8 +1,10 @@
-0.58.8 2019-11-05
+0.58.8 2019-11-06
     - [bugfix] Fix offset retrieval in concurrent searches
       (diewald)
     - [cleanup] Removed deprecated numberOf() method from index
       (diewald)
+    - [bugfix] Fix offset retrieval in concurrent getMatchInfo requests
+      (diewald)
 
 0.58.7 2019-09-16
     - [bugfix] Fix the behaviour of negative operands in virtual
diff --git a/src/main/java/de/ids_mannheim/korap/KrillIndex.java b/src/main/java/de/ids_mannheim/korap/KrillIndex.java
index ff71249..8efb926 100644
--- a/src/main/java/de/ids_mannheim/korap/KrillIndex.java
+++ b/src/main/java/de/ids_mannheim/korap/KrillIndex.java
@@ -11,10 +11,6 @@
 import java.time.LocalDate;
 
 import org.apache.lucene.analysis.Analyzer;
-/*
-import org.apache.lucene.analysis.standard.StandardAnalyzer;
-import org.apache.lucene.analysis.core.WhitespaceAnalyzer;
-*/
 import org.apache.lucene.analysis.miscellaneous.PerFieldAnalyzerWrapper;
 import org.apache.lucene.document.Document;
 import org.apache.lucene.index.DirectoryReader;
@@ -165,7 +161,7 @@
     private HashMap termContexts;
     private ObjectMapper mapper = new ObjectMapper();
 
-    private static ByteBuffer bbTerm = ByteBuffer.allocate(32);
+    // private ByteBuffer bbTerm;
 
     // Some initializations ...
     {
@@ -960,6 +956,7 @@
         Filter filter = (Filter) new QueryWrapperFilter(bool);
 
         CompiledAutomaton fst = null;
+        ByteBuffer bbTerm = ByteBuffer.allocate(32);
 
         if (info) {
             /* Create an automaton for prefixed terms of interest.
diff --git a/src/test/java/de/ids_mannheim/korap/index/TestParallelIndex.java b/src/test/java/de/ids_mannheim/korap/index/TestParallelIndex.java
index 4834dab..66d8d03 100644
--- a/src/test/java/de/ids_mannheim/korap/index/TestParallelIndex.java
+++ b/src/test/java/de/ids_mannheim/korap/index/TestParallelIndex.java
@@ -1,6 +1,7 @@
 package de.ids_mannheim.korap.index;
 import static org.junit.Assert.assertEquals;
 
+import java.util.*;
 import java.io.IOException;
 import java.nio.file.Paths;
 import java.util.concurrent.*;
@@ -13,6 +14,7 @@
 import de.ids_mannheim.korap.Krill;
 import de.ids_mannheim.korap.KrillIndex;
 import de.ids_mannheim.korap.response.Result;
+import de.ids_mannheim.korap.response.Match;
 
 import org.apache.lucene.store.MMapDirectory;
 
@@ -164,4 +166,137 @@
         
         executor.shutdown();
     };
+
+    @Test
+    public void TestSampleIndexMatchinfoParallel () throws IOException, QueryException, InterruptedException, ExecutionException {
+
+        sample = getSampleIndex();
+        // The sample index is global
+
+        Callable<String> req1 = new Callable<String>(){
+                @Override
+                public String call() throws Exception {
+
+                    Match km = sample.getMatchInfo(
+                        "match-GOE/AGI/00000-p1075-1076",
+                        "tokens",
+                        true,
+                        (ArrayList) null,
+                        (ArrayList) null,
+                        false,
+                        true,
+                        false
+                        );
+
+                    if (km.getStartPos() != 1075) {
+                        return "1-StartPos=" + km.getStartPos();
+                    }
+                    if (km.getEndPos() != 1076) {
+                        return "1-EndPos=" + km.getEndPos();
+                    }
+
+                    if (!km.getSnippetBrackets().equals(
+                            "... [[{corenlp/p:PPOSAT:{marmot/m:case:acc:{marmot/m:gender:neut:{marmot/m:number:pl:{marmot/p:PPOSAT:{opennlp/p:PPOSAT:{tt/l:mein:{tt/p:PPOSAT:meine}}}}}}}}]] ..."
+                            )) {
+                        return "1-Snippet=" + km.getSnippetBrackets();
+                    }
+                    return "ok";
+                }
+            };
+
+        Callable<String> req2 = new Callable<String>(){
+                @Override
+                public String call() throws Exception {
+
+                    Match km = sample.getMatchInfo(
+                        "match-GOE/AGD/00000-p142169-142170",
+                        "tokens",
+                        true,
+                        (ArrayList) null,
+                        (ArrayList) null,
+                        false,
+                        true,
+                        false
+                        );
+
+                    if (km.getStartPos() != 142169) {
+                        return "2-StartPos=" + km.getStartPos();
+                    }
+                    if (km.getEndPos() != 142170) {
+                        return "2-EndPos=" + km.getEndPos();
+                    }
+
+                    if (!km.getSnippetBrackets().equals(
+                            "... [[{corenlp/p:NN:{marmot/m:case:acc:{marmot/m:gender:masc:{marmot/m:number:sg:{marmot/p:NN:{opennlp/p:NN:{tt/l:Baum:{tt/p:NN:Baum}}}}}}}}]] ..."
+                            )) {
+                        return "2-Snippet=" + km.getSnippetBrackets();
+                    }
+                    return "ok";
+                }
+            };
+
+        Callable<String> req3 = new Callable<String>(){
+                @Override
+                public String call() throws Exception {
+
+                    Match km = sample.getMatchInfo(
+                        "match-GOE/AGI/04846-p42348-42349",
+                        "tokens",
+                        true,
+                        (ArrayList) null,
+                        (ArrayList) null,
+                        false,
+                        true,
+                        false
+                        );
+
+                    if (km.getStartPos() != 42348) {
+                        return "3-StartPos=" + km.getStartPos();
+                    }
+                    if (km.getEndPos() != 42349) {
+                        return "3-EndPos=" + km.getEndPos();
+                    }
+
+                    if (!km.getSnippetBrackets().equals(
+                            "... [[{corenlp/p:NN:{marmot/m:case:nom:{marmot/m:gender:fem:{marmot/m:number:sg:{marmot/p:NN:{opennlp/p:NN:{tt/l:Straße:{tt/p:NN:Straße}}}}}}}}]] ..."
+                            )) {
+                        return "3-Snippet=" + km.getSnippetBrackets();
+                    }
+                    return "ok";
+                }
+            };        
+        
+
+        // Create a pool with n threads
+        ExecutorService executor = Executors.newFixedThreadPool(16);
+
+        for (int i = 0; i < 200; i++) {
+            Future<String> res1 = executor.submit(req1);
+            Future<String> res2 = executor.submit(req2);
+            Future<String> res3 = executor.submit(req3);
+            
+            String value1 = res1.get();
+            String value2 = res2.get();
+            String value3 = res3.get();
+
+            if (!value1.equals("ok")) {
+                System.err.println("at "+ i);
+                assertEquals("ok", value1);
+                break;
+            }
+            if (!value2.equals("ok")) {
+                System.err.println("at "+ i);
+                assertEquals("ok", value2);
+                break;
+            }
+            if (!value3.equals("ok")) {
+                System.err.println("at "+ i);
+                assertEquals("ok", value3);
+                break;
+            }
+            System.err.println("Run "+i);
+        };
+        
+        executor.shutdown();
+    };
 };