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 }