This project has retired. For details please refer to its Attic page.
Source code
001package org.apache.archiva.rest.services;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 *
012 *   http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022import org.apache.archiva.common.utils.VersionComparator;
023import org.apache.archiva.indexer.search.RepositorySearch;
024import org.apache.archiva.indexer.search.RepositorySearchException;
025import org.apache.archiva.indexer.search.SearchFields;
026import org.apache.archiva.indexer.search.SearchResultHit;
027import org.apache.archiva.indexer.search.SearchResultLimits;
028import org.apache.archiva.indexer.search.SearchResults;
029import org.apache.archiva.maven2.model.Artifact;
030import org.apache.archiva.metadata.model.ArtifactMetadata;
031import org.apache.archiva.metadata.repository.MetadataRepository;
032import org.apache.archiva.metadata.repository.MetadataRepositoryException;
033import org.apache.archiva.metadata.repository.RepositorySession;
034import org.apache.archiva.metadata.repository.RepositorySessionFactory;
035import org.apache.archiva.rest.api.model.ChecksumSearch;
036import org.apache.archiva.rest.api.model.GroupIdList;
037import org.apache.archiva.rest.api.model.SearchRequest;
038import org.apache.archiva.rest.api.model.StringList;
039import org.apache.archiva.rest.api.services.ArchivaRestServiceException;
040import org.apache.archiva.rest.api.services.SearchService;
041import org.apache.commons.collections.ListUtils;
042import org.apache.commons.lang.StringUtils;
043import org.springframework.stereotype.Service;
044
045import javax.inject.Inject;
046import javax.ws.rs.core.Response;
047import java.net.URI;
048import java.util.ArrayList;
049import java.util.Arrays;
050import java.util.Collection;
051import java.util.Collections;
052import java.util.HashSet;
053import java.util.List;
054import java.util.Set;
055import java.util.TreeMap;
056
057/**
058 * @author Olivier Lamy
059 */
060@Service( "searchService#rest" )
061public class DefaultSearchService
062    extends AbstractRestService
063    implements SearchService
064{
065
066    private static final String LATEST_KEYWORD = "LATEST";
067
068    @Inject
069    private RepositorySearch repositorySearch;
070
071    @Inject
072    private RepositorySessionFactory repositorySessionFactory;
073
074    @Override
075    public List<Artifact> quickSearch( String queryString )
076        throws ArchivaRestServiceException
077    {
078        if ( StringUtils.isBlank( queryString ) )
079        {
080            return Collections.emptyList( );
081        }
082
083        SearchResultLimits limits = new SearchResultLimits( 0 );
084        try
085        {
086            SearchResults searchResults =
087                repositorySearch.search( getPrincipal( ), getObservableRepos( ), queryString, limits,
088                    Collections.<String>emptyList( ) );
089            return getArtifacts( searchResults );
090
091        }
092        catch ( RepositorySearchException e )
093        {
094            log.error( e.getMessage( ), e );
095            throw new ArchivaRestServiceException( e.getMessage( ), e );
096        }
097    }
098
099    @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, Boolean literalVersion )
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 ( literalVersion.booleanValue( ) || !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            log.debug( "Searching repository {}", repositoryId );
350            searchField.setExactSearch( true );
351            SearchResults searchResults = repositorySearch.search( getPrincipal( ), searchField, null );
352            List<Artifact> artifacts = getArtifacts( searchResults, repositoryId );
353
354            if ( artifacts.isEmpty( ) )
355            {
356                return Response.status( new Response.StatusType( )
357                {
358                    @Override
359                    public int getStatusCode( )
360                    {
361                        return Response.Status.NO_CONTENT.getStatusCode( );
362                    }
363
364                    @Override
365                    public Response.Status.Family getFamily( )
366                    {
367                        return Response.Status.NO_CONTENT.getFamily( );
368                    }
369
370                    @Override
371                    public String getReasonPhrase( )
372                    {
373                        return "your query doesn't return any artifact";
374                    }
375                } ).build( );
376            }
377
378            // TODO improve that with querying lucene with null value for classifier
379            // so simple loop and retain only artifact with null classifier
380            if ( classifier == null )
381            {
382                List<Artifact> filteredArtifacts = new ArrayList<>( artifacts.size( ) );
383                for ( Artifact artifact : artifacts )
384                {
385                    if ( artifact.getClassifier( ) == null )
386                    {
387                        filteredArtifacts.add( artifact );
388                    }
389                }
390
391                artifacts = filteredArtifacts;
392            }
393
394            // TODO return json result of the query ?
395            if ( artifacts.size( ) > 1 && ( literalVersion || !StringUtils.equals( version, LATEST_KEYWORD ) ) )
396            {
397                return Response.status( new Response.StatusType( )
398                {
399                    @Override
400                    public int getStatusCode( )
401                    {
402                        return Response.Status.BAD_REQUEST.getStatusCode( );
403                    }
404
405                    @Override
406                    public Response.Status.Family getFamily( )
407                    {
408                        return Response.Status.BAD_REQUEST.getFamily( );
409                    }
410
411                    @Override
412                    public String getReasonPhrase( )
413                    {
414                        return "your query return more than one artifact";
415                    }
416                } ).build( );
417            }
418
419            // version is LATEST so we have to find the latest one from the result
420            if ( !literalVersion && ( artifacts.size( ) > 1 && StringUtils.equals( version, LATEST_KEYWORD ) ) )
421            {
422                TreeMap<String, Artifact> artifactPerVersion = new TreeMap<>( VersionComparator.getInstance( ) );
423
424                for ( Artifact artifact : artifacts )
425                {
426                    artifactPerVersion.put( artifact.getVersion( ), artifact );
427                }
428
429                return Response.temporaryRedirect(
430                    new URI( artifactPerVersion.lastEntry( ).getValue( ).getUrl( ) ) ).build( );
431
432            }
433
434            Artifact artifact = artifacts.get( 0 );
435            log.debug( "Returning artifact {}, {}", artifact.getUrl( ), artifact.getRepositoryId( ) );
436            return Response.temporaryRedirect( new URI( artifact.getUrl( ) ) ).build( );
437        }
438        catch ( Exception e )
439        {
440            throw new ArchivaRestServiceException( e.getMessage( ), e );
441        }
442    }
443
444    protected List<Artifact> getArtifacts( SearchResults searchResults )
445           throws ArchivaRestServiceException
446    {
447        return getArtifacts( searchResults, null );
448    }
449
450    //-------------------------------------
451    // internal
452    //-------------------------------------
453    protected List<Artifact> getArtifacts( SearchResults searchResults, String repositoryId )
454        throws ArchivaRestServiceException
455    {
456
457        if ( searchResults == null || searchResults.isEmpty() )
458        {
459            return Collections.emptyList();
460        }
461        List<Artifact> artifacts = new ArrayList<>( searchResults.getReturnedHitsCount() );
462        for ( SearchResultHit hit : searchResults.getHits() )
463        {
464            // duplicate Artifact one per available version
465            if ( hit.getVersions().size() > 0 )
466            {
467                for ( String version : hit.getVersions() )
468                {
469
470                    Artifact versionned = getModelMapper().map( hit, Artifact.class );
471
472                    if ( StringUtils.isNotBlank( version ) )
473                    {
474                        versionned.setVersion( version );
475                        versionned.setUrl( getArtifactUrl( versionned, repositoryId ) );
476
477                        artifacts.add( versionned );
478
479                    }
480                }
481            }
482        }
483        return artifacts;
484    }
485
486
487}