]> source.dussan.org Git - archiva.git/blob
a21c4fa6b7831ebb89a4add446f2d49d04d5b795
[archiva.git] /
1 package org.apache.maven.archiva.indexer.search;
2
3 /*
4  * Licensed to the Apache Software Foundation (ASF) under one
5  * or more contributor license agreements.  See the NOTICE file
6  * distributed with this work for additional information
7  * regarding copyright ownership.  The ASF licenses this file
8  * to you under the Apache License, Version 2.0 (the
9  * "License"); you may not use this file except in compliance
10  * with the License.  You may obtain a copy of the License at
11  *
12  *  http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing,
15  * software distributed under the License is distributed on an
16  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17  * KIND, either express or implied.  See the License for the
18  * specific language governing permissions and limitations
19  * under the License.
20  */
21
22 import java.io.IOException;
23 import java.util.ArrayList;
24 import java.util.List;
25
26 import org.apache.lucene.document.Document;
27 import org.apache.lucene.index.Term;
28 import org.apache.lucene.queryParser.MultiFieldQueryParser;
29 import org.apache.lucene.queryParser.ParseException;
30 import org.apache.lucene.queryParser.QueryParser;
31 import org.apache.lucene.search.BooleanClause;
32 import org.apache.lucene.search.BooleanQuery;
33 import org.apache.lucene.search.Filter;
34 import org.apache.lucene.search.Hits;
35 import org.apache.lucene.search.MultiSearcher;
36 import org.apache.lucene.search.Query;
37 import org.apache.lucene.search.QueryWrapperFilter;
38 import org.apache.lucene.search.Searchable;
39 import org.apache.lucene.search.TermQuery;
40 import org.apache.maven.archiva.configuration.ArchivaConfiguration;
41 import org.apache.maven.archiva.configuration.ConfigurationNames;
42 import org.apache.maven.archiva.configuration.ManagedRepositoryConfiguration;
43 import org.apache.maven.archiva.indexer.ArtifactKeys;
44 import org.apache.maven.archiva.indexer.RepositoryContentIndex;
45 import org.apache.maven.archiva.indexer.RepositoryContentIndexFactory;
46 import org.apache.maven.archiva.indexer.RepositoryIndexException;
47 import org.apache.maven.archiva.indexer.RepositoryIndexSearchException;
48 import org.apache.maven.archiva.indexer.bytecode.BytecodeHandlers;
49 import org.apache.maven.archiva.indexer.bytecode.BytecodeKeys;
50 import org.apache.maven.archiva.indexer.filecontent.FileContentHandlers;
51 import org.apache.maven.archiva.indexer.hashcodes.HashcodesHandlers;
52 import org.apache.maven.archiva.indexer.hashcodes.HashcodesKeys;
53 import org.apache.maven.archiva.indexer.lucene.LuceneEntryConverter;
54 import org.apache.maven.archiva.indexer.lucene.LuceneQuery;
55 import org.apache.maven.archiva.indexer.lucene.LuceneRepositoryContentRecord;
56 import org.codehaus.plexus.personality.plexus.lifecycle.phase.Initializable;
57 import org.codehaus.plexus.personality.plexus.lifecycle.phase.InitializationException;
58 import org.codehaus.plexus.registry.Registry;
59 import org.codehaus.plexus.registry.RegistryListener;
60 import org.slf4j.Logger;
61 import org.slf4j.LoggerFactory;
62
63 /**
64  * DefaultCrossRepositorySearch
65  * 
66  * @author <a href="mailto:joakime@apache.org">Joakim Erdfelt</a>
67  * @version $Id$
68  * @plexus.component role="org.apache.maven.archiva.indexer.search.CrossRepositorySearch" role-hint="default"
69  */
70 public class DefaultCrossRepositorySearch
71     implements CrossRepositorySearch, RegistryListener, Initializable
72 {
73     private Logger log = LoggerFactory.getLogger( DefaultCrossRepositorySearch.class );
74
75     /**
76      * @plexus.requirement role-hint="lucene"
77      */
78     private RepositoryContentIndexFactory indexFactory;
79
80     /**
81      * @plexus.requirement
82      */
83     private ArchivaConfiguration configuration;
84
85     private List<ManagedRepositoryConfiguration> localIndexedRepositories = new ArrayList<ManagedRepositoryConfiguration>();
86
87     public SearchResults executeFilteredSearch( String principal, List<String> selectedRepos, String groupId,
88                                                 String artifactId, String version, String className,
89                                                 SearchResultLimits limits )
90     {
91         List<RepositoryContentIndex> indexes = getBytecodeIndexes( principal, selectedRepos );
92         SearchResults results = new SearchResults();
93         BooleanQuery booleanQuery = new BooleanQuery();
94
95         if ( groupId != null && groupId.length() > 0 )
96         {
97             parseAndAdd( booleanQuery, ArtifactKeys.GROUPID, groupId, "\\.|-" );
98         }
99
100         if ( artifactId != null && artifactId.length() > 0 )
101         {
102             parseAndAdd( booleanQuery, ArtifactKeys.ARTIFACTID, artifactId, "\\.|-" );
103         }
104
105         if ( version != null && version.length() > 0 )
106         {
107             parseAndAdd( booleanQuery, ArtifactKeys.VERSION, version, "\\.|-" );
108         }
109
110         if ( className != null && className.length() > 0 )
111         {
112
113             try
114             {
115                 QueryParser parser =
116                     new MultiFieldQueryParser( new String[] { BytecodeKeys.CLASSES, BytecodeKeys.FILES,
117                         BytecodeKeys.METHODS }, new BytecodeHandlers().getAnalyzer() );
118                 booleanQuery.add( parser.parse( className ), BooleanClause.Occur.MUST );
119             }
120             catch ( ParseException e )
121             {
122
123             }
124         }
125
126         LuceneQuery query = new LuceneQuery( booleanQuery );
127         results = searchAll( query, limits, indexes, null );
128         results.getRepositories().add( this.localIndexedRepositories );
129
130         return results;
131     }
132
133     public SearchResults searchForChecksum( String principal, List<String> selectedRepos, String checksum,
134                                             SearchResultLimits limits )
135     {
136         List<RepositoryContentIndex> indexes = getHashcodeIndexes( principal, selectedRepos );
137
138         try
139         {
140             QueryParser parser = new MultiFieldQueryParser( new String[]{HashcodesKeys.MD5, HashcodesKeys.SHA1},
141                                            new HashcodesHandlers().getAnalyzer() );
142             LuceneQuery query = new LuceneQuery( parser.parse( checksum ) );
143             SearchResults results = searchAll( query, limits, indexes, null );
144             results.getRepositories().addAll( this.localIndexedRepositories );
145
146             return results;
147         }
148         catch ( ParseException e )
149         {
150             log.warn( "Unable to parse query [" + checksum + "]: " + e.getMessage(), e );
151         }
152
153         // empty results.
154         return new SearchResults();
155     }
156
157     public SearchResults searchForBytecode( String principal, List<String> selectedRepos, String term, SearchResultLimits limits )
158     {
159         List<RepositoryContentIndex> indexes = getBytecodeIndexes( principal, selectedRepos );
160
161         try
162         {
163             QueryParser parser = new BytecodeHandlers().getQueryParser();
164             LuceneQuery query = new LuceneQuery( parser.parse( term ) );
165             SearchResults results = searchAll( query, limits, indexes, null );
166             results.getRepositories().addAll( this.localIndexedRepositories );
167
168             return results;
169         }
170         catch ( ParseException e )
171         {
172             log.warn( "Unable to parse query [" + term + "]: " + e.getMessage(), e );
173         }
174
175         // empty results.
176         return new SearchResults();
177     }
178
179     public SearchResults searchForTerm( String principal, List<String> selectedRepos, String term, SearchResultLimits limits )
180     {
181         return searchForTerm( principal, selectedRepos, term, limits, null );        
182     }
183
184     public SearchResults searchForTerm( String principal, List<String> selectedRepos, String term,
185                                         SearchResultLimits limits, List<String> previousSearchTerms )
186     {
187         List<RepositoryContentIndex> indexes = getFileContentIndexes( principal, selectedRepos );
188
189         try
190         {
191             QueryParser parser = new FileContentHandlers().getQueryParser();
192             LuceneQuery query = null;
193             SearchResults results = null;
194             if ( previousSearchTerms == null || previousSearchTerms.isEmpty() )
195             {
196                 query = new LuceneQuery( parser.parse( term ) );
197                 results = searchAll( query, limits, indexes, null );
198             }
199             else
200             {
201                 // AND the previous search terms
202                 BooleanQuery booleanQuery = new BooleanQuery();
203                 for ( String previousSearchTerm : previousSearchTerms )
204                 {
205                     booleanQuery.add( parser.parse( previousSearchTerm ), BooleanClause.Occur.MUST );
206                 }
207
208                 query = new LuceneQuery( booleanQuery );
209                 Filter filter = new QueryWrapperFilter( parser.parse( term ) );
210                 results = searchAll( query, limits, indexes, filter );
211             }
212             results.getRepositories().addAll( this.localIndexedRepositories );
213
214             return results;
215         }
216         catch ( ParseException e )
217         {
218             log.warn( "Unable to parse query [" + term + "]: " + e.getMessage(), e );
219         }
220
221         // empty results.
222         return new SearchResults();
223     }
224
225     private SearchResults searchAll( LuceneQuery luceneQuery, SearchResultLimits limits, List<RepositoryContentIndex> indexes, Filter filter )
226     {
227         org.apache.lucene.search.Query specificQuery = luceneQuery.getLuceneQuery();
228
229         SearchResults results = new SearchResults();
230
231         if ( indexes.isEmpty() )
232         {
233             // No point going any further.
234             return results;
235         }
236
237         // Setup the converter
238         LuceneEntryConverter converter = null;
239         RepositoryContentIndex index = indexes.get( 0 );
240         converter = index.getEntryConverter();
241
242         // Process indexes into an array of Searchables.
243         List<Searchable> searchableList = toSearchables( indexes );
244
245         Searchable searchables[] = new Searchable[searchableList.size()];
246         searchableList.toArray( searchables );
247
248         MultiSearcher searcher = null;
249
250         try
251         {
252             // Create a multi-searcher for looking up the information.
253             searcher = new MultiSearcher( searchables );
254
255             // Perform the search.
256             Hits hits = null;
257             if ( filter != null )
258             {
259                 hits = searcher.search( specificQuery, filter );
260             }
261             else
262             {
263                 hits = searcher.search( specificQuery );
264             }
265
266             int hitCount = hits.length();
267
268             // Now process the limits.
269             results.setLimits( limits );
270             results.setTotalHits( hitCount );
271
272             int fetchCount = limits.getPageSize();
273             int offset = ( limits.getSelectedPage() * limits.getPageSize() );
274
275             if ( limits.getSelectedPage() == SearchResultLimits.ALL_PAGES )
276             {
277                 fetchCount = hitCount;
278                 offset = 0;
279             }
280
281             // Goto offset.
282             if ( offset < hitCount )
283             {
284                 // only process if the offset is within the hit count.
285                 for ( int i = 0; i < fetchCount; i++ )
286                 {
287                     // Stop fetching if we are past the total # of available hits.
288                     if ( offset + i >= hitCount )
289                     {
290                         break;
291                     }
292
293                     try
294                     {
295                         Document doc = hits.doc( offset + i );
296                         LuceneRepositoryContentRecord record = converter.convert( doc );
297                         results.addHit( record );
298                     }
299                     catch ( java.text.ParseException e )
300                     {
301                         log.warn( "Unable to parse document into record: " + e.getMessage(), e );
302                     }
303                 }
304             }
305
306         }
307         catch ( IOException e )
308         {
309             log.error( "Unable to setup multi-search: " + e.getMessage(), e );
310         }
311         finally
312         {
313             try
314             {
315                 if ( searcher != null )
316                 {
317                     searcher.close();
318                 }
319             }
320             catch ( IOException ie )
321             {
322                 log.error( "Unable to close index searcher: " + ie.getMessage(), ie );
323             }
324         }
325
326         return results;
327     }
328
329     private List<Searchable> toSearchables( List<RepositoryContentIndex> indexes )
330     {
331         List<Searchable> searchableList = new ArrayList<Searchable>();
332         for ( RepositoryContentIndex contentIndex : indexes )
333         {
334             try
335             {
336                 searchableList.add( contentIndex.getSearchable() );
337             }
338             catch ( RepositoryIndexSearchException e )
339             {
340                 log.warn( "Unable to get searchable for index [" + contentIndex.getId() + "] :"
341                                       + e.getMessage(), e );
342             }
343         }
344         return searchableList;
345     }
346
347     public List<RepositoryContentIndex> getBytecodeIndexes( String principal, List<String> selectedRepos )
348     {
349         List<RepositoryContentIndex> ret = new ArrayList<RepositoryContentIndex>();
350
351         for ( ManagedRepositoryConfiguration repoConfig : localIndexedRepositories )
352         {
353             // Only used selected repo
354             if ( selectedRepos.contains( repoConfig.getId() ) )
355             {
356                 RepositoryContentIndex index = indexFactory.createBytecodeIndex( repoConfig );
357                 // If they exist.
358                 if ( indexExists( index ) )
359                 {
360                     ret.add( index );
361                 }
362             }
363         }
364
365         return ret;
366     }
367
368     public List<RepositoryContentIndex> getFileContentIndexes( String principal, List<String> selectedRepos )
369     {
370         List<RepositoryContentIndex> ret = new ArrayList<RepositoryContentIndex>();
371
372         for ( ManagedRepositoryConfiguration repoConfig : localIndexedRepositories )
373         {
374             // Only used selected repo
375             if ( selectedRepos.contains( repoConfig.getId() ) )
376             {
377                 RepositoryContentIndex index = indexFactory.createFileContentIndex( repoConfig );
378                 // If they exist.
379                 if ( indexExists( index ) )
380                 {
381                     ret.add( index );
382                 }
383             }
384         }
385
386         return ret;
387     }
388
389     public List<RepositoryContentIndex> getHashcodeIndexes( String principal, List<String> selectedRepos )
390     {
391         List<RepositoryContentIndex> ret = new ArrayList<RepositoryContentIndex>();
392
393         for ( ManagedRepositoryConfiguration repoConfig : localIndexedRepositories )
394         {
395             // Only used selected repo
396             if ( selectedRepos.contains( repoConfig.getId() ) )
397             {
398                 RepositoryContentIndex index = indexFactory.createHashcodeIndex( repoConfig );
399                 // If they exist.
400                 if ( indexExists( index ) )
401                 {
402                     ret.add( index );
403                 }
404             }
405         }
406
407         return ret;
408     }
409
410     private boolean indexExists( RepositoryContentIndex index )
411     {
412         try
413         {
414             return index.exists();
415         }
416         catch ( RepositoryIndexException e )
417         {
418             log.info(
419                               "Repository Content Index [" + index.getId() + "] for repository ["
420                                   + index.getRepository().getId() + "] does not exist yet in ["
421                                   + index.getIndexDirectory().getAbsolutePath() + "]." );
422             return false;
423         }
424     }
425
426     public void afterConfigurationChange( Registry registry, String propertyName, Object propertyValue )
427     {
428         if ( ConfigurationNames.isManagedRepositories( propertyName ) )
429         {
430             initRepositories();
431         }
432     }
433
434     public void beforeConfigurationChange( Registry registry, String propertyName, Object propertyValue )
435     {
436         /* Nothing to do here */
437     }
438
439     private void initRepositories()
440     {
441         synchronized ( this.localIndexedRepositories )
442         {
443             this.localIndexedRepositories.clear();
444
445             List<ManagedRepositoryConfiguration> repos = configuration.getConfiguration().getManagedRepositories();
446             for ( ManagedRepositoryConfiguration repo : repos )
447             {
448                 if ( repo.isScanned() )
449                 {
450                     localIndexedRepositories.add( repo );
451                 }
452             }
453         }
454     }
455
456     private void parseAndAdd( BooleanQuery query, String key, String value, String delimiter )
457     {
458         if ( value != null && value.length() > 0 )
459         {
460             String[] terms = value.split( delimiter );
461             for ( int i = 0; i < terms.length; i++ )
462             {
463                 Term valueTerm = new Term( key, terms[i] );
464                 Query valueQuery = new TermQuery( valueTerm );
465                 query.add( valueQuery, BooleanClause.Occur.MUST );
466             }
467         }
468         else
469         {
470             Term valueTerm = new Term( key, value );
471             Query valueQuery = new TermQuery( valueTerm );
472             query.add( valueQuery, BooleanClause.Occur.MUST );
473         }
474     }
475
476     public void initialize()
477         throws InitializationException
478     {
479         initRepositories();
480         configuration.addChangeListener( this );
481     }
482 }