]> source.dussan.org Git - archiva.git/blob
3fa8044d900f487839b2e1024cf99e2a92eeb90f
[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         List<String> fieldsList = new ArrayList<String>();
94         List<String> termsList = new ArrayList<String>();
95         List<BooleanClause.Occur> flagsList = new ArrayList<BooleanClause.Occur>();
96         
97         if( groupId != null && !"".equals( groupId.trim() ) )
98         {
99             fieldsList.add( ArtifactKeys.GROUPID );
100             termsList.add( groupId );
101             flagsList.add( BooleanClause.Occur.MUST );            
102         }
103         
104         if( artifactId != null && !"".equals( artifactId.trim() ) )
105         {
106             fieldsList.add( ArtifactKeys.ARTIFACTID );
107             termsList.add( artifactId );
108             flagsList.add( BooleanClause.Occur.MUST );
109         }
110         
111         if( version != null && !"".equals( version.trim() ) )
112         {
113             fieldsList.add( ArtifactKeys.VERSION );
114             termsList.add( version );
115             flagsList.add( BooleanClause.Occur.MUST );
116         }
117         
118         if( className != null && !"".equals( className.trim() ) )
119         {   
120             fieldsList.add( BytecodeKeys.CLASSES );
121             fieldsList.add( BytecodeKeys.FILES );
122             fieldsList.add( BytecodeKeys.METHODS );
123             termsList.add( className.trim() );
124             termsList.add( className.trim() );
125             termsList.add( className.trim() );
126             flagsList.add( BooleanClause.Occur.SHOULD );
127             flagsList.add( BooleanClause.Occur.SHOULD );
128             flagsList.add( BooleanClause.Occur.SHOULD );
129         }        
130         
131         try
132         {
133             String[] fieldsArr = new String[ fieldsList.size() ];
134             String[] queryArr = new String[ termsList.size() ];
135             BooleanClause.Occur[] flagsArr = new BooleanClause.Occur[ flagsList.size() ];
136             
137             Query fieldsQuery =
138                 MultiFieldQueryParser.parse( termsList.toArray( queryArr ), fieldsList.toArray( fieldsArr ),
139                                              flagsList.toArray( flagsArr ), new BytecodeHandlers().getAnalyzer() );
140             
141             LuceneQuery query = new LuceneQuery( fieldsQuery );
142             results = searchAll( query, limits, indexes, null );
143             results.getRepositories().add( this.localIndexedRepositories );
144         }
145         catch ( ParseException e )
146         {
147             log.warn( "Unable to parse advanced search fields and query terms." );
148         }        
149
150         return results;
151     }
152
153     public SearchResults searchForChecksum( String principal, List<String> selectedRepos, String checksum,
154                                             SearchResultLimits limits )
155     {
156         List<RepositoryContentIndex> indexes = getHashcodeIndexes( principal, selectedRepos );
157
158         try
159         {
160             QueryParser parser = new MultiFieldQueryParser( new String[]{HashcodesKeys.MD5, HashcodesKeys.SHA1},
161                                            new HashcodesHandlers().getAnalyzer() );
162             LuceneQuery query = new LuceneQuery( parser.parse( checksum ) );
163             SearchResults results = searchAll( query, limits, indexes, null );
164             results.getRepositories().addAll( this.localIndexedRepositories );
165
166             return results;
167         }
168         catch ( ParseException e )
169         {
170             log.warn( "Unable to parse query [" + checksum + "]: " + e.getMessage(), e );
171         }
172
173         // empty results.
174         return new SearchResults();
175     }
176
177     public SearchResults searchForBytecode( String principal, List<String> selectedRepos, String term, SearchResultLimits limits )
178     {
179         List<RepositoryContentIndex> indexes = getBytecodeIndexes( principal, selectedRepos );
180
181         try
182         {
183             QueryParser parser = new BytecodeHandlers().getQueryParser();
184             LuceneQuery query = new LuceneQuery( parser.parse( term ) );
185             SearchResults results = searchAll( query, limits, indexes, null );
186             results.getRepositories().addAll( this.localIndexedRepositories );
187
188             return results;
189         }
190         catch ( ParseException e )
191         {
192             log.warn( "Unable to parse query [" + term + "]: " + e.getMessage(), e );
193         }
194
195         // empty results.
196         return new SearchResults();
197     }
198
199     public SearchResults searchForTerm( String principal, List<String> selectedRepos, String term, SearchResultLimits limits )
200     {
201         return searchForTerm( principal, selectedRepos, term, limits, null );        
202     }
203
204     public SearchResults searchForTerm( String principal, List<String> selectedRepos, String term,
205                                         SearchResultLimits limits, List<String> previousSearchTerms )
206     {
207         List<RepositoryContentIndex> indexes = getFileContentIndexes( principal, selectedRepos );
208
209         try
210         {
211             QueryParser parser = new FileContentHandlers().getQueryParser();
212             LuceneQuery query = null;
213             SearchResults results = null;
214             if ( previousSearchTerms == null || previousSearchTerms.isEmpty() )
215             {
216                 query = new LuceneQuery( parser.parse( term ) );
217                 results = searchAll( query, limits, indexes, null );
218             }
219             else
220             {
221                 // AND the previous search terms
222                 BooleanQuery booleanQuery = new BooleanQuery();
223                 for ( String previousSearchTerm : previousSearchTerms )
224                 {
225                     booleanQuery.add( parser.parse( previousSearchTerm ), BooleanClause.Occur.MUST );
226                 }
227
228                 query = new LuceneQuery( booleanQuery );
229                 Filter filter = new QueryWrapperFilter( parser.parse( term ) );
230                 results = searchAll( query, limits, indexes, filter );
231             }
232             results.getRepositories().addAll( this.localIndexedRepositories );
233
234             return results;
235         }
236         catch ( ParseException e )
237         {
238             log.warn( "Unable to parse query [" + term + "]: " + e.getMessage(), e );
239         }
240
241         // empty results.
242         return new SearchResults();
243     }
244
245     private SearchResults searchAll( LuceneQuery luceneQuery, SearchResultLimits limits, List<RepositoryContentIndex> indexes, Filter filter )
246     {
247         org.apache.lucene.search.Query specificQuery = luceneQuery.getLuceneQuery();
248
249         SearchResults results = new SearchResults();
250
251         if ( indexes.isEmpty() )
252         {
253             // No point going any further.
254             return results;
255         }
256
257         // Setup the converter
258         LuceneEntryConverter converter = null;
259         RepositoryContentIndex index = indexes.get( 0 );
260         converter = index.getEntryConverter();
261
262         // Process indexes into an array of Searchables.
263         List<Searchable> searchableList = toSearchables( indexes );
264
265         Searchable searchables[] = new Searchable[searchableList.size()];
266         searchableList.toArray( searchables );
267
268         MultiSearcher searcher = null;
269
270         try
271         {
272             // Create a multi-searcher for looking up the information.
273             searcher = new MultiSearcher( searchables );
274
275             // Perform the search.
276             Hits hits = null;
277             if ( filter != null )
278             {
279                 hits = searcher.search( specificQuery, filter );
280             }
281             else
282             {
283                 hits = searcher.search( specificQuery );
284             }
285
286             int hitCount = hits.length();     
287             
288             // Now process the limits.
289             results.setLimits( limits );
290             results.setTotalHits( hitCount );
291
292             int fetchCount = limits.getPageSize();
293             int offset = ( limits.getSelectedPage() * limits.getPageSize() );
294
295             if ( limits.getSelectedPage() == SearchResultLimits.ALL_PAGES )
296             {
297                 fetchCount = hitCount;
298                 offset = 0;
299             }
300
301             // Goto offset.
302             if ( offset < hitCount )
303             {
304                 // only process if the offset is within the hit count.
305                 for ( int i = 0; i < fetchCount; i++ )
306                 {
307                     // Stop fetching if we are past the total # of available hits.
308                     if ( offset + i >= hitCount )
309                     {
310                         break;
311                     }
312
313                     try
314                     {
315                         Document doc = hits.doc( offset + i );
316                         LuceneRepositoryContentRecord record = converter.convert( doc );
317                         results.addHit( record );
318                     }
319                     catch ( java.text.ParseException e )
320                     {
321                         log.warn( "Unable to parse document into record: " + e.getMessage(), e );
322                     }
323                 }
324             }
325
326         }
327         catch ( IOException e )
328         {
329             log.error( "Unable to setup multi-search: " + e.getMessage(), e );
330         }
331         finally
332         {
333             try
334             {
335                 if ( searcher != null )
336                 {
337                     searcher.close();
338                 }
339             }
340             catch ( IOException ie )
341             {
342                 log.error( "Unable to close index searcher: " + ie.getMessage(), ie );
343             }
344         }
345
346         return results;
347     }
348
349     private List<Searchable> toSearchables( List<RepositoryContentIndex> indexes )
350     {
351         List<Searchable> searchableList = new ArrayList<Searchable>();
352         for ( RepositoryContentIndex contentIndex : indexes )
353         {
354             try
355             {
356                 searchableList.add( contentIndex.getSearchable() );
357             }
358             catch ( RepositoryIndexSearchException e )
359             {
360                 log.warn( "Unable to get searchable for index [" + contentIndex.getId() + "] :"
361                                       + e.getMessage(), e );
362             }
363         }
364         return searchableList;
365     }
366
367     public List<RepositoryContentIndex> getBytecodeIndexes( String principal, List<String> selectedRepos )
368     {
369         List<RepositoryContentIndex> ret = new ArrayList<RepositoryContentIndex>();
370
371         for ( ManagedRepositoryConfiguration repoConfig : localIndexedRepositories )
372         {
373             // Only used selected repo
374             if ( selectedRepos.contains( repoConfig.getId() ) )
375             {
376                 RepositoryContentIndex index = indexFactory.createBytecodeIndex( repoConfig );
377                 // If they exist.
378                 if ( indexExists( index ) )
379                 {
380                     ret.add( index );
381                 }
382             }
383         }
384
385         return ret;
386     }
387
388     public List<RepositoryContentIndex> getFileContentIndexes( String principal, List<String> selectedRepos )
389     {
390         List<RepositoryContentIndex> ret = new ArrayList<RepositoryContentIndex>();
391
392         for ( ManagedRepositoryConfiguration repoConfig : localIndexedRepositories )
393         {
394             // Only used selected repo
395             if ( selectedRepos.contains( repoConfig.getId() ) )
396             {
397                 RepositoryContentIndex index = indexFactory.createFileContentIndex( repoConfig );
398                 // If they exist.
399                 if ( indexExists( index ) )
400                 {
401                     ret.add( index );
402                 }
403             }
404         }
405
406         return ret;
407     }
408
409     public List<RepositoryContentIndex> getHashcodeIndexes( String principal, List<String> selectedRepos )
410     {
411         List<RepositoryContentIndex> ret = new ArrayList<RepositoryContentIndex>();
412
413         for ( ManagedRepositoryConfiguration repoConfig : localIndexedRepositories )
414         {
415             // Only used selected repo
416             if ( selectedRepos.contains( repoConfig.getId() ) )
417             {
418                 RepositoryContentIndex index = indexFactory.createHashcodeIndex( repoConfig );
419                 // If they exist.
420                 if ( indexExists( index ) )
421                 {
422                     ret.add( index );
423                 }
424             }
425         }
426
427         return ret;
428     }
429
430     private boolean indexExists( RepositoryContentIndex index )
431     {
432         try
433         {
434             return index.exists();
435         }
436         catch ( RepositoryIndexException e )
437         {
438             log.info(
439                               "Repository Content Index [" + index.getId() + "] for repository ["
440                                   + index.getRepository().getId() + "] does not exist yet in ["
441                                   + index.getIndexDirectory().getAbsolutePath() + "]." );
442             return false;
443         }
444     }
445
446     public void afterConfigurationChange( Registry registry, String propertyName, Object propertyValue )
447     {
448         if ( ConfigurationNames.isManagedRepositories( propertyName ) )
449         {
450             initRepositories();
451         }
452     }
453
454     public void beforeConfigurationChange( Registry registry, String propertyName, Object propertyValue )
455     {
456         /* Nothing to do here */
457     }
458
459     private void initRepositories()
460     {
461         synchronized ( this.localIndexedRepositories )
462         {
463             this.localIndexedRepositories.clear();
464
465             List<ManagedRepositoryConfiguration> repos = configuration.getConfiguration().getManagedRepositories();
466             for ( ManagedRepositoryConfiguration repo : repos )
467             {
468                 if ( repo.isScanned() )
469                 {
470                     localIndexedRepositories.add( repo );
471                 }
472             }
473         }
474     }
475
476     public void initialize()
477         throws InitializationException
478     {
479         initRepositories();
480         configuration.addChangeListener( this );
481     }
482 }