]> source.dussan.org Git - archiva.git/blob
e5c1e4f5bb30bff1c8a88311b0d82d4955ae5d6d
[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 org.apache.archiva.common.plexusbridge.MavenIndexerUtils;
23 import org.apache.archiva.common.plexusbridge.PlexusSisuBridge;
24 import org.apache.archiva.common.plexusbridge.PlexusSisuBridgeException;
25 import org.apache.archiva.indexer.util.SearchUtil;
26 import org.apache.commons.lang.StringUtils;
27 import org.apache.lucene.search.BooleanClause.Occur;
28 import org.apache.lucene.search.BooleanQuery;
29 import org.apache.maven.archiva.configuration.ArchivaConfiguration;
30 import org.apache.maven.archiva.configuration.Configuration;
31 import org.apache.maven.archiva.configuration.ManagedRepositoryConfiguration;
32 import org.apache.maven.index.ArtifactInfo;
33 import org.apache.maven.index.FlatSearchRequest;
34 import org.apache.maven.index.FlatSearchResponse;
35 import org.apache.maven.index.MAVEN;
36 import org.apache.maven.index.NexusIndexer;
37 import org.apache.maven.index.OSGI;
38 import org.apache.maven.index.context.IndexCreator;
39 import org.apache.maven.index.context.IndexingContext;
40 import org.apache.maven.index.context.UnsupportedExistingLuceneIndexException;
41 import org.apache.maven.index.expr.StringSearchExpression;
42 import org.slf4j.Logger;
43 import org.slf4j.LoggerFactory;
44 import org.springframework.stereotype.Service;
45
46 import javax.inject.Inject;
47 import java.io.File;
48 import java.io.IOException;
49 import java.util.ArrayList;
50 import java.util.List;
51 import java.util.Map;
52 import java.util.Set;
53
54 /**
55  * RepositorySearch implementation which uses the Nexus Indexer for searching.
56  */
57 @Service( "nexusSearch" )
58 public class NexusRepositorySearch
59     implements RepositorySearch
60 {
61     private Logger log = LoggerFactory.getLogger( getClass() );
62
63     private NexusIndexer indexer;
64
65     private ArchivaConfiguration archivaConfig;
66
67     private MavenIndexerUtils mavenIndexerUtils;
68
69     @Inject
70     public NexusRepositorySearch( PlexusSisuBridge plexusSisuBridge, ArchivaConfiguration archivaConfig,
71                                   MavenIndexerUtils mavenIndexerUtils )
72         throws PlexusSisuBridgeException
73     {
74         this.indexer = plexusSisuBridge.lookup( NexusIndexer.class );
75         this.archivaConfig = archivaConfig;
76         this.mavenIndexerUtils = mavenIndexerUtils;
77
78     }
79
80     /**
81      * @see RepositorySearch#search(String, List, String, SearchResultLimits, List)
82      */
83     public SearchResults search( String principal, List<String> selectedRepos, String term, SearchResultLimits limits,
84                                  List<String> previousSearchTerms )
85         throws RepositorySearchException
86     {
87         List<String> indexingContextIds = addIndexingContexts( selectedRepos );
88
89         // since upgrade to nexus 2.0.0, query has changed from g:[QUERIED TERM]* to g:*[QUERIED TERM]*
90         //      resulting to more wildcard searches so we need to increase max clause count
91         BooleanQuery.setMaxClauseCount( Integer.MAX_VALUE );
92         BooleanQuery q = new BooleanQuery();
93
94         if ( previousSearchTerms == null || previousSearchTerms.isEmpty() )
95         {
96             constructQuery( term, q );
97         }
98         else
99         {
100             for ( String previousTerm : previousSearchTerms )
101             {
102                 BooleanQuery iQuery = new BooleanQuery();
103                 constructQuery( previousTerm, iQuery );
104
105                 q.add( iQuery, Occur.MUST );
106             }
107
108             BooleanQuery iQuery = new BooleanQuery();
109             constructQuery( term, iQuery );
110             q.add( iQuery, Occur.MUST );
111         }
112
113         return search( limits, q, indexingContextIds );
114     }
115
116     /**
117      * @see RepositorySearch#search(String, SearchFields, SearchResultLimits)
118      */
119     public SearchResults search( String principal, SearchFields searchFields, SearchResultLimits limits )
120         throws RepositorySearchException
121     {
122         if ( searchFields.getRepositories() == null )
123         {
124             throw new RepositorySearchException( "Repositories cannot be null." );
125         }
126
127         List<String> indexingContextIds = addIndexingContexts( searchFields.getRepositories() );
128
129         BooleanQuery q = new BooleanQuery();
130         if ( StringUtils.isNotBlank( searchFields.getGroupId() ) )
131         {
132             q.add( indexer.constructQuery( MAVEN.GROUP_ID, new StringSearchExpression( searchFields.getGroupId() ) ),
133                    Occur.MUST );
134         }
135
136         if ( StringUtils.isNotBlank( searchFields.getArtifactId() ) )
137         {
138             q.add(
139                 indexer.constructQuery( MAVEN.ARTIFACT_ID, new StringSearchExpression( searchFields.getArtifactId() ) ),
140                 Occur.MUST );
141         }
142
143         if ( StringUtils.isNotBlank( searchFields.getVersion() ) )
144         {
145             q.add( indexer.constructQuery( MAVEN.VERSION, new StringSearchExpression( searchFields.getVersion() ) ),
146                    Occur.MUST );
147         }
148
149         if ( StringUtils.isNotBlank( searchFields.getPackaging() ) )
150         {
151             q.add( indexer.constructQuery( MAVEN.PACKAGING, new StringSearchExpression( searchFields.getPackaging() ) ),
152                    Occur.MUST );
153         }
154
155         if ( StringUtils.isNotBlank( searchFields.getClassName() ) )
156         {
157             q.add(
158                 indexer.constructQuery( MAVEN.CLASSNAMES, new StringSearchExpression( searchFields.getClassName() ) ),
159                 Occur.MUST );
160         }
161
162         if ( StringUtils.isNotBlank( searchFields.getBundleSymbolicName() ) )
163         {
164             q.add( indexer.constructQuery( OSGI.SYMBOLIC_NAME,
165                                            new StringSearchExpression( searchFields.getBundleSymbolicName() ) ),
166                    Occur.MUST );
167         }
168
169         if ( StringUtils.isNotBlank( searchFields.getBundleVersion() ) )
170         {
171             q.add(
172                 indexer.constructQuery( OSGI.VERSION, new StringSearchExpression( searchFields.getBundleVersion() ) ),
173                 Occur.MUST );
174         }
175
176         if ( StringUtils.isNotBlank( searchFields.getBundleExportPackage() ) )
177         {
178             q.add( indexer.constructQuery( OSGI.EXPORT_PACKAGE,
179                                            new StringSearchExpression( searchFields.getBundleExportPackage() ) ),
180                    Occur.MUST );
181         }
182
183         if ( StringUtils.isNotBlank( searchFields.getBundleExportService() ) )
184         {
185             q.add( indexer.constructQuery( OSGI.SYMBOLIC_NAME,
186                                            new StringSearchExpression( searchFields.getBundleExportService() ) ),
187                    Occur.MUST );
188         }
189
190         if ( q.getClauses() == null || q.getClauses().length <= 0 )
191         {
192             throw new RepositorySearchException( "No search fields set." );
193         }
194
195         return search( limits, q, indexingContextIds );
196     }
197
198     private SearchResults search( SearchResultLimits limits, BooleanQuery q, List<String> indexingContextIds )
199         throws RepositorySearchException
200     {
201         try
202         {
203             FlatSearchRequest request = new FlatSearchRequest( q );
204             request.setContexts( getIndexingContexts( indexingContextIds ) );
205             FlatSearchResponse response = indexer.searchFlat( request );
206
207             if ( response == null || response.getTotalHits() == 0 )
208             {
209                 SearchResults results = new SearchResults();
210                 results.setLimits( limits );
211                 return results;
212             }
213
214             return convertToSearchResults( response, limits );
215         }
216         catch ( IOException e )
217         {
218             throw new RepositorySearchException( e );
219         }
220         /*
221         olamy : don't understand why this ?? it remove content from index ??
222         comment until someone explain WTF ?? :-))
223         finally
224         {
225             Map<String, IndexingContext> indexingContexts = indexer.getIndexingContexts();
226
227             for ( Map.Entry<String, IndexingContext> entry : indexingContexts.entrySet() )
228             {
229                 try
230                 {
231                     indexer.removeIndexingContext( entry.getValue(), false );
232                     log.debug( "Indexing context '{}' removed from search.", entry.getKey() );
233                 }
234                 catch ( IOException e )
235                 {
236                     log.warn( "IOException occurred while removing indexing content '" + entry.getKey() + "'." );
237                     continue;
238                 }
239             }
240         }*/
241     }
242
243     private List<IndexingContext> getIndexingContexts( List<String> ids )
244     {
245         List<IndexingContext> contexts = new ArrayList<IndexingContext>( ids.size() );
246
247         for ( String id : ids )
248         {
249             IndexingContext context = indexer.getIndexingContexts().get( id );
250             if ( context != null )
251             {
252                 contexts.add( context );
253             }
254             else
255             {
256                 log.warn( "context with id {} not exists", id );
257             }
258         }
259
260         return contexts;
261     }
262
263     private void constructQuery( String term, BooleanQuery q )
264     {
265         q.add( indexer.constructQuery( MAVEN.GROUP_ID, new StringSearchExpression( term ) ), Occur.SHOULD );
266         q.add( indexer.constructQuery( MAVEN.ARTIFACT_ID, new StringSearchExpression( term ) ), Occur.SHOULD );
267         q.add( indexer.constructQuery( MAVEN.VERSION, new StringSearchExpression( term ) ), Occur.SHOULD );
268         q.add( indexer.constructQuery( MAVEN.PACKAGING, new StringSearchExpression( term ) ), Occur.SHOULD );
269         q.add( indexer.constructQuery( MAVEN.CLASSNAMES, new StringSearchExpression( term ) ), Occur.SHOULD );
270         // olamy IMHO we could set this option as at least one must match
271         //q.setMinimumNumberShouldMatch( 1 );
272     }
273
274
275     /**
276      * @param selectedRepos
277      * @return indexing contextId used
278      */
279     private List<String> addIndexingContexts( List<String> selectedRepos )
280     {
281         List<String> indexingContextIds = new ArrayList<String>();
282         for ( String repo : selectedRepos )
283         {
284             try
285             {
286                 Configuration config = archivaConfig.getConfiguration();
287                 ManagedRepositoryConfiguration repoConfig = config.findManagedRepositoryById( repo );
288
289                 if ( repoConfig != null )
290                 {
291                     String indexDir = repoConfig.getIndexDir();
292                     File indexDirectory = null;
293                     if ( indexDir != null && !"".equals( indexDir ) )
294                     {
295                         indexDirectory = new File( repoConfig.getIndexDir() );
296                     }
297                     else
298                     {
299                         indexDirectory = new File( repoConfig.getLocation(), ".indexer" );
300                     }
301
302                     IndexingContext context = indexer.getIndexingContexts().get( repoConfig.getId() );
303                     if ( context != null )
304                     {
305                         // alreday here so no need to record it again
306                         log.debug( "index with id {} already exists skip adding it", repoConfig.getId() );
307                         // set searchable flag
308                         context.setSearchable( repoConfig.isScanned() );
309                         indexingContextIds.add( context.getId() );
310                         continue;
311                     }
312
313                     context = indexer.addIndexingContext( repoConfig.getId(), repoConfig.getId(),
314                                                           new File( repoConfig.getLocation() ), indexDirectory, null,
315                                                           null, getAllIndexCreators() );
316                     context.setSearchable( repoConfig.isScanned() );
317                     if ( context.isSearchable() )
318                     {
319                         indexingContextIds.add( context.getId() );
320                     }
321                     else
322                     {
323                         log.warn( "indexingContext with id {} not searchable", repoConfig.getId() );
324                     }
325
326                 }
327                 else
328                 {
329                     log.warn( "Repository '" + repo + "' not found in configuration." );
330                 }
331             }
332             catch ( UnsupportedExistingLuceneIndexException e )
333             {
334                 log.warn( "Error accessing index of repository '" + repo + "' : " + e.getMessage() );
335                 continue;
336             }
337             catch ( IOException e )
338             {
339                 log.warn( "IO error occured while accessing index of repository '" + repo + "' : " + e.getMessage() );
340                 continue;
341             }
342         }
343         return indexingContextIds;
344     }
345
346
347     protected List<? extends IndexCreator> getAllIndexCreators()
348     {
349         return mavenIndexerUtils.getAllIndexCreators();
350     }
351
352
353     private SearchResults convertToSearchResults( FlatSearchResponse response, SearchResultLimits limits )
354     {
355         SearchResults results = new SearchResults();
356         Set<ArtifactInfo> artifactInfos = response.getResults();
357
358         for ( ArtifactInfo artifactInfo : artifactInfos )
359         {
360             String id = SearchUtil.getHitId( artifactInfo.groupId, artifactInfo.artifactId );
361             Map<String, SearchResultHit> hitsMap = results.getHitsMap();
362
363             SearchResultHit hit = hitsMap.get( id );
364             if ( hit != null )
365             {
366                 if ( !hit.getVersions().contains( artifactInfo.version ) )
367                 {
368                     hit.addVersion( artifactInfo.version );
369                 }
370             }
371             else
372             {
373                 hit = new SearchResultHit();
374                 hit.setArtifactId( artifactInfo.artifactId );
375                 hit.setGroupId( artifactInfo.groupId );
376                 // do we still need to set the repository id even though we're merging everything?
377                 //hit.setRepositoryId( artifactInfo.repository );
378                 hit.setUrl( artifactInfo.repository + "/" + artifactInfo.fname );
379                 hit.addVersion( artifactInfo.version );
380                 hit.setBundleExportPackage( artifactInfo.bundleExportPackage );
381                 hit.setBundleExportService( artifactInfo.bundleExportService );
382                 hit.setBundleSymbolicName( artifactInfo.bundleSymbolicName );
383                 hit.setBundleVersion( artifactInfo.bundleVersion );
384             }
385
386             results.addHit( id, hit );
387         }
388
389         results.setTotalHits( response.getTotalHitsCount() );
390         results.setReturnedHitsCount( response.getReturnedHitsCount() );
391         results.setLimits( limits );
392
393         if ( limits == null || limits.getSelectedPage() == SearchResultLimits.ALL_PAGES )
394         {
395             return results;
396         }
397         else
398         {
399             return paginate( results );
400         }
401     }
402
403     private SearchResults paginate( SearchResults results )
404     {
405         SearchResultLimits limits = results.getLimits();
406         SearchResults paginated = new SearchResults();
407
408         int fetchCount = limits.getPageSize();
409         int offset = ( limits.getSelectedPage() * limits.getPageSize() );
410
411         if ( fetchCount > results.getTotalHits() )
412         {
413             fetchCount = results.getTotalHits();
414         }
415
416         // Goto offset.
417         if ( offset < results.getTotalHits() )
418         {
419             // only process if the offset is within the hit count.
420             for ( int i = 0; i < fetchCount; i++ )
421             {
422                 // Stop fetching if we are past the total # of available hits.
423                 if ( offset + i >= results.getHits().size() )
424                 {
425                     break;
426                 }
427
428                 SearchResultHit hit = results.getHits().get( ( offset + i ) );
429                 if ( hit != null )
430                 {
431                     String id = SearchUtil.getHitId( hit.getGroupId(), hit.getArtifactId() );
432                     paginated.addHit( id, hit );
433                 }
434                 else
435                 {
436                     break;
437                 }
438             }
439         }
440         paginated.setTotalHits( results.getTotalHits() );
441         paginated.setReturnedHitsCount( paginated.getHits().size() );
442         paginated.setLimits( limits );
443
444         return paginated;
445     }
446 }