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