]> source.dussan.org Git - archiva.git/blob
597d9785504ffdc3419a5afa845fb327a202364c
[archiva.git] /
1 package org.apache.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.File;
23 import java.io.IOException;
24 import java.util.List;
25 import java.util.Map;
26 import java.util.Set;
27
28 import org.apache.lucene.search.BooleanQuery;
29 import org.apache.lucene.search.BooleanClause.Occur;
30 import org.apache.maven.archiva.configuration.ArchivaConfiguration;
31 import org.apache.maven.archiva.configuration.Configuration;
32 import org.apache.maven.archiva.configuration.ManagedRepositoryConfiguration;
33 import org.apache.maven.archiva.indexer.search.SearchResultHit;
34 import org.apache.maven.archiva.indexer.search.SearchResultLimits;
35 import org.apache.maven.archiva.indexer.search.SearchResults;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
38 import org.sonatype.nexus.index.ArtifactInfo;
39 import org.sonatype.nexus.index.FlatSearchRequest;
40 import org.sonatype.nexus.index.FlatSearchResponse;
41 import org.sonatype.nexus.index.NexusIndexer;
42 import org.sonatype.nexus.index.context.IndexContextInInconsistentStateException;
43 import org.sonatype.nexus.index.context.IndexingContext;
44 import org.sonatype.nexus.index.context.UnsupportedExistingLuceneIndexException;
45
46 /**
47  * RepositorySearch implementation which uses the Nexus Indexer for searching.
48  */
49 public class NexusRepositorySearch
50     implements RepositorySearch
51 {
52     private static final Logger log = LoggerFactory.getLogger( NexusRepositorySearch.class ); 
53                                                               
54     private NexusIndexer indexer;
55     
56     private ArchivaConfiguration archivaConfig;
57     
58     public NexusRepositorySearch( NexusIndexer indexer, ArchivaConfiguration archivaConfig )
59     {
60         this.indexer = indexer;
61         this.archivaConfig = archivaConfig;
62     }
63
64     /**
65      * @see RepositorySearch#search(String, List, String, SearchResultLimits, List)
66      */
67     public SearchResults search( String principal, List<String> selectedRepos, String term, SearchResultLimits limits,
68                                  List<String> previousSearchTerms )
69         throws RepositorySearchException
70     {   
71         addIndexingContexts( selectedRepos );
72                 
73         BooleanQuery q = new BooleanQuery();
74         if( previousSearchTerms == null || previousSearchTerms.isEmpty() )
75         {            
76             constructQuery( term, q );
77         }
78         else
79         {   
80             for( String previousTerm : previousSearchTerms )
81             {
82                 BooleanQuery iQuery = new BooleanQuery();
83                 constructQuery( previousTerm, iQuery );
84                 
85                 q.add( iQuery, Occur.MUST );
86             }
87             
88             BooleanQuery iQuery = new BooleanQuery();
89             constructQuery( term, iQuery );
90             q.add( iQuery, Occur.MUST );
91         }        
92                     
93         return search( limits, q );
94     }
95     
96     /**
97      * @see RepositorySearch#search(String, SearchFields, SearchResultLimits)
98      */
99     public SearchResults search( String principal, SearchFields searchFields, SearchResultLimits limits )
100         throws RepositorySearchException
101     {
102         if( searchFields.getRepositories() == null )
103         {
104             throw new RepositorySearchException( "Repositories cannot be null." );
105         }
106         
107         addIndexingContexts( searchFields.getRepositories() );
108         
109         BooleanQuery q = new BooleanQuery();
110         if( searchFields.getGroupId() != null && !"".equals( searchFields.getGroupId() ) )
111         {   
112             q.add( indexer.constructQuery( ArtifactInfo.GROUP_ID, searchFields.getGroupId() ), Occur.MUST );
113         }
114         
115         if( searchFields.getArtifactId() != null && !"".equals( searchFields.getArtifactId() ) )
116         {
117             q.add( indexer.constructQuery( ArtifactInfo.ARTIFACT_ID, searchFields.getArtifactId() ), Occur.MUST );
118         }
119         
120         if( searchFields.getVersion() != null && !"".equals( searchFields.getVersion() ) )
121         {
122             q.add( indexer.constructQuery( ArtifactInfo.VERSION, searchFields.getVersion() ), Occur.MUST );
123         }
124         
125         if( searchFields.getPackaging() != null && !"".equals( searchFields.getPackaging() ) )
126         {
127             q.add( indexer.constructQuery( ArtifactInfo.PACKAGING, searchFields.getPackaging() ), Occur.MUST );
128         }
129         
130         if( searchFields.getClassName() != null && !"".equals( searchFields.getClassName() ) )
131         {
132             q.add( indexer.constructQuery( ArtifactInfo.NAMES, searchFields.getClassName() ), Occur.MUST );
133         }
134         
135         if( q.getClauses() == null || q.getClauses().length <= 0 )
136         {
137             throw new RepositorySearchException( "No search fields set." );
138         }
139         
140         return search( limits, q );        
141     }
142
143     private SearchResults search( SearchResultLimits limits, BooleanQuery q )
144         throws RepositorySearchException
145     {
146         try
147         {
148             FlatSearchRequest request = new FlatSearchRequest( q );
149             FlatSearchResponse response = indexer.searchFlat( request );
150             
151             if( response == null || response.getTotalHits() == 0 )
152             {
153                 SearchResults results = new SearchResults();
154                 results.setLimits( limits );
155                 return results;
156             }
157             
158             return convertToSearchResults( response, limits );
159         }
160         catch ( IndexContextInInconsistentStateException e )
161         {
162             throw new RepositorySearchException( e );
163         }
164         catch ( IOException e )
165         {
166             throw new RepositorySearchException( e );
167         }
168         finally
169         {
170             Map<String, IndexingContext> indexingContexts = indexer.getIndexingContexts();
171             Set<String> keys = indexingContexts.keySet();
172             for( String key : keys )
173             {
174                 try                
175                 {   
176                     indexer.removeIndexingContext( indexingContexts.get( key ), false );
177                     log.debug( "Indexing context '" + key + "' removed from search." );
178                 }
179                 catch ( IOException e )
180                 {
181                     log.warn( "IOException occurred while removing indexing content '" + key  + "'." );
182                     continue;
183                 }
184             }            
185         }
186     }
187
188     private void constructQuery( String term, BooleanQuery q )
189     {
190         q.add( indexer.constructQuery( ArtifactInfo.GROUP_ID, term ), Occur.SHOULD );
191         q.add( indexer.constructQuery( ArtifactInfo.ARTIFACT_ID, term ), Occur.SHOULD );
192         q.add( indexer.constructQuery( ArtifactInfo.VERSION, term ), Occur.SHOULD );
193         q.add( indexer.constructQuery( ArtifactInfo.PACKAGING, term ), Occur.SHOULD );
194         q.add( indexer.constructQuery( ArtifactInfo.NAMES, term ), Occur.SHOULD );        
195     }
196        
197     
198     private void addIndexingContexts( List<String> selectedRepos )
199     {
200         for( String repo : selectedRepos )
201         {
202             try
203             {
204                 Configuration config = archivaConfig.getConfiguration();
205                 ManagedRepositoryConfiguration repoConfig = config.findManagedRepositoryById( repo );
206                 
207                 if( repoConfig != null )
208                 {
209                     String indexDir = repoConfig.getIndexDir();
210                     File indexDirectory = null;
211                     if( indexDir != null && !"".equals( indexDir ) )
212                     {
213                         indexDirectory = new File( repoConfig.getIndexDir() );
214                     }
215                     else
216                     {
217                         indexDirectory = new File( repoConfig.getLocation(), ".indexer" );
218                     }
219                     
220                     IndexingContext context =
221                         indexer.addIndexingContext( repoConfig.getId(), repoConfig.getId(), new File( repoConfig.getLocation() ),
222                                                     indexDirectory, null, null, NexusIndexer.FULL_INDEX );
223                     context.setSearchable( repoConfig.isScanned() );
224                 }
225                 else
226                 {
227                     log.warn( "Repository '" + repo + "' not found in configuration." );
228                 }
229             }
230             catch ( UnsupportedExistingLuceneIndexException e )
231             {                
232                 log.warn( "Error accessing index of repository '" + repo + "' : " + e.getMessage() );
233                 continue;
234             }
235             catch ( IOException e )
236             {                
237                 log.warn( "IO error occured while accessing index of repository '" + repo + "' : " + e.getMessage() );
238                 continue;
239             }
240         }
241     }
242
243     private SearchResults convertToSearchResults( FlatSearchResponse response, SearchResultLimits limits )
244     {   
245         SearchResults results = new SearchResults();
246         Set<ArtifactInfo> artifactInfos = response.getResults();
247         
248         for ( ArtifactInfo artifactInfo : artifactInfos )
249         {
250             String id = getHitId( artifactInfo.groupId, artifactInfo.artifactId );            
251             Map<String, SearchResultHit> hitsMap = results.getHitsMap();
252
253             SearchResultHit hit = hitsMap.get( id );
254             if ( hit != null )
255             {
256                 hit.addVersion( artifactInfo.version );
257             }
258             else
259             {
260                 hit = new SearchResultHit();
261                 hit.setArtifactId( artifactInfo.artifactId );
262                 hit.setGroupId( artifactInfo.groupId );
263                 // do we still need to set the repository id even though we're merging everything?
264                 //hit.setRepositoryId( artifactInfo.repository );
265                 hit.setUrl( artifactInfo.repository + "/" + artifactInfo.fname );
266                 if( !hit.getVersions().contains( artifactInfo.version ) )
267                 {
268                     hit.addVersion( artifactInfo.version );
269                 }
270             }
271
272             results.addHit( id, hit );
273         }
274         
275         results.setTotalHits( results.getHitsMap().size() );
276         results.setLimits( limits );
277         
278         if( limits == null || limits.getSelectedPage() == SearchResultLimits.ALL_PAGES )
279         {   
280             return results;
281         }
282         else
283         {
284             return paginate( results );            
285         }        
286     }
287
288     private SearchResults paginate( SearchResults results )
289     {
290         SearchResultLimits limits = results.getLimits();
291         SearchResults paginated = new SearchResults();  
292         
293         int fetchCount = limits.getPageSize();
294         int offset = ( limits.getSelectedPage() * limits.getPageSize() );
295         
296         if( fetchCount > results.getTotalHits() )
297         {
298             fetchCount = results.getTotalHits();
299         }
300         
301         // Goto offset.
302         if ( offset < results.getTotalHits() )
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 >= results.getHits().size() )
309                 {
310                     break;
311                 }
312                 
313                 SearchResultHit hit = results.getHits().get( ( offset + i ) );
314                 if( hit != null )
315                 {
316                     String id = getHitId( hit.getGroupId(), hit.getArtifactId() );
317                     paginated.addHit( id, hit );
318                 }
319                 else
320                 {
321                     break;
322                 }
323             }
324         }            
325         paginated.setTotalHits( results.getTotalHits() );
326         paginated.setLimits( limits );
327         
328         return paginated;
329     }
330     
331     private static String getHitId( String groupId, String artifactId )
332     {
333         return groupId + ":" + artifactId;
334     }
335 }