View Javadoc
1   package org.apache.archiva.rest.services;
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.utils.VersionComparator;
23  import org.apache.archiva.indexer.search.RepositorySearch;
24  import org.apache.archiva.indexer.search.RepositorySearchException;
25  import org.apache.archiva.indexer.search.SearchFields;
26  import org.apache.archiva.indexer.search.SearchResultHit;
27  import org.apache.archiva.indexer.search.SearchResultLimits;
28  import org.apache.archiva.indexer.search.SearchResults;
29  import org.apache.archiva.maven2.model.Artifact;
30  import org.apache.archiva.metadata.model.ArtifactMetadata;
31  import org.apache.archiva.metadata.repository.MetadataRepository;
32  import org.apache.archiva.metadata.repository.MetadataRepositoryException;
33  import org.apache.archiva.metadata.repository.RepositorySession;
34  import org.apache.archiva.metadata.repository.RepositorySessionFactory;
35  import org.apache.archiva.rest.api.model.ChecksumSearch;
36  import org.apache.archiva.rest.api.model.GroupIdList;
37  import org.apache.archiva.rest.api.model.SearchRequest;
38  import org.apache.archiva.rest.api.model.StringList;
39  import org.apache.archiva.rest.api.services.ArchivaRestServiceException;
40  import org.apache.archiva.rest.api.services.SearchService;
41  import org.apache.commons.collections.ListUtils;
42  import org.apache.commons.lang.StringUtils;
43  import org.springframework.stereotype.Service;
44  
45  import javax.inject.Inject;
46  import javax.ws.rs.core.Response;
47  import java.net.URI;
48  import java.util.ArrayList;
49  import java.util.Arrays;
50  import java.util.Collection;
51  import java.util.Collections;
52  import java.util.HashSet;
53  import java.util.List;
54  import java.util.Set;
55  import java.util.TreeMap;
56  
57  /**
58   * @author Olivier Lamy
59   */
60  @Service( "searchService#rest" )
61  public class DefaultSearchService
62      extends AbstractRestService
63      implements SearchService
64  {
65  
66      private static final String LATEST_KEYWORD = "LATEST";
67  
68      @Inject
69      private RepositorySearch repositorySearch;
70  
71      @Inject
72      private RepositorySessionFactory repositorySessionFactory;
73  
74      @Override
75      public List<Artifact> quickSearch( String queryString )
76          throws ArchivaRestServiceException
77      {
78          if ( StringUtils.isBlank( queryString ) )
79          {
80              return Collections.emptyList();
81          }
82  
83          SearchResultLimits limits = new SearchResultLimits( 0 );
84          try
85          {
86              SearchResults searchResults =
87                  repositorySearch.search( getPrincipal(), getObservableRepos(), queryString, limits,
88                                           Collections.<String>emptyList() );
89              return getArtifacts( searchResults );
90  
91          }
92          catch ( RepositorySearchException e )
93          {
94              log.error( e.getMessage(), e );
95              throw new ArchivaRestServiceException( e.getMessage(), e );
96          }
97      }
98  
99      @Override
100     public List<Artifact> quickSearchWithRepositories( SearchRequest searchRequest )
101         throws ArchivaRestServiceException
102     {
103         String queryString = searchRequest.getQueryTerms();
104         if ( StringUtils.isBlank( queryString ) )
105         {
106             return Collections.emptyList();
107         }
108         List<String> repositories = searchRequest.getRepositories();
109         if ( repositories == null || repositories.isEmpty() )
110         {
111             repositories = getObservableRepos();
112         }
113         SearchResultLimits limits =
114             new SearchResultLimits( searchRequest.getPageSize(), searchRequest.getSelectedPage() );
115         try
116         {
117             SearchResults searchResults = repositorySearch.search( getPrincipal(), repositories, queryString, limits,
118                                                                    Collections.<String>emptyList() );
119             return getArtifacts( searchResults );
120 
121         }
122         catch ( RepositorySearchException e )
123         {
124             log.error( e.getMessage(), e );
125             throw new ArchivaRestServiceException( e.getMessage(), e );
126         }
127     }
128 
129     @Override
130     public List<Artifact> getArtifactVersions( String groupId, String artifactId, String packaging )
131         throws ArchivaRestServiceException
132     {
133         if ( StringUtils.isBlank( groupId ) || StringUtils.isBlank( artifactId ) )
134         {
135             return Collections.emptyList();
136         }
137         SearchFields searchField = new SearchFields();
138         searchField.setGroupId( groupId );
139         searchField.setArtifactId( artifactId );
140         searchField.setPackaging( StringUtils.isBlank( packaging ) ? "jar" : packaging );
141         searchField.setRepositories( getObservableRepos() );
142 
143         try
144         {
145             SearchResults searchResults = repositorySearch.search( getPrincipal(), searchField, null );
146             return getArtifacts( searchResults );
147         }
148         catch ( RepositorySearchException e )
149         {
150             log.error( e.getMessage(), e );
151             throw new ArchivaRestServiceException( e.getMessage(), e );
152         }
153     }
154 
155     @Override
156     public List<Artifact> searchArtifacts( SearchRequest searchRequest )
157         throws ArchivaRestServiceException
158     {
159         if ( searchRequest == null )
160         {
161             return Collections.emptyList();
162         }
163         SearchFields searchField = getModelMapper().map( searchRequest, SearchFields.class );
164         SearchResultLimits limits = new SearchResultLimits( 0 );
165         limits.setPageSize( searchRequest.getPageSize() );
166 
167         // if no repos set we use ones available for the user
168         if ( searchField.getRepositories() == null || searchField.getRepositories().isEmpty() )
169         {
170             searchField.setRepositories( getObservableRepos() );
171         }
172 
173         try
174         {
175             SearchResults searchResults = repositorySearch.search( getPrincipal(), searchField, limits );
176             return getArtifacts( searchResults );
177         }
178         catch ( RepositorySearchException e )
179         {
180             log.error( e.getMessage(), e );
181             throw new ArchivaRestServiceException( e.getMessage(), e );
182         }
183     }
184 
185     @Override
186     public GroupIdList getAllGroupIds( List<String> selectedRepos )
187         throws ArchivaRestServiceException
188     {
189         List<String> observableRepos = getObservableRepos();
190         List<String> repos = ListUtils.intersection( observableRepos, selectedRepos );
191         if ( repos == null || repos.isEmpty() )
192         {
193             return new GroupIdList( Collections.<String>emptyList() );
194         }
195         try
196         {
197             return new GroupIdList( new ArrayList<>( repositorySearch.getAllGroupIds( getPrincipal(), repos ) ) );
198         }
199         catch ( RepositorySearchException e )
200         {
201             log.error( e.getMessage(), e );
202             throw new ArchivaRestServiceException( e.getMessage(), e );
203         }
204 
205     }
206 
207 
208     public List<Artifact> getArtifactByChecksum( ChecksumSearch checksumSearch )
209         throws ArchivaRestServiceException
210     {
211 
212         // if no repos set we use ones available for the user
213         if ( checksumSearch.getRepositories() == null || checksumSearch.getRepositories().isEmpty() )
214         {
215             checksumSearch.setRepositories( getObservableRepos() );
216         }
217 
218         RepositorySession repositorySession = repositorySessionFactory.createSession();
219 
220         MetadataRepository metadataRepository = repositorySession.getRepository();
221 
222         Set<Artifact> artifactSet = new HashSet<>();
223 
224         try
225         {
226             for ( String repoId : checksumSearch.getRepositories() )
227             {
228                 Collection<ArtifactMetadata> artifactMetadatas =
229                     metadataRepository.getArtifactsByChecksum( repoId, checksumSearch.getChecksum() );
230                 artifactSet.addAll( buildArtifacts( artifactMetadatas, repoId ) );
231             }
232 
233             return new ArrayList<>( artifactSet );
234 
235         }
236         catch ( MetadataRepositoryException e )
237         {
238             log.error( e.getMessage(), e );
239             throw new ArchivaRestServiceException( e.getMessage(), e );
240         }
241         finally
242         {
243             repositorySession.closeQuietly();
244         }
245 
246 
247     }
248 
249     @Override
250     public StringList getObservablesRepoIds()
251         throws ArchivaRestServiceException
252     {
253         return new StringList( getObservableRepos() );
254     }
255 
256     @Override
257     public Response redirectToArtifactFile( String repositoryId, String groupId, String artifactId, String version,
258                                             String packaging, String classifier )
259         throws ArchivaRestServiceException
260     {
261         try
262         {
263             // validate query
264 
265             if ( StringUtils.isEmpty( groupId ) )
266             {
267                 return Response.status( new Response.StatusType()
268                 {
269                     @Override
270                     public int getStatusCode()
271                     {
272                         return Response.Status.BAD_REQUEST.getStatusCode();
273                     }
274 
275                     @Override
276                     public Response.Status.Family getFamily()
277                     {
278                         return Response.Status.BAD_REQUEST.getFamily();
279                     }
280 
281                     @Override
282                     public String getReasonPhrase()
283                     {
284                         return "groupId mandatory";
285                     }
286                 } ).build();
287             }
288 
289             if ( StringUtils.isEmpty( version ) )
290             {
291                 return Response.status( new Response.StatusType()
292                 {
293                     @Override
294                     public int getStatusCode()
295                     {
296                         return Response.Status.BAD_REQUEST.getStatusCode();
297                     }
298 
299                     @Override
300                     public Response.Status.Family getFamily()
301                     {
302                         return Response.Status.BAD_REQUEST.getFamily();
303                     }
304 
305                     @Override
306                     public String getReasonPhrase()
307                     {
308                         return "version mandatory";
309                     }
310                 } ).build();
311             }
312 
313             if ( StringUtils.isEmpty( artifactId ) )
314             {
315                 return Response.status( new Response.StatusType()
316                 {
317                     @Override
318                     public int getStatusCode()
319                     {
320                         return Response.Status.BAD_REQUEST.getStatusCode();
321                     }
322 
323                     @Override
324                     public Response.Status.Family getFamily()
325                     {
326                         return Response.Status.BAD_REQUEST.getFamily();
327                     }
328 
329                     @Override
330                     public String getReasonPhrase()
331                     {
332                         return "artifactId mandatory";
333                     }
334                 } ).build();
335             }
336 
337             SearchFields searchField = new SearchFields();
338             searchField.setGroupId( groupId );
339             searchField.setArtifactId( artifactId );
340             searchField.setPackaging( StringUtils.isBlank( packaging ) ? "jar" : packaging );
341             if ( !StringUtils.equals( version, LATEST_KEYWORD ) )
342             {
343                 searchField.setVersion( version );
344             }
345             searchField.setClassifier( classifier );
346             List<String> userRepos = getObservablesRepoIds().getStrings();
347             searchField.setRepositories(
348                 StringUtils.isEmpty( repositoryId ) ? userRepos : Arrays.asList( repositoryId ) );
349             searchField.setExactSearch( true );
350             SearchResults searchResults = repositorySearch.search( getPrincipal(), searchField, null );
351             List<Artifact> artifacts = getArtifacts( searchResults );
352 
353             if ( artifacts.isEmpty() )
354             {
355                 return Response.status( new Response.StatusType()
356                 {
357                     @Override
358                     public int getStatusCode()
359                     {
360                         return Response.Status.NO_CONTENT.getStatusCode();
361                     }
362 
363                     @Override
364                     public Response.Status.Family getFamily()
365                     {
366                         return Response.Status.NO_CONTENT.getFamily();
367                     }
368 
369                     @Override
370                     public String getReasonPhrase()
371                     {
372                         return "your query doesn't return any artifact";
373                     }
374                 } ).build();
375             }
376 
377             // TODO improve that with querying lucene with null value for classifier
378             // so simple loop and retain only artifact with null classifier
379             if ( classifier == null )
380             {
381                 List<Artifact> filteredArtifacts = new ArrayList<>( artifacts.size() );
382                 for ( Artifact artifact : artifacts )
383                 {
384                     if ( artifact.getClassifier() == null )
385                     {
386                         filteredArtifacts.add( artifact );
387                     }
388                 }
389 
390                 artifacts = filteredArtifacts;
391             }
392 
393             // TODO return json result of the query ?
394             if ( artifacts.size() > 1 && !StringUtils.equals( version, LATEST_KEYWORD ) )
395             {
396                 return Response.status( new Response.StatusType()
397                 {
398                     @Override
399                     public int getStatusCode()
400                     {
401                         return Response.Status.BAD_REQUEST.getStatusCode();
402                     }
403 
404                     @Override
405                     public Response.Status.Family getFamily()
406                     {
407                         return Response.Status.BAD_REQUEST.getFamily();
408                     }
409 
410                     @Override
411                     public String getReasonPhrase()
412                     {
413                         return "your query return more than one artifact";
414                     }
415                 } ).build();
416             }
417 
418             // version is LATEST so we have to find the latest one from the result
419             if ( artifacts.size() > 1 && StringUtils.equals( version, LATEST_KEYWORD ) )
420             {
421                 TreeMap<String, Artifact> artifactPerVersion = new TreeMap<>( VersionComparator.getInstance() );
422 
423                 for ( Artifact artifact : artifacts )
424                 {
425                     artifactPerVersion.put( artifact.getVersion(), artifact );
426                 }
427 
428                 return Response.temporaryRedirect(
429                     new URI( artifactPerVersion.lastEntry().getValue().getUrl() ) ).build();
430 
431             }
432 
433             Artifact artifact = artifacts.get( 0 );
434 
435             return Response.temporaryRedirect( new URI( artifact.getUrl() ) ).build();
436         }
437         catch ( Exception e )
438         {
439             throw new ArchivaRestServiceException( e.getMessage(), e );
440         }
441     }
442 
443 
444     //-------------------------------------
445     // internal
446     //-------------------------------------
447     protected List<Artifact> getArtifacts( SearchResults searchResults )
448         throws ArchivaRestServiceException
449     {
450 
451         if ( searchResults == null || searchResults.isEmpty() )
452         {
453             return Collections.emptyList();
454         }
455         List<Artifact> artifacts = new ArrayList<>( searchResults.getReturnedHitsCount() );
456         for ( SearchResultHit hit : searchResults.getHits() )
457         {
458             // duplicate Artifact one per available version
459             if ( hit.getVersions().size() > 0 )
460             {
461                 for ( String version : hit.getVersions() )
462                 {
463 
464                     Artifact versionned = getModelMapper().map( hit, Artifact.class );
465 
466                     if ( StringUtils.isNotBlank( version ) )
467                     {
468                         versionned.setVersion( version );
469                         versionned.setUrl( getArtifactUrl( versionned ) );
470 
471                         artifacts.add( versionned );
472 
473                     }
474                 }
475             }
476         }
477         return artifacts;
478     }
479 
480 
481 }