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