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 }