Author: natalia
Date: Sun Aug 26 18:27:50 2007
New Revision: 569949

URL: http://svn.apache.org/viewvc?rev=569949&view=rev
Log:
Make search not synchronized

Modified:
    
xml/xindice/trunk/java/src/org/apache/xindice/core/indexer/LuceneIndexer.java

Modified: 
xml/xindice/trunk/java/src/org/apache/xindice/core/indexer/LuceneIndexer.java
URL: 
http://svn.apache.org/viewvc/xml/xindice/trunk/java/src/org/apache/xindice/core/indexer/LuceneIndexer.java?rev=569949&r1=569948&r2=569949&view=diff
==============================================================================
--- 
xml/xindice/trunk/java/src/org/apache/xindice/core/indexer/LuceneIndexer.java 
(original)
+++ 
xml/xindice/trunk/java/src/org/apache/xindice/core/indexer/LuceneIndexer.java 
Sun Aug 26 18:27:50 2007
@@ -99,10 +99,18 @@
 
     private File          idxFile;
     private IndexWriter   iw;
-    private IndexReader   ir;
-    private IndexSearcher is;
     private Analyzer      an;
 
+    /**
+     * Most recently opened searcher. The same Searcher instance is going to
+     * be used for all the searches unless index has changed and new Searcher
+     * is required to access the changes.
+     *
+     * Searcher cannot be closed if it is being used (if there is a query in
+     * progress or hits are iterated).
+     */
+    private Searcher searcher;
+
     private Configuration config;
     private Collection    collection;
 
@@ -113,6 +121,8 @@
     private int docsAdded;
     private int docsDeleted;
 
+    private final Object lock = new Object();
+
     private String defaultField = "";
 
     private void setFile(File f) {
@@ -214,7 +224,7 @@
         return config;
     }
 
-    public synchronized boolean exists() {
+    public boolean exists() {
         return IndexReader.indexExists(idxFile);
     }
 
@@ -246,22 +256,24 @@
         return false;
     }
 
-    public synchronized boolean open() throws DBException {
+    public boolean open() throws DBException {
         openWrite(false);
         return true;
     }
 
-    public synchronized boolean isOpened() {
-        return (null != iw) || (null != ir);
+    public boolean isOpened() {
+        return null != iw;
     }
 
     public synchronized boolean close() throws DBException {
         closeWrite();
-        closeRead();
+        if (searcher != null) {
+            searcher.close(true);
+        }
         return true;
     }
 
-    public synchronized boolean drop() throws DBException {
+    public boolean drop() throws DBException {
         try {
             if (IndexReader.indexExists(idxFile)) {
                 close();
@@ -271,7 +283,7 @@
             }
         } catch (IOException e) {
             throw new DBException(FaultCodes.IDX_CORRUPTED,
-                    "Failed to delete index " + name + ", collection " + 
collection.getCanonicalName(), e);
+                                  "Failed to delete index " + name + ", 
collection " + collection.getCanonicalName(), e);
         }
     }
 
@@ -287,50 +299,22 @@
         return an;
     }
 
-    private void openRead() throws DBException {
-        if (log.isTraceEnabled()) {
-            log.trace("Calling openRead()");
-        }
-
-        if (null == ir) {
-            closeWrite();
-            try {
-                ir = IndexReader.open(getFile());
-            } catch (IOException e) {
-                throw new DBException(FaultCodes.IDX_CORRUPTED,
-                        "Failed to open index " + name + ", collection " + 
collection.getCanonicalName(), e);
-            }
-        }
-    }
-
-    private void openSearch() throws DBException {
-        if (log.isTraceEnabled()) {
-            log.trace("Calling openSearch()");
-        }
-
-        if (null == is) {
-            openRead();
-            is = new IndexSearcher(ir);
-        }
-    }
-
     private void openWrite(boolean create) throws DBException {
         if (log.isTraceEnabled()) {
             log.trace("Calling openWrite(" + create + ")");
         }
 
-        if (null == iw) {
-            closeRead();
-            try {
+        try {
+            if (iw == null) {
                 iw = new IndexWriter(getFile(), getAnalyzer(), create);
-            } catch (IOException e) {
-                if (create) {
-                    throw new DBException(FaultCodes.IDX_CANNOT_CREATE,
-                            "Failed to cleate index " + name + ", collection " 
+ collection.getCanonicalName(), e);
-                } else {
-                    throw new DBException(FaultCodes.IDX_CORRUPTED,
-                            "Failed to open index " + name + ", collection " + 
collection.getCanonicalName(), e);
-                }
+            }
+        } catch (IOException e) {
+            if (create) {
+                throw new DBException(FaultCodes.IDX_CANNOT_CREATE,
+                                      "Failed to cleate index " + name + ", 
collection " + collection.getCanonicalName(), e);
+            } else {
+                throw new DBException(FaultCodes.IDX_CORRUPTED,
+                                      "Failed to open index " + name + ", 
collection " + collection.getCanonicalName(), e);
             }
         }
     }
@@ -341,63 +325,14 @@
         }
     }
 
-    private void assertRead() throws DBException {
-        assertOpen();
-        openRead();
-    }
-
-    private void assertWrite() throws DBException {
-        assertOpen();
-        openWrite(false);
-    }
-
-    private void closeRead() throws DBException {
-        if (null != ir) {
-            closeSearch();
-            try {
-                ir.close();
-                ir = null;
-            } catch (IOException e) {
-                throw new DBException(FaultCodes.IDX_CORRUPTED,
-                        "Failed to close reader for index " + name + ", 
collection " + collection.getCanonicalName(), e);
-            }
-        }
-    }
-
-    private void closeSearch() throws DBException {
-        if (null != is) {
-            try {
-                is.close();
-                is = null;
-            } catch (IOException e) {
-                throw new DBException(FaultCodes.IDX_CORRUPTED,
-                        "Failed to close searcher for index " + name + ", 
collection " + collection.getCanonicalName(), e);
-            }
-        }
-    }
-
     private void closeWrite() throws DBException {
         if (null != iw) {
             try {
-                int nDocs = iw.docCount();
-                /* Fairly arbitrary rules for triggering index optimisation. 
Need to
-                 * play with these.
-                 */
-                if (docsAdded > nDocs / 10 || docsAdded > 50 || docsDeleted > 
10) {
-                    if (log.isDebugEnabled()) {
-                        log.debug("Optimizing text index for " + 
collection.getCanonicalName() + "...");
-                    }
-
-                    iw.optimize();
-                    docsAdded = 0;
-                    docsDeleted = 0;
-                }
-
                 iw.close();
                 iw = null;
             } catch (IOException e) {
                 throw new DBException(FaultCodes.IDX_CORRUPTED,
-                        "Failed to close writer for index " + name + ", 
collection " + collection.getCanonicalName(), e);
+                                      "Failed to close writer for index " + 
name + ", collection " + collection.getCanonicalName(), e);
             }
         }
     }
@@ -414,15 +349,32 @@
         return f.delete();
     }
 
-    public synchronized void flush() throws DBException {
+    public void flush() throws DBException {
         try {
             assertOpen();
             if (iw != null) {
                 iw.flush();
+
+                int nDocs = iw.docCount();
+                /* Fairly arbitrary rules for triggering index optimisation. 
Need to
+                 * play with these.
+                 */
+                synchronized(lock) {
+                    if (docsAdded > nDocs / 10 || docsAdded > 50 || 
docsDeleted > 10) {
+                        if (log.isDebugEnabled()) {
+                            log.debug("Optimizing text index for " + 
collection.getCanonicalName() + "...");
+                        }
+
+                        iw.optimize();
+                        docsAdded = 0;
+                        docsDeleted = 0;
+                    }
+                }
+
             }
         } catch (IOException e) {
             throw new DBException(FaultCodes.IDX_CORRUPTED,
-                    "Could not force unwritten data to disk for index " + name 
+ ", collection " + collection.getCanonicalName(), e);
+                                  "Could not force unwritten data to disk for 
index " + name + ", collection " + collection.getCanonicalName(), e);
         }
     }
 
@@ -437,29 +389,33 @@
         return new BasicIndexerEventHandler() {
             Document doc;
 
-            public synchronized void onDocumentAdded(Key key) throws 
DBException {
+            public void onDocumentAdded(Key key) throws DBException {
                 if (doc != null) {
-                    assertWrite();
+                    assertOpen();
 
                     try {
                         iw.addDocument(doc);
-                        docsAdded++;
+                        synchronized(lock) {
+                            docsAdded++;
+                        }
                     } catch (IOException e) {
                         throw new DBException(FaultCodes.IDX_CORRUPTED,
-                            "Failed to add document to the index " + name + ", 
collection " + collection.getCanonicalName(), e);
+                                              "Failed to add document to the 
index " + name + ", collection " + collection.getCanonicalName(), e);
                     }
                 }
             }
 
-            public synchronized void onDocumentDeleted(Key key) throws 
DBException {
-                assertRead();
+            public void onDocumentDeleted(Key key) throws DBException {
+                assertOpen();
 
                 try {
-                    ir.deleteDocuments(new Term(KEYNAME, key.toString()));
-                    docsDeleted++;
+                    iw.deleteDocuments(new Term(KEYNAME, key.toString()));
+                    synchronized(lock) {
+                        docsDeleted++;
+                    }
                 } catch (IOException e) {
                     throw new DBException(FaultCodes.IDX_CORRUPTED,
-                            "Failed to delete document from the index " + name 
+ ", collection " + collection.getCanonicalName(), e);
+                                          "Failed to delete document from the 
index " + name + ", collection " + collection.getCanonicalName(), e);
                 }
             }
 
@@ -497,11 +453,12 @@
      * @return The resulting matches
      * @throws DBException if IOException prevented indexer from executing the 
query.
      */
-    public synchronized IndexMatch[] queryMatches(Query query) throws 
DBException {
+    public IndexMatch[] queryMatches(Query query) throws DBException {
         ArrayList matches = new ArrayList();
-        openSearch();
+        Searcher searcher = getSearcher();
+
         try {
-            Hits hits = is.search(query);
+            Hits hits = searcher.search(query);
             for (Iterator i = hits.iterator(); i.hasNext(); ) {
                 Hit hit = (Hit) i.next();
                 Key key = new 
Key(hit.getDocument().getField(KEYNAME).stringValue());
@@ -509,8 +466,113 @@
             }
         } catch (IOException e) {
             throw new ProcessingException("Failed to process a query", e);
+        } finally {
+            searcher.free();
         }
 
         return (IndexMatch[]) matches.toArray(EMPTY_MATCHES);
     }
-}
+
+    /**
+     * getSearcher returns Searcher that uses current version of the index.
+     * If index has been modified since last time searcher was requested
+     * this method will create new Searcher instance, otherwise it will
+     * return Searcher instance it created previously.
+     *
+     * @return current Searcher
+     * @throws DBException
+     */
+    private synchronized Searcher getSearcher() throws DBException {
+
+        if (searcher != null && !searcher.isCurrent()) {
+            searcher.close(false);
+            searcher = null;
+        }
+
+        if (searcher == null) {
+            searcher = new Searcher();
+        } else {
+            searcher.incRef();
+        }
+
+        return searcher;
+    }
+
+    private class Searcher {
+        private IndexReader ir;
+        private IndexSearcher is;
+
+        // number of searches in progress using that searcher
+        private int ref = 1;
+
+        public Searcher() throws DBException {
+            try {
+                ir = IndexReader.open(getFile());
+                is = new IndexSearcher(ir);
+            } catch (IOException e) {
+                throw new DBException(FaultCodes.IDX_CORRUPTED,
+                                      "Failed to open access " + name + ", 
collection " + collection.getCanonicalName(), e);
+            }
+        }
+
+        public boolean isCurrent() throws DBException {
+            try {
+                return ir.isCurrent();
+            } catch (IOException e) {
+                throw new DBException(FaultCodes.IDX_CORRUPTED,
+                                      "Failed to access index " + name + ", 
collection " + collection.getCanonicalName(), e);
+            }
+        }
+
+        public void incRef() {
+            ref++;
+        }
+
+        /**
+         * This method must be called after executing text query to cleanup
+         * resources that are not in use anymore. It decrements number of
+         * searches referencing this searcher and then attempts to close it
+         * unless it is the most recently opened searcher. If there were no
+         * searchers opened after this one, the searcher will be kept open
+         * for future use, even if it is not used at the moment.
+         *
+         * @throws DBException if there was IOException
+         */
+        public void free() throws DBException {
+            synchronized (LuceneIndexer.this) {
+                ref--;
+
+                if (searcher != this) {
+                    close(false);
+                }
+            }
+        }
+
+        /**
+         * Closes the searcher if it is not used in any search.
+         *
+         * @param force true if searcher has to be closed even if it is used
+         * @throws DBException if there was IOException
+         */
+        public void close(boolean force) throws DBException {
+            try {
+                if (ref == 0 || force) {
+                    is.close();
+                    ir.close();
+                }
+            } catch (IOException e) {
+                throw new DBException(FaultCodes.IDX_CORRUPTED,
+                                      "Failed to access index " + name + ", 
collection " + collection.getCanonicalName(), e);
+            }
+        }
+
+        public Hits search(Query query) throws DBException {
+            try {
+                return is.search(query);
+            } catch (IOException e) {
+                throw new DBException(FaultCodes.IDX_CORRUPTED,
+                                      "Failed to access index " + name + ", 
collection " + collection.getCanonicalName(), e);
+            }
+        }
+    }
+}
\ No newline at end of file


Reply via email to