]> source.dussan.org Git - archiva.git/blob
5d0698fb6f97bde42701401e7b93d6d53d44a323
[archiva.git] /
1 package org.apache.archiva.indexer.maven.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.admin.model.RepositoryAdminException;
23 import org.apache.archiva.admin.model.beans.ProxyConnector;
24 import org.apache.archiva.admin.model.proxyconnector.ProxyConnectorAdmin;
25 import org.apache.archiva.indexer.UnsupportedBaseContextException;
26 import org.apache.archiva.indexer.search.ArtifactInfoFilter;
27 import org.apache.archiva.indexer.search.NoClassifierArtifactInfoFilter;
28 import org.apache.archiva.indexer.search.RepositorySearch;
29 import org.apache.archiva.indexer.search.RepositorySearchException;
30 import org.apache.archiva.indexer.search.SearchFields;
31 import org.apache.archiva.indexer.search.SearchResultHit;
32 import org.apache.archiva.indexer.search.SearchResultLimits;
33 import org.apache.archiva.indexer.search.SearchResults;
34 import org.apache.archiva.indexer.util.SearchUtil;
35 import org.apache.archiva.model.ArchivaArtifactModel;
36 import org.apache.archiva.repository.RemoteRepository;
37 import org.apache.archiva.repository.Repository;
38 import org.apache.archiva.repository.RepositoryRegistry;
39 import org.apache.archiva.repository.RepositoryType;
40 import org.apache.commons.lang.StringUtils;
41 import org.apache.maven.index.ArtifactInfo;
42 import org.apache.maven.index.FlatSearchRequest;
43 import org.apache.maven.index.FlatSearchResponse;
44 import org.apache.maven.index.Indexer;
45 import org.apache.maven.index.MAVEN;
46 import org.apache.maven.index.OSGI;
47 import org.apache.maven.index.QueryCreator;
48 import org.apache.maven.index.SearchType;
49 import org.apache.maven.index.context.IndexingContext;
50 import org.apache.maven.index.expr.SearchExpression;
51 import org.apache.maven.index.expr.SearchTyped;
52 import org.apache.maven.index.expr.SourcedSearchExpression;
53 import org.apache.maven.index.expr.UserInputSearchExpression;
54 import org.apache.maven.index_shaded.lucene.search.BooleanClause;
55 import org.apache.maven.index_shaded.lucene.search.BooleanClause.Occur;
56 import org.apache.maven.index_shaded.lucene.search.BooleanQuery;
57 import org.slf4j.Logger;
58 import org.slf4j.LoggerFactory;
59 import org.springframework.stereotype.Service;
60
61 import javax.inject.Inject;
62 import java.io.IOException;
63 import java.util.ArrayList;
64 import java.util.Collection;
65 import java.util.Collections;
66 import java.util.HashSet;
67 import java.util.List;
68 import java.util.Map;
69 import java.util.Set;
70
71 /**
72  * RepositorySearch implementation which uses the Maven Indexer for searching.
73  */
74 @Service( "repositorySearch#maven" )
75 public class MavenRepositorySearch
76     implements RepositorySearch
77 {
78     private Logger log = LoggerFactory.getLogger( getClass() );
79
80     private Indexer indexer;
81
82     private QueryCreator queryCreator;
83
84
85     RepositoryRegistry repositoryRegistry;
86
87     private ProxyConnectorAdmin proxyConnectorAdmin;
88
89     protected MavenRepositorySearch()
90     {
91         // for test purpose
92     }
93
94     @Inject
95     public MavenRepositorySearch( Indexer nexusIndexer, RepositoryRegistry repositoryRegistry,
96
97                                   ProxyConnectorAdmin proxyConnectorAdmin, QueryCreator queryCreator )
98     {
99         this.indexer = nexusIndexer;
100         this.queryCreator = queryCreator;
101         this.repositoryRegistry = repositoryRegistry;
102         this.proxyConnectorAdmin = proxyConnectorAdmin;
103     }
104
105     /**
106      * @see RepositorySearch#search(String, List, String, SearchResultLimits, List)
107      */
108     @Override
109     public SearchResults search(String principal, List<String> selectedRepos, String term, SearchResultLimits limits,
110                                 List<String> previousSearchTerms )
111         throws RepositorySearchException
112     {
113         List<String> indexingContextIds = addIndexingContexts( selectedRepos );
114
115         // since upgrade to nexus 2.0.0, query has changed from g:[QUERIED TERM]* to g:*[QUERIED TERM]*
116         //      resulting to more wildcard searches so we need to increase max clause count
117         BooleanQuery.setMaxClauseCount( Integer.MAX_VALUE );
118         BooleanQuery.Builder qb = new BooleanQuery.Builder();
119
120         if ( previousSearchTerms == null || previousSearchTerms.isEmpty() )
121         {
122             constructQuery( term, qb );
123         }
124         else
125         {
126             for ( String previousTerm : previousSearchTerms )
127             {
128                 BooleanQuery.Builder iQuery = new BooleanQuery.Builder();
129                 constructQuery( previousTerm, iQuery );
130
131                 qb.add( iQuery.build(), BooleanClause.Occur.MUST );
132             }
133
134             BooleanQuery.Builder iQuery = new BooleanQuery.Builder();
135             constructQuery( term, iQuery );
136             qb.add( iQuery.build(), BooleanClause.Occur.MUST );
137         }
138
139         // we retun only artifacts without classifier in quick search, olamy cannot find a way to say with this field empty
140         // FIXME  cannot find a way currently to setup this in constructQuery !!!
141         return search( limits, qb.build(), indexingContextIds, NoClassifierArtifactInfoFilter.LIST, selectedRepos, true );
142
143     }
144
145     /**
146      * @see RepositorySearch#search(String, SearchFields, SearchResultLimits)
147      */
148     @Override
149     public SearchResults search( String principal, SearchFields searchFields, SearchResultLimits limits )
150         throws RepositorySearchException
151     {
152         if ( searchFields.getRepositories() == null )
153         {
154             throw new RepositorySearchException( "Repositories cannot be null." );
155         }
156
157         List<String> indexingContextIds = addIndexingContexts( searchFields.getRepositories() );
158
159         // if no index found in the specified ones return an empty search result instead of doing a search on all index
160         // olamy: IMHO doesn't make sense
161         if ( !searchFields.getRepositories().isEmpty() && ( indexingContextIds == null
162             || indexingContextIds.isEmpty() ) )
163         {
164             return new SearchResults();
165         }
166
167         BooleanQuery.Builder qb = new BooleanQuery.Builder();
168         if ( StringUtils.isNotBlank( searchFields.getGroupId() ) )
169         {
170             qb.add( indexer.constructQuery( MAVEN.GROUP_ID, searchFields.isExactSearch() ? new SourcedSearchExpression(
171                        searchFields.getGroupId() ) : new UserInputSearchExpression( searchFields.getGroupId() ) ),
172                    BooleanClause.Occur.MUST );
173         }
174
175         if ( StringUtils.isNotBlank( searchFields.getArtifactId() ) )
176         {
177             qb.add( indexer.constructQuery( MAVEN.ARTIFACT_ID,
178                                            searchFields.isExactSearch()
179                                                ? new SourcedSearchExpression( searchFields.getArtifactId() )
180                                                : new UserInputSearchExpression( searchFields.getArtifactId() ) ),
181                    BooleanClause.Occur.MUST );
182         }
183
184         if ( StringUtils.isNotBlank( searchFields.getVersion() ) )
185         {
186             qb.add( indexer.constructQuery( MAVEN.VERSION, searchFields.isExactSearch() ? new SourcedSearchExpression(
187                        searchFields.getVersion() ) : new SourcedSearchExpression( searchFields.getVersion() ) ),
188                    BooleanClause.Occur.MUST );
189         }
190
191         if ( StringUtils.isNotBlank( searchFields.getPackaging() ) )
192         {
193             qb.add( indexer.constructQuery( MAVEN.PACKAGING, searchFields.isExactSearch() ? new SourcedSearchExpression(
194                        searchFields.getPackaging() ) : new UserInputSearchExpression( searchFields.getPackaging() ) ),
195                    BooleanClause.Occur.MUST );
196         }
197
198         if ( StringUtils.isNotBlank( searchFields.getClassName() ) )
199         {
200             qb.add( indexer.constructQuery( MAVEN.CLASSNAMES,
201                                            new UserInputSearchExpression( searchFields.getClassName() ) ),
202                    BooleanClause.Occur.MUST );
203         }
204
205         if ( StringUtils.isNotBlank( searchFields.getBundleSymbolicName() ) )
206         {
207             qb.add( indexer.constructQuery( OSGI.SYMBOLIC_NAME,
208                                            new UserInputSearchExpression( searchFields.getBundleSymbolicName() ) ),
209                    BooleanClause.Occur.MUST );
210         }
211
212         if ( StringUtils.isNotBlank( searchFields.getBundleVersion() ) )
213         {
214             qb.add( indexer.constructQuery( OSGI.VERSION,
215                                            new UserInputSearchExpression( searchFields.getBundleVersion() ) ),
216                    BooleanClause.Occur.MUST );
217         }
218
219         if ( StringUtils.isNotBlank( searchFields.getBundleExportPackage() ) )
220         {
221             qb.add( indexer.constructQuery( OSGI.EXPORT_PACKAGE,
222                                            new UserInputSearchExpression( searchFields.getBundleExportPackage() ) ),
223                    Occur.MUST );
224         }
225
226         if ( StringUtils.isNotBlank( searchFields.getBundleExportService() ) )
227         {
228             qb.add( indexer.constructQuery( OSGI.EXPORT_SERVICE,
229                                            new UserInputSearchExpression( searchFields.getBundleExportService() ) ),
230                    Occur.MUST );
231         }
232
233         if ( StringUtils.isNotBlank( searchFields.getBundleImportPackage() ) )
234         {
235             qb.add( indexer.constructQuery( OSGI.IMPORT_PACKAGE,
236                                            new UserInputSearchExpression( searchFields.getBundleImportPackage() ) ),
237                    Occur.MUST );
238         }
239
240         if ( StringUtils.isNotBlank( searchFields.getBundleName() ) )
241         {
242             qb.add( indexer.constructQuery( OSGI.NAME, new UserInputSearchExpression( searchFields.getBundleName() ) ),
243                    Occur.MUST );
244         }
245
246         if ( StringUtils.isNotBlank( searchFields.getBundleImportPackage() ) )
247         {
248             qb.add( indexer.constructQuery( OSGI.IMPORT_PACKAGE,
249                                            new UserInputSearchExpression( searchFields.getBundleImportPackage() ) ),
250                    Occur.MUST );
251         }
252
253         if ( StringUtils.isNotBlank( searchFields.getBundleRequireBundle() ) )
254         {
255             qb.add( indexer.constructQuery( OSGI.REQUIRE_BUNDLE,
256                                            new UserInputSearchExpression( searchFields.getBundleRequireBundle() ) ),
257                    Occur.MUST );
258         }
259
260         if ( StringUtils.isNotBlank( searchFields.getClassifier() ) )
261         {
262             qb.add( indexer.constructQuery( MAVEN.CLASSIFIER, searchFields.isExactSearch() ? new SourcedSearchExpression(
263                        searchFields.getClassifier() ) : new UserInputSearchExpression( searchFields.getClassifier() ) ),
264                    Occur.MUST );
265         }
266         else if ( searchFields.isExactSearch() )
267         {
268             //TODO improvement in case of exact search and no classifier we must query for classifier with null value
269             // currently it's done in DefaultSearchService with some filtering
270         }
271
272         BooleanQuery qu = qb.build();
273         if ( qu.clauses() == null || qu.clauses().size() <= 0 )
274         {
275             throw new RepositorySearchException( "No search fields set." );
276         }
277         if (qu.clauses()!=null) {
278             log.debug("CLAUSES ", qu.clauses());
279             for (BooleanClause cl : qu.clauses()) {
280                 log.debug("Clause ",cl);
281             }
282         }
283
284         return search( limits, qu, indexingContextIds, Collections.<ArtifactInfoFilter>emptyList(),
285                        searchFields.getRepositories(), searchFields.isIncludePomArtifacts() );
286     }
287
288     private static class NullSearch
289         implements SearchTyped, SearchExpression
290     {
291         private static final NullSearch INSTANCE = new NullSearch();
292
293         @Override
294         public String getStringValue()
295         {
296             return "[[NULL_VALUE]]";
297         }
298
299         @Override
300         public SearchType getSearchType()
301         {
302             return SearchType.EXACT;
303         }
304     }
305
306     private SearchResults search( SearchResultLimits limits, BooleanQuery q, List<String> indexingContextIds,
307                                   List<? extends ArtifactInfoFilter> filters, List<String> selectedRepos,
308                                   boolean includePoms )
309         throws RepositorySearchException
310     {
311
312         try
313         {
314             FlatSearchRequest request = new FlatSearchRequest( q );
315
316             request.setContexts( getIndexingContexts( indexingContextIds ) );
317             if ( limits != null )
318             {
319                 // we apply limits only when first page asked
320                 if ( limits.getSelectedPage() == 0 )
321                 {
322                     request.setCount( limits.getPageSize() * ( Math.max( 1, limits.getSelectedPage() ) ) );
323                 }
324             }
325
326             FlatSearchResponse response = indexer.searchFlat( request );
327
328             if ( response == null || response.getTotalHits() == 0 )
329             {
330                 SearchResults results = new SearchResults();
331                 results.setLimits( limits );
332                 return results;
333             }
334
335             return convertToSearchResults( response, limits, filters, selectedRepos, includePoms );
336         }
337         catch ( IOException e )
338         {
339             throw new RepositorySearchException( e.getMessage(), e );
340         }
341         catch ( RepositoryAdminException e )
342         {
343             throw new RepositorySearchException( e.getMessage(), e );
344         }
345
346     }
347
348     private IndexingContext getIndexingContext(String id) {
349         String repoId;
350         if (StringUtils.startsWith(id, "remote-")) {
351             repoId = StringUtils.substringAfter(id, "remote-");
352         } else {
353             repoId = id;
354         }
355         Repository repo = repositoryRegistry.getRepository(repoId);
356         if (repo==null) {
357             return null;
358         } else {
359             if (repo.getIndexingContext()!=null) {
360                 try {
361                     return repo.getIndexingContext().getBaseContext(IndexingContext.class);
362                 } catch (UnsupportedBaseContextException e) {
363                     return null;
364                 }
365             } else {
366                 return null;
367             }
368         }
369     }
370
371     private List<IndexingContext> getIndexingContexts( List<String> ids )
372     {
373         List<IndexingContext> contexts = new ArrayList<>( ids.size() );
374
375         for ( String id : ids )
376         {
377             IndexingContext context = getIndexingContext(id);
378             if ( context != null )
379             {
380                 contexts.add( context );
381             }
382             else
383             {
384                 log.warn( "context with id {} not exists", id );
385             }
386         }
387
388         return contexts;
389     }
390
391     private void constructQuery( String term, BooleanQuery.Builder q )
392     {
393         q.add( indexer.constructQuery( MAVEN.GROUP_ID, new UserInputSearchExpression( term ) ), Occur.SHOULD );
394         q.add( indexer.constructQuery( MAVEN.ARTIFACT_ID, new UserInputSearchExpression( term ) ), Occur.SHOULD );
395         q.add( indexer.constructQuery( MAVEN.VERSION, new UserInputSearchExpression( term ) ), Occur.SHOULD );
396         q.add( indexer.constructQuery( MAVEN.PACKAGING, new UserInputSearchExpression( term ) ), Occur.SHOULD );
397         q.add( indexer.constructQuery( MAVEN.CLASSNAMES, new UserInputSearchExpression( term ) ), Occur.SHOULD );
398
399         //Query query =
400         //    new WildcardQuery( new Term( MAVEN.CLASSNAMES.getFieldName(), "*" ) );
401         //q.add( query, Occur.MUST_NOT );
402         // olamy IMHO we could set this option as at least one must match
403         //q.setMinimumNumberShouldMatch( 1 );
404     }
405
406
407     /**
408      * @param selectedRepos
409      * @return indexing contextId used
410      */
411     private List<String> addIndexingContexts( List<String> selectedRepos )
412     {
413         Set<String> indexingContextIds = new HashSet<>();
414         for ( String repo : selectedRepos )
415         {
416             try
417             {
418                 Repository rRepo = repositoryRegistry.getRepository(repo);
419
420                 if ( rRepo != null )
421                 {
422
423                     if (rRepo.getType().equals(RepositoryType.MAVEN)) {
424                         assert rRepo.getIndexingContext() != null;
425                         IndexingContext context = rRepo.getIndexingContext().getBaseContext(IndexingContext.class);
426                         if (context.isSearchable()) {
427                             indexingContextIds.addAll(getRemoteIndexingContextIds(repo));
428                             indexingContextIds.add(context.getId());
429                         } else {
430                             log.warn("indexingContext with id {} not searchable", rRepo.getId());
431                         }
432                     }
433
434                 }
435                 else
436                 {
437                     log.warn( "Repository '{}' not found in configuration.", repo );
438                 }
439             }
440             catch ( RepositorySearchException e )
441             {
442                 log.warn( "RepositorySearchException occured while accessing index of repository '{}' : {}", repo,
443                     e.getMessage() );
444                 continue;
445             } catch (UnsupportedBaseContextException e) {
446                 log.error("Fatal situation: Maven repository without IndexingContext found.");
447                 continue;
448             }
449         }
450
451         return new ArrayList<>( indexingContextIds );
452     }
453
454
455     @Override
456     public Set<String> getRemoteIndexingContextIds( String managedRepoId )
457         throws RepositorySearchException
458     {
459         Set<String> ids = new HashSet<>();
460
461         List<ProxyConnector> proxyConnectors = null;
462         try
463         {
464             proxyConnectors = proxyConnectorAdmin.getProxyConnectorAsMap().get( managedRepoId );
465         }
466         catch ( RepositoryAdminException e )
467         {
468             throw new RepositorySearchException( e.getMessage(), e );
469         }
470
471         if ( proxyConnectors == null || proxyConnectors.isEmpty() )
472         {
473             return ids;
474         }
475
476         for ( ProxyConnector proxyConnector : proxyConnectors )
477         {
478             String remoteId = "remote-" + proxyConnector.getTargetRepoId();
479             RemoteRepository repo = repositoryRegistry.getRemoteRepository(proxyConnector.getTargetRepoId());
480             if (repo.getType()==RepositoryType.MAVEN) {
481                 try {
482                     IndexingContext context = repo.getIndexingContext() != null ? repo.getIndexingContext().getBaseContext(IndexingContext.class) : null;
483                     if (context!=null && context.isSearchable()) {
484                         ids.add(remoteId);
485                     }
486                 } catch (UnsupportedBaseContextException e) {
487                     // Ignore this one
488                 }
489             }
490         }
491
492         return ids;
493     }
494
495     @Override
496     public Collection<String> getAllGroupIds( String principal, List<String> selectedRepos )
497         throws RepositorySearchException
498     {
499         List<IndexingContext> indexContexts = getIndexingContexts( selectedRepos );
500
501         if ( indexContexts == null || indexContexts.isEmpty() )
502         {
503             return Collections.emptyList();
504         }
505
506         try
507         {
508             Set<String> allGroupIds = new HashSet<>();
509             for ( IndexingContext indexingContext : indexContexts )
510             {
511                 allGroupIds.addAll( indexingContext.getAllGroups() );
512             }
513             return allGroupIds;
514         }
515         catch ( IOException e )
516         {
517             throw new RepositorySearchException( e.getMessage(), e );
518         }
519
520     }
521
522     private SearchResults convertToSearchResults( FlatSearchResponse response, SearchResultLimits limits,
523                                                   List<? extends ArtifactInfoFilter> artifactInfoFilters,
524                                                   List<String> selectedRepos, boolean includePoms )
525         throws RepositoryAdminException
526     {
527         SearchResults results = new SearchResults();
528         Set<ArtifactInfo> artifactInfos = response.getResults();
529
530         for ( ArtifactInfo artifactInfo : artifactInfos )
531         {
532             if ( StringUtils.equalsIgnoreCase( "pom", artifactInfo.getFileExtension() ) && !includePoms )
533             {
534                 continue;
535             }
536             String id = SearchUtil.getHitId( artifactInfo.getGroupId(), //
537                                              artifactInfo.getArtifactId(), //
538                                              artifactInfo.getClassifier(), //
539                                              artifactInfo.getPackaging() );
540             Map<String, SearchResultHit> hitsMap = results.getHitsMap();
541
542
543             if ( !applyArtifactInfoFilters( artifactInfo, artifactInfoFilters, hitsMap ) )
544             {
545                 continue;
546             }
547
548             SearchResultHit hit = hitsMap.get( id );
549             if ( hit != null )
550             {
551                 if ( !hit.getVersions().contains( artifactInfo.getVersion() ) )
552                 {
553                     hit.addVersion( artifactInfo.getVersion() );
554                 }
555             }
556             else
557             {
558                 hit = new SearchResultHit();
559                 hit.setArtifactId( artifactInfo.getArtifactId() );
560                 hit.setGroupId( artifactInfo.getGroupId() );
561                 hit.setRepositoryId( artifactInfo.getRepository() );
562                 hit.addVersion( artifactInfo.getVersion() );
563                 hit.setBundleExportPackage( artifactInfo.getBundleExportPackage() );
564                 hit.setBundleExportService( artifactInfo.getBundleExportService() );
565                 hit.setBundleSymbolicName( artifactInfo.getBundleSymbolicName() );
566                 hit.setBundleVersion( artifactInfo.getBundleVersion() );
567                 hit.setBundleDescription( artifactInfo.getBundleDescription() );
568                 hit.setBundleDocUrl( artifactInfo.getBundleDocUrl() );
569                 hit.setBundleRequireBundle( artifactInfo.getBundleRequireBundle() );
570                 hit.setBundleImportPackage( artifactInfo.getBundleImportPackage() );
571                 hit.setBundleLicense( artifactInfo.getBundleLicense() );
572                 hit.setBundleName( artifactInfo.getBundleName() );
573                 hit.setContext( artifactInfo.getContext() );
574                 hit.setGoals( artifactInfo.getGoals() );
575                 hit.setPrefix( artifactInfo.getPrefix() );
576                 hit.setPackaging( artifactInfo.getPackaging() );
577                 hit.setClassifier( artifactInfo.getClassifier() );
578                 hit.setFileExtension( artifactInfo.getFileExtension() );
579                 hit.setUrl( getBaseUrl( artifactInfo, selectedRepos ) );
580             }
581
582             results.addHit( id, hit );
583         }
584
585         results.setTotalHits( response.getTotalHitsCount() );
586         results.setTotalHitsMapSize( results.getHitsMap().values().size() );
587         results.setReturnedHitsCount( response.getReturnedHitsCount() );
588         results.setLimits( limits );
589
590         if ( limits == null || limits.getSelectedPage() == SearchResultLimits.ALL_PAGES )
591         {
592             return results;
593         }
594         else
595         {
596             return paginate( results );
597         }
598     }
599
600     /**
601      * calculate baseUrl without the context and base Archiva Url
602      *
603      * @param artifactInfo
604      * @return
605      */
606     protected String getBaseUrl( ArtifactInfo artifactInfo, List<String> selectedRepos )
607         throws RepositoryAdminException
608     {
609         StringBuilder sb = new StringBuilder();
610         if ( StringUtils.startsWith( artifactInfo.getContext(), "remote-" ) )
611         {
612             // it's a remote index result we search a managed which proxying this remote and on which
613             // current user has read karma
614             String managedRepoId =
615                 getManagedRepoId( StringUtils.substringAfter( artifactInfo.getContext(), "remote-" ), selectedRepos );
616             if ( managedRepoId != null )
617             {
618                 sb.append( '/' ).append( managedRepoId );
619                 artifactInfo.setContext( managedRepoId );
620             }
621         }
622         else
623         {
624             sb.append( '/' ).append( artifactInfo.getContext() );
625         }
626
627         sb.append( '/' ).append( StringUtils.replaceChars( artifactInfo.getGroupId(), '.', '/' ) );
628         sb.append( '/' ).append( artifactInfo.getArtifactId() );
629         sb.append( '/' ).append( artifactInfo.getVersion() );
630         sb.append( '/' ).append( artifactInfo.getArtifactId() );
631         sb.append( '-' ).append( artifactInfo.getVersion() );
632         if ( StringUtils.isNotBlank( artifactInfo.getClassifier() ) )
633         {
634             sb.append( '-' ).append( artifactInfo.getClassifier() );
635         }
636         // maven-plugin packaging is a jar
637         if ( StringUtils.equals( "maven-plugin", artifactInfo.getPackaging() ) )
638         {
639             sb.append( "jar" );
640         }
641         else
642         {
643             sb.append( '.' ).append( artifactInfo.getPackaging() );
644         }
645
646         return sb.toString();
647     }
648
649     /**
650      * return a managed repo for a remote result
651      *
652      * @param remoteRepo
653      * @param selectedRepos
654      * @return
655      * @throws RepositoryAdminException
656      */
657     private String getManagedRepoId( String remoteRepo, List<String> selectedRepos )
658         throws RepositoryAdminException
659     {
660         Map<String, List<ProxyConnector>> proxyConnectorMap = proxyConnectorAdmin.getProxyConnectorAsMap();
661         if ( proxyConnectorMap == null || proxyConnectorMap.isEmpty() )
662         {
663             return null;
664         }
665         if ( selectedRepos != null && !selectedRepos.isEmpty() )
666         {
667             for ( Map.Entry<String, List<ProxyConnector>> entry : proxyConnectorMap.entrySet() )
668             {
669                 if ( selectedRepos.contains( entry.getKey() ) )
670                 {
671                     for ( ProxyConnector proxyConnector : entry.getValue() )
672                     {
673                         if ( StringUtils.equals( remoteRepo, proxyConnector.getTargetRepoId() ) )
674                         {
675                             return proxyConnector.getSourceRepoId();
676                         }
677                     }
678                 }
679             }
680         }
681
682         // we don't find in search selected repos so return the first one
683         for ( Map.Entry<String, List<ProxyConnector>> entry : proxyConnectorMap.entrySet() )
684         {
685
686             for ( ProxyConnector proxyConnector : entry.getValue() )
687             {
688                 if ( StringUtils.equals( remoteRepo, proxyConnector.getTargetRepoId() ) )
689                 {
690                     return proxyConnector.getSourceRepoId();
691                 }
692             }
693
694         }
695         return null;
696     }
697
698     private boolean applyArtifactInfoFilters( ArtifactInfo artifactInfo,
699                                               List<? extends ArtifactInfoFilter> artifactInfoFilters,
700                                               Map<String, SearchResultHit> currentResult )
701     {
702         if ( artifactInfoFilters == null || artifactInfoFilters.isEmpty() )
703         {
704             return true;
705         }
706
707         ArchivaArtifactModel artifact = new ArchivaArtifactModel();
708         artifact.setArtifactId( artifactInfo.getArtifactId() );
709         artifact.setClassifier( artifactInfo.getClassifier() );
710         artifact.setGroupId( artifactInfo.getGroupId() );
711         artifact.setRepositoryId( artifactInfo.getRepository() );
712         artifact.setVersion( artifactInfo.getVersion() );
713         artifact.setChecksumMD5( artifactInfo.getMd5() );
714         artifact.setChecksumSHA1( artifactInfo.getSha1() );
715         for ( ArtifactInfoFilter filter : artifactInfoFilters )
716         {
717             if ( !filter.addArtifactInResult( artifact, currentResult ) )
718             {
719                 return false;
720             }
721         }
722         return true;
723     }
724
725     protected SearchResults paginate( SearchResults results )
726     {
727         SearchResultLimits limits = results.getLimits();
728         SearchResults paginated = new SearchResults();
729
730         // ( limits.getPageSize() * ( Math.max( 1, limits.getSelectedPage() ) ) );
731
732         int fetchCount = limits.getPageSize();
733         int offset = ( limits.getSelectedPage() * limits.getPageSize() );
734
735         if ( fetchCount > results.getTotalHits() )
736         {
737             fetchCount = results.getTotalHits();
738         }
739
740         // Goto offset.
741         if ( offset < results.getTotalHits() )
742         {
743             // only process if the offset is within the hit count.
744             for ( int i = 0; i < fetchCount; i++ )
745             {
746                 // Stop fetching if we are past the total # of available hits.
747                 if ( offset + i >= results.getHits().size() )
748                 {
749                     break;
750                 }
751
752                 SearchResultHit hit = results.getHits().get( ( offset + i ) );
753                 if ( hit != null )
754                 {
755                     String id = SearchUtil.getHitId( hit.getGroupId(), hit.getArtifactId(), hit.getClassifier(),
756                                                      hit.getPackaging() );
757                     paginated.addHit( id, hit );
758                 }
759                 else
760                 {
761                     break;
762                 }
763             }
764         }
765         paginated.setTotalHits( results.getTotalHits() );
766         paginated.setReturnedHitsCount( paginated.getHits().size() );
767         paginated.setTotalHitsMapSize( results.getTotalHitsMapSize() );
768         paginated.setLimits( limits );
769
770         return paginated;
771     }
772
773
774 }