View Javadoc

1   package org.apache.lucene.search;
2   
3   /**
4    * Copyright 2004 The Apache Software Foundation
5    *
6    * Licensed under the Apache License, Version 2.0 (the "License");
7    * you may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  import org.apache.lucene.index.IndexReader;
20  import org.apache.lucene.store.Directory;
21  
22  import java.io.IOException;
23  import java.util.logging.Logger;
24  
25  /**
26   * Implements search over a single IndexReader, but remains open even if close() is called. This way it can be shared by
27   * multiple objects that need to search the index without being aware of the keep-the-index-open-until-it-changes logic.<br>
28   * <br>
29   * Basic use (works, but can slow all searcher while opening a new Searcher instance:
30   * <pre>
31   * class SearcherFactory {
32   *     SearcherFactory(Directory directory){
33   *         currentSearcher=new DelayCloseIndexSearcher(directory);
34   *     }
35   * <p/>
36   *     public synchronized IndexSearcher createSearcher() {
37   *         if (!currentSearcher.isCurrent()) {
38   *             currentSearcher.closeWhenDone();
39   *             currentSearcher=new DelayCloseIndexSearcher(directory);
40   *         }
41   * <p/>
42   *         currentSearcher.open();
43   *         return currentSearcher;
44   *     }
45   * <p/>
46   *     void synchronized close() {
47   *         currentSearcher.closeWhenDone();
48   *     }
49   * <p/>
50   *     private final Directory directory;
51   * <p/>
52   *     private DelayCloseIndexSearcher currentSearcher;
53   * }
54   * </pre>
55   * <p/>
56   * Objects that need to search the index do the following:<br>
57   * <pre>
58   * //searcherFactory is created once at startup
59   * <p/>
60   * IndexSearcher indexSearcher=searcherFactory.createSearcher();
61   * Hits hist=indexSearcher.search(query, filter, sort);
62   * ... // handle results
63   * indexSearcher.close();
64   * <p/>
65   * // when the application shuts down
66   * searcherFactory.close();
67   * </pre>
68   *
69   * @author Luc Vanlerberghe
70   */
71  public class DelayCloseIndexSearcher extends IndexSearcher
72  {
73      public DelayCloseIndexSearcher(String string) throws IOException
74      {
75          super(string);
76      }
77  
78      /**
79       * Creates a DelayCloseIndexSearcher searching the index in the provided directory..
80       *
81       * @param directory containing the index.
82       * @throws IOException if an I/O error occurs.
83       */
84      public DelayCloseIndexSearcher(Directory directory) throws IOException
85      {
86          super(directory);
87          this.usageCount = 0;
88          this.shouldCloseWhenDone = false;
89  
90          logger.finer("<init>");
91      }
92  
93      /**
94       * Creates a DelayCloseIndexSearcher searching the index using the provided Reader
95       *
96       * @param indexReader Reader to use for opening the searcher
97       * @throws IOException if an I/O error occurs
98       */
99      public DelayCloseIndexSearcher(IndexReader indexReader) throws IOException
100     {
101         super(indexReader);
102         this.usageCount = 0;
103         this.shouldCloseWhenDone = false;
104 
105         logger.finer("<init>");
106     }
107 
108     /**
109      * @param indexReader The indexReader to use for the searcher
110      * @param directory   No longer in use
111      * @throws IOException If there's a problem opening the underlying searcher
112      * @deprecated Use {@link #DelayCloseIndexSearcher(org.apache.lucene.index.IndexReader)} instead
113      */
114     public DelayCloseIndexSearcher(IndexReader indexReader, Directory directory) throws IOException
115     {
116         this(indexReader);
117     }
118 
119     /**
120      * This should be called whenever this instances is passed as a new IndexSearcher.
121      * Only when each call to open() is balanced with a call to close(), and closeWhenDone has been called,
122      * will super.close() be called.
123      */
124     public void open()
125     {
126         synchronized (this)
127         {
128             if (shouldCloseWhenDone)
129             {
130                 throw new IllegalStateException("closeWhenDone() already called");
131             }
132 
133             ++usageCount;
134         }
135 
136         logger.finest("open()");
137     }
138 
139     /**
140      * Signals that this instance may really close when all open() calls have been balanced with a call to close().
141      *
142      * @throws IOException if an I/O error occurs.
143      */
144     public void closeWhenDone() throws IOException
145     {
146         logger.finer("closeWhenDone()");
147 
148         boolean doClose; // Keep the actual super.close() out of the synchronized block
149 
150         synchronized (this)
151         {
152             if (shouldCloseWhenDone)
153             {
154                 throw new IllegalStateException("closeWhenDone() already called");
155             }
156 
157             shouldCloseWhenDone = true;
158 
159             doClose = (usageCount == 0);
160         }
161 
162         if (doClose)
163         {
164             logger.finer("super.close()");
165 
166             super.close();
167         }
168     }
169 
170     /**
171      * Returns whether the underlying IndexSearcher instance still works on a current version of the index.
172      * If it returns false, closeWhenDone() should be called and another instance created to handle further search requests.
173      *
174      * @return whether the underlying IndexSearcher instance still works on a current version of the index
175      * @throws IOException if an I/O error occurs.
176      */
177     public boolean isCurrent() throws IOException
178     {
179         return getIndexReader().isCurrent();
180     }
181 
182     /**
183      * Returns wether the underlying IndexSearcher has really been closed.
184      * If it is true, this instance can no longer be used.
185      *
186      * @return whether the underlying IndexSearcher has really been closed.
187      */
188     public boolean isClosed()
189     {
190         synchronized (this)
191         {
192             return shouldCloseWhenDone && usageCount == 0;
193         }
194     }
195 
196     //
197     // IndexSearcher overrides
198     //
199 
200     /**
201      * Should be called once for every call to open().
202      * If the usageCount drops to zero and closeWhenDone() was called, super.close() will be called.
203      *
204      * @throws IOException if an I/O error occurs.
205      */
206     public void close() throws IOException
207     {
208         logger.finest("close()");
209 
210         boolean doClose; // Keep the actual super.close() out of the synchronized block
211 
212         synchronized (this)
213         {
214             if (usageCount <= 0)
215             {
216                 throw new IllegalStateException("usageCount<=0");
217             }
218 
219             doClose = (--usageCount == 0 && shouldCloseWhenDone);
220         }
221 
222         if (doClose)
223         {
224             logger.finer("super.close()");
225 
226             super.close();
227         }
228     }
229 
230     //
231     // private members
232     //
233 
234     /**
235      * The number of open() calls minus the number of close() calls.
236      * If this drops to zero and closeWhenDone() is true, super.close() is called.
237      */
238     private int usageCount;
239 
240     /**
241      * Indicates if closeWhenDone() was called.
242      * If true and usageCount is zero, super.close() is called.
243      */
244     private boolean shouldCloseWhenDone;
245 
246     /**
247      * The Logger instance for this Class.
248      */
249     private static final Logger logger = Logger.getLogger(DelayCloseIndexSearcher.class.getName());
250 }