KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > cocoon > components > search > SimpleLuceneCocoonSearcherImpl


1 /*
2  * Copyright 1999-2004 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 package org.apache.cocoon.components.search;
17 import org.apache.avalon.excalibur.pool.Recyclable;
18 import org.apache.avalon.framework.activity.Disposable;
19 import org.apache.avalon.framework.configuration.Configurable;
20 import org.apache.avalon.framework.configuration.Configuration;
21 import org.apache.avalon.framework.configuration.ConfigurationException;
22 import org.apache.avalon.framework.logger.AbstractLogEnabled;
23 import org.apache.avalon.framework.service.ServiceException;
24 import org.apache.avalon.framework.service.ServiceManager;
25 import org.apache.avalon.framework.service.Serviceable;
26 import org.apache.cocoon.ProcessingException;
27 import org.apache.cocoon.util.ClassUtils;
28 import org.apache.lucene.analysis.Analyzer;
29 import org.apache.lucene.index.IndexReader;
30 import org.apache.lucene.queryParser.ParseException;
31 import org.apache.lucene.queryParser.QueryParser;
32 import org.apache.lucene.search.Hits;
33 import org.apache.lucene.search.IndexSearcher;
34 import org.apache.lucene.search.Query;
35 import org.apache.lucene.store.Directory;
36 import org.apache.lucene.store.FSDirectory;
37
38 import java.io.File JavaDoc;
39 import java.io.IOException JavaDoc;
40
41 /**
42  * This class provides searching via lucene.
43  *
44  * <p>
45  * In order to do searching you need a lucene Directory where the lucene generated
46  * index resides.
47  * Moreover you must know the lucene Analyzer which has been used for
48  * indexing, and which will be used for searching.
49  * </p>
50  * <p>
51  * Knowing this you can may start searching having a query which is parsable
52  * by an QueryParser, and having the name of the default field to use in
53  * searching.
54  * </p>
55  * <p>
56  * This class returns an Hit object as its search result.
57  * </p>
58  *
59  * @author <a HREF="mailto:berni_huber@a1.net">Bernhard Huber</a>
60  * @version CVS $Id: SimpleLuceneCocoonSearcherImpl.java 47285 2004-09-27 12:52:44Z cziegeler $
61  */

62 public class SimpleLuceneCocoonSearcherImpl extends AbstractLogEnabled
63          implements LuceneCocoonSearcher, Configurable, Serviceable, Disposable, Recyclable
64 {
65
66     /**
67      * Configuration element name of lucene's Analyzer class.
68      * <p>
69      * Its value is
70      * <code>analyzer-classname</code>.
71      * </p>
72      *
73      */

74     protected final static String JavaDoc ANALYZER_CLASSNAME_CONFIG = "analyzer-classname";
75     /**
76      * Configuration element default value of lucene's Analyzer class.
77      * <p>
78      * Its value is,
79      * <code>org.apache.lucene.analysis.standard.StandardAnalyzer</code>.
80      * </p>
81      *
82      */

83     protected final static String JavaDoc ANALYZER_CLASSNAME_DEFAULT = "org.apache.lucene.analysis.standard.StandardAnalyzer";
84
85     /**
86      * Configuration element name of default search field.
87      * <p>
88      * Its value is
89      * <code>default-seach-field</code>.
90      * </p>
91      *
92      */

93     protected final static String JavaDoc DEFAULT_SEARCH_FIELD_CONFIG = "default-search-field";
94     /**
95      * Configuration element default value of lucene's default search field.
96      * <p>
97      * Its value is <code>body</code>.
98      * </p>
99      *
100      */

101     protected final static String JavaDoc DEFAULT_SEARCH_FIELD_DEFAULT = "body";
102
103     /**
104      * Configuration element name of default-query.
105      * <p>
106      * Its value is
107      * <code>default-query</code>.
108      * </p>
109      *
110      */

111     protected final static String JavaDoc DEFAULT_QUERY_CONFIG = "default-query";
112     /**
113      * Configuration element default value of default-query.
114      * <p>
115      * Its value is <code>null</code>.
116      * </p>
117      *
118      */

119     protected final static String JavaDoc DEFAULT_QUERY_DEFAULT = null;
120
121     /**
122      * Configuration element name of query parser class name.
123      * <p>
124      * Its value is
125      * <code>queryparser-classname</code>.
126      * </p>
127      *
128      */

129     protected final static String JavaDoc QUERYPARSER_CLASSNAME_CONFIG = "queryparser-classname";
130     /**
131      * Configuration element default value of queryparser-classname.
132      * <p>
133      * Its value is
134      * <code>org.apache.lucene.queryParser.QueryParser</code>.
135      * </p>
136      *
137      */

138     protected final static String JavaDoc QUERYPARSER_CLASSNAME_DEFAULT = "org.apache.lucene.queryParser.QueryParser";
139
140     /**
141      * Configuration element name of lucene's default filesystem default
142      * directory.
143      * <p>
144      * Its value is <code>directory</code>.
145      * </p>
146      *
147      */

148     protected final static String JavaDoc DIRECTORY_CONFIG = "directory";
149     /**
150      * Configuration element default value of filesystem default directory.
151      * <p>
152      * Its value is <code>null</code>.
153      * </p>
154      *
155      */

156     protected final static String JavaDoc DIRECTORY_DEFAULT = null;
157
158     /**
159      * The service manager instance
160      *
161      */

162     protected ServiceManager manager = null;
163
164     private String JavaDoc analyzerClassnameDefault = ANALYZER_CLASSNAME_DEFAULT;
165     private String JavaDoc defaultSearchFieldDefault = DEFAULT_SEARCH_FIELD_DEFAULT;
166     private String JavaDoc defaultQueryDefault = DEFAULT_QUERY_DEFAULT;
167 // private String queryparserClassnameDefault = QUERYPARSER_CLASSNAME_DEFAULT;
168
private String JavaDoc directoryDefault = DIRECTORY_DEFAULT;
169
170     /**
171      * The lucene analyzer used for searching
172      */

173     private Analyzer analyzer;
174     /**
175      * The lucene directory used for searching
176      */

177     private Directory directory;
178     /**
179      * The lucene index searcher used for searching
180      */

181     private IndexSearcher indexSearcher;
182
183     /**
184      * A lucene index reader cache to maximize sharing of
185      * lucene index readers
186      */

187     private IndexReaderCache indexReaderCache;
188
189     /**
190      * set an analyzer, overriding the analyzerClassnameDefault.
191      *
192      * @param analyzer The new analyzer value
193      */

194     public void setAnalyzer(Analyzer analyzer) {
195         this.analyzer = analyzer;
196     }
197
198     /**
199      * get the analyzer.
200      *
201      */

202     public Analyzer getAnalyzer() {
203         return this.analyzer;
204     }
205
206     /**
207      *Sets the directory attribute of the SimpleLuceneCocoonSearcherImpl object
208      *
209      * @param directory The new directory value
210      */

211     public void setDirectory(Directory directory) {
212         this.directory = directory;
213         if (indexReaderCache != null) {
214             indexReaderCache.close();
215             indexReaderCache = null;
216         }
217     }
218
219
220     /**
221      * Get an IndexReader.
222      * <p>
223      * As an IndexReader might be cached, it is check if the indexReader is
224      * still valid.
225      * </p>
226      *
227      * @return IndexReader an up to date indexReader
228      * @exception IOException is thrown iff it's impossible to create
229      * an IndexReader
230      */

231     public IndexReader getReader() throws IOException JavaDoc {
232         if (indexReaderCache == null) {
233             indexReaderCache = new IndexReaderCache();
234         }
235         return indexReaderCache.getIndexReader(directory);
236     }
237
238
239     /**
240      * configure this component
241      *
242      * @param conf of this component
243      * @exception ConfigurationException is thrown iff configuration of
244      * this component fails
245      */

246     public void configure(Configuration conf) throws ConfigurationException {
247         Configuration child;
248         String JavaDoc value;
249
250         child = conf.getChild(ANALYZER_CLASSNAME_CONFIG, false);
251         if (child != null) {
252             // fix Bugzilla Bug 25277, use child.getValue
253
// and in all following blocks
254
value = child.getValue(ANALYZER_CLASSNAME_DEFAULT);
255             if (value != null) {
256                 analyzerClassnameDefault = value;
257                 try {
258                     analyzer = (Analyzer) ClassUtils.newInstance(analyzerClassnameDefault);
259                 } catch (Exception JavaDoc e) {
260                     throw new ConfigurationException("Cannot create analyzer of class " +
261                             analyzerClassnameDefault, e);
262                 }
263             }
264         }
265
266         child = conf.getChild(DEFAULT_SEARCH_FIELD_CONFIG, false);
267         if (child != null) {
268             value = child.getValue(DEFAULT_SEARCH_FIELD_DEFAULT);
269             if (value != null) {
270                 defaultSearchFieldDefault = value;
271             }
272         }
273
274         child = conf.getChild(DEFAULT_QUERY_CONFIG, false);
275         if (child != null) {
276             value = child.getValue(DEFAULT_QUERY_DEFAULT);
277             if (value != null) {
278                 defaultQueryDefault = value;
279             }
280         }
281 /*
282         child = conf.getChild(QUERYPARSER_CLASSNAME_CONFIG, false);
283         if (child != null) {
284             value = child.getValue(QUERYPARSER_CLASSNAME_DEFAULT);
285             if (value != null) {
286                 queryparserClassnameDefault = value;
287             }
288         }
289 */

290         child = conf.getChild(DIRECTORY_CONFIG, false);
291         if (child != null) {
292             value = child.getValue(DIRECTORY_DEFAULT);
293             if (value != null) {
294                 directoryDefault = value;
295                 try {
296                     setDirectory(FSDirectory.getDirectory(new File JavaDoc(directoryDefault), false));
297                 } catch (IOException JavaDoc ioe) {
298                     throw new ConfigurationException("Cannot set index directory " + directoryDefault, ioe);
299                 }
300             }
301         }
302     }
303
304
305     /**
306      * Set the current <code>ServiceManager</code> instance used by this
307      * <code>Serviceable</code>.
308      *
309      * @param manager manager of this component
310      * @exception ServiceException is never thrown
311      */

312     public void service(ServiceManager manager) throws ServiceException {
313         this.manager = manager;
314     }
315
316
317     /**
318      * Dispose this component, releasing IndexSearcher, and IndexReaderCache.
319      */

320     public void dispose() {
321         releaseIndexSearcher();
322         releaseIndexReaderCache();
323     }
324
325
326     /**
327      * Recycle this component, releasing IndexSearcher, and IndexReaderCache.
328      */

329     public void recycle() {
330         releaseIndexSearcher();
331         releaseIndexReaderCache();
332     }
333
334
335     /**
336      * Search lucene index.
337      *
338      * @param query_string is lucene's query string
339      * @param default_field the lucene field to run the query
340      * @return lucene Hits
341      * @exception ProcessingException iff its not possible do run the query
342      */

343     public Hits search(String JavaDoc query_string, String JavaDoc default_field) throws ProcessingException {
344         Hits hits = null;
345
346         if (query_string == null) {
347             query_string = defaultQueryDefault;
348         }
349         if (default_field == null) {
350             default_field = defaultSearchFieldDefault;
351         }
352
353         try {
354             Query query = QueryParser.parse(query_string, default_field, analyzer);
355
356             // release index searcher for each new search
357
releaseIndexSearcher();
358
359             IndexSearcher indexSearcher = new IndexSearcher(getReader());
360             hits = indexSearcher.search(query);
361             // do not close indexSearcher now, as using hits needs an
362
// opened indexSearcher indexSearcher.close();
363
} catch (ParseException pe) {
364             throw new ProcessingException("Cannot parse query " + query_string, pe);
365         } catch (IOException JavaDoc ioe) {
366             throw new ProcessingException("Cannot access hits", ioe);
367         }
368         return hits;
369     }
370
371     /**
372      * Search lucene index.
373      * This method is designed to be used by other components, or Flowscripts
374      *
375      * @param query the lucene Query
376      * @return lucene Hits
377      * @exception ProcessingException if its not possible do run the query
378      */

379     public Hits search(Query query) throws ProcessingException {
380         Hits hits = null;
381         try {
382             // release index searcher for each new search
383
releaseIndexSearcher();
384
385             IndexSearcher indexSearcher = new IndexSearcher(getReader());
386             hits = indexSearcher.search(query);
387             // do not close indexSearcher now, as using hits needs an
388
// opened indexSearcher indexSearcher.close();
389
} catch (IOException JavaDoc ioe) {
390             throw new ProcessingException("Cannot access hits", ioe);
391         }
392         return hits;
393     }
394
395     /**
396      * Release the index searcher.
397      *
398      */

399     private void releaseIndexSearcher() {
400         if (indexSearcher != null) {
401             try {
402                 indexSearcher.close();
403             } catch (IOException JavaDoc ioe) {
404                 // ignore it
405
}
406             indexSearcher = null;
407         }
408     }
409
410
411     /**
412      * Release the IndexReaderCache
413      *
414      */

415     private void releaseIndexReaderCache() {
416         if (indexReaderCache != null) {
417             indexReaderCache = null;
418         }
419     }
420
421
422     /**
423      * This class should help to minimise usage of IndexReaders.
424      *
425      */

426     static class IndexReaderCache
427     {
428         private IndexReader indexReader;
429         private long lastModified;
430
431
432         /**
433          * Create an IndexReaderCache.
434          *
435          */

436         IndexReaderCache() { }
437
438
439         /**
440          * return cached IndexReader object.
441          *
442          * @param directory lucene index directory
443          * @return The indexReader value
444          */

445         public IndexReader getIndexReader(Directory directory) throws IOException JavaDoc {
446             if (indexReader == null) {
447                 createIndexReader(directory);
448             } else {
449                 if (!indexReaderIsValid(directory)) {
450                     createIndexReader(directory);
451                 }
452             }
453             return indexReader;
454         }
455
456
457         /**
458          * Close an opened lucene IndexReader
459          *
460          */

461         public void close() {
462             if (indexReader != null) {
463                 try {
464                     indexReader.close();
465                 } catch (IOException JavaDoc ioe) {
466                     // ignore it
467
}
468                 indexReader = null;
469             }
470         }
471
472
473         /**
474          * Check if cached IndexReader is up to date.
475          *
476          * @param directory lucene index directory
477          * @return boolean return true if there is a cached IndexReader object,
478          * and its lastModified date is greater equal than the lastModified date
479          * of its lucene Directory.
480          * @exception IOException Description of Exception
481          */

482         public boolean indexReaderIsValid(Directory directory) throws IOException JavaDoc {
483             return indexReader != null &&
484             IndexReader.getCurrentVersion(directory) == lastModified;
485         }
486
487
488         /**
489          * Release all resources, most notably the lucene IndexReader.
490          *
491          * @exception Throwable Description of Exception
492          */

493         protected void finalize() throws Throwable JavaDoc {
494             close();
495         }
496
497
498         /**
499          * Create unconditionally a lucene IndexReader.
500          *
501          * @param directory lucene index directory
502          * @exception IOException Description of Exception
503          */

504         private void createIndexReader(Directory directory) throws IOException JavaDoc {
505             close();
506             indexReader = IndexReader.open(directory);
507             lastModified = IndexReader.getCurrentVersion(directory);
508         }
509     }
510 }
511
512
Popular Tags