package org.apache.archiva.rest.services; /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ import org.apache.archiva.common.utils.VersionComparator; import org.apache.archiva.indexer.search.RepositorySearch; import org.apache.archiva.indexer.search.RepositorySearchException; import org.apache.archiva.indexer.search.SearchFields; import org.apache.archiva.indexer.search.SearchResultHit; import org.apache.archiva.indexer.search.SearchResultLimits; import org.apache.archiva.indexer.search.SearchResults; import org.apache.archiva.maven2.model.Artifact; import org.apache.archiva.metadata.model.ArtifactMetadata; import org.apache.archiva.metadata.repository.MetadataRepository; import org.apache.archiva.metadata.repository.MetadataRepositoryException; import org.apache.archiva.metadata.repository.RepositorySession; import org.apache.archiva.metadata.repository.RepositorySessionFactory; import org.apache.archiva.rest.api.model.ChecksumSearch; import org.apache.archiva.rest.api.model.GroupIdList; import org.apache.archiva.rest.api.model.SearchRequest; import org.apache.archiva.rest.api.model.StringList; import org.apache.archiva.rest.api.services.ArchivaRestServiceException; import org.apache.archiva.rest.api.services.SearchService; import org.apache.commons.collections.ListUtils; import org.apache.commons.lang.StringUtils; import org.springframework.stereotype.Service; import javax.inject.Inject; import javax.ws.rs.core.Response; import java.net.URI; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.TreeMap; /** * @author Olivier Lamy */ @Service( "searchService#rest" ) public class DefaultSearchService extends AbstractRestService implements SearchService { private static final String LATEST_KEYWORD = "LATEST"; @Inject private RepositorySearch repositorySearch; @Inject private RepositorySessionFactory repositorySessionFactory; @Override public List<Artifact> quickSearch( String queryString ) throws ArchivaRestServiceException { if ( StringUtils.isBlank( queryString ) ) { return Collections.emptyList(); } SearchResultLimits limits = new SearchResultLimits( 0 ); try { SearchResults searchResults = repositorySearch.search( getPrincipal(), getObservableRepos(), queryString, limits, Collections.<String>emptyList() ); return getArtifacts( searchResults ); } catch ( RepositorySearchException e ) { log.error( e.getMessage(), e ); throw new ArchivaRestServiceException( e.getMessage(), e ); } } @Override public List<Artifact> quickSearchWithRepositories( SearchRequest searchRequest ) throws ArchivaRestServiceException { String queryString = searchRequest.getQueryTerms(); if ( StringUtils.isBlank( queryString ) ) { return Collections.emptyList(); } List<String> repositories = searchRequest.getRepositories(); if ( repositories == null || repositories.isEmpty() ) { repositories = getObservableRepos(); } SearchResultLimits limits = new SearchResultLimits( searchRequest.getPageSize(), searchRequest.getSelectedPage() ); try { SearchResults searchResults = repositorySearch.search( getPrincipal(), repositories, queryString, limits, Collections.<String>emptyList() ); return getArtifacts( searchResults ); } catch ( RepositorySearchException e ) { log.error( e.getMessage(), e ); throw new ArchivaRestServiceException( e.getMessage(), e ); } } @Override public List<Artifact> getArtifactVersions( String groupId, String artifactId, String packaging ) throws ArchivaRestServiceException { if ( StringUtils.isBlank( groupId ) || StringUtils.isBlank( artifactId ) ) { return Collections.emptyList(); } SearchFields searchField = new SearchFields(); searchField.setGroupId( groupId ); searchField.setArtifactId( artifactId ); searchField.setPackaging( StringUtils.isBlank( packaging ) ? "jar" : packaging ); searchField.setRepositories( getObservableRepos() ); try { SearchResults searchResults = repositorySearch.search( getPrincipal(), searchField, null ); return getArtifacts( searchResults ); } catch ( RepositorySearchException e ) { log.error( e.getMessage(), e ); throw new ArchivaRestServiceException( e.getMessage(), e ); } } @Override public List<Artifact> searchArtifacts( SearchRequest searchRequest ) throws ArchivaRestServiceException { if ( searchRequest == null ) { return Collections.emptyList(); } SearchFields searchField = getModelMapper().map( searchRequest, SearchFields.class ); SearchResultLimits limits = new SearchResultLimits( 0 ); limits.setPageSize( searchRequest.getPageSize() ); // if no repos set we use ones available for the user if ( searchField.getRepositories() == null || searchField.getRepositories().isEmpty() ) { searchField.setRepositories( getObservableRepos() ); } try { SearchResults searchResults = repositorySearch.search( getPrincipal(), searchField, limits ); return getArtifacts( searchResults ); } catch ( RepositorySearchException e ) { log.error( e.getMessage(), e ); throw new ArchivaRestServiceException( e.getMessage(), e ); } } @Override public GroupIdList getAllGroupIds( List<String> selectedRepos ) throws ArchivaRestServiceException { List<String> observableRepos = getObservableRepos(); List<String> repos = ListUtils.intersection( observableRepos, selectedRepos ); if ( repos == null || repos.isEmpty() ) { return new GroupIdList( Collections.<String>emptyList() ); } try { return new GroupIdList( new ArrayList<>( repositorySearch.getAllGroupIds( getPrincipal(), repos ) ) ); } catch ( RepositorySearchException e ) { log.error( e.getMessage(), e ); throw new ArchivaRestServiceException( e.getMessage(), e ); } } public List<Artifact> getArtifactByChecksum( ChecksumSearch checksumSearch ) throws ArchivaRestServiceException { // if no repos set we use ones available for the user if ( checksumSearch.getRepositories() == null || checksumSearch.getRepositories().isEmpty() ) { checksumSearch.setRepositories( getObservableRepos() ); } RepositorySession repositorySession = repositorySessionFactory.createSession(); MetadataRepository metadataRepository = repositorySession.getRepository(); Set<Artifact> artifactSet = new HashSet<>(); try { for ( String repoId : checksumSearch.getRepositories() ) { Collection<ArtifactMetadata> artifactMetadatas = metadataRepository.getArtifactsByChecksum( repoId, checksumSearch.getChecksum() ); artifactSet.addAll( buildArtifacts( artifactMetadatas, repoId ) ); } return new ArrayList<>( artifactSet ); } catch ( MetadataRepositoryException e ) { log.error( e.getMessage(), e ); throw new ArchivaRestServiceException( e.getMessage(), e ); } finally { repositorySession.closeQuietly(); } } @Override public StringList getObservablesRepoIds() throws ArchivaRestServiceException { return new StringList( getObservableRepos() ); } @Override public Response redirectToArtifactFile( String repositoryId, String groupId, String artifactId, String version, String packaging, String classifier ) throws ArchivaRestServiceException { try { // validate query if ( StringUtils.isEmpty( groupId ) ) { return Response.status( new Response.StatusType() { @Override public int getStatusCode() { return Response.Status.BAD_REQUEST.getStatusCode(); } @Override public Response.Status.Family getFamily() { return Response.Status.BAD_REQUEST.getFamily(); } @Override public String getReasonPhrase() { return "groupId mandatory"; } } ).build(); } if ( StringUtils.isEmpty( version ) ) { return Response.status( new Response.StatusType() { @Override public int getStatusCode() { return Response.Status.BAD_REQUEST.getStatusCode(); } @Override public Response.Status.Family getFamily() { return Response.Status.BAD_REQUEST.getFamily(); } @Override public String getReasonPhrase() { return "version mandatory"; } } ).build(); } if ( StringUtils.isEmpty( artifactId ) ) { return Response.status( new Response.StatusType() { @Override public int getStatusCode() { return Response.Status.BAD_REQUEST.getStatusCode(); } @Override public Response.Status.Family getFamily() { return Response.Status.BAD_REQUEST.getFamily(); } @Override public String getReasonPhrase() { return "artifactId mandatory"; } } ).build(); } SearchFields searchField = new SearchFields(); searchField.setGroupId( groupId ); searchField.setArtifactId( artifactId ); searchField.setPackaging( StringUtils.isBlank( packaging ) ? "jar" : packaging ); if ( !StringUtils.equals( version, LATEST_KEYWORD ) ) { searchField.setVersion( version ); } searchField.setClassifier( classifier ); List<String> userRepos = getObservablesRepoIds().getStrings(); searchField.setRepositories( StringUtils.isEmpty( repositoryId ) ? userRepos : Arrays.asList( repositoryId ) ); searchField.setExactSearch( true ); SearchResults searchResults = repositorySearch.search( getPrincipal(), searchField, null ); List<Artifact> artifacts = getArtifacts( searchResults ); if ( artifacts.isEmpty() ) { return Response.status( new Response.StatusType() { @Override public int getStatusCode() { return Response.Status.NO_CONTENT.getStatusCode(); } @Override public Response.Status.Family getFamily() { return Response.Status.NO_CONTENT.getFamily(); } @Override public String getReasonPhrase() { return "your query doesn't return any artifact"; } } ).build(); } // TODO improve that with querying lucene with null value for classifier // so simple loop and retain only artifact with null classifier if ( classifier == null ) { List<Artifact> filteredArtifacts = new ArrayList<>( artifacts.size() ); for ( Artifact artifact : artifacts ) { if ( artifact.getClassifier() == null ) { filteredArtifacts.add( artifact ); } } artifacts = filteredArtifacts; } // TODO return json result of the query ? if ( artifacts.size() > 1 && !StringUtils.equals( version, LATEST_KEYWORD ) ) { return Response.status( new Response.StatusType() { @Override public int getStatusCode() { return Response.Status.BAD_REQUEST.getStatusCode(); } @Override public Response.Status.Family getFamily() { return Response.Status.BAD_REQUEST.getFamily(); } @Override public String getReasonPhrase() { return "your query return more than one artifact"; } } ).build(); } // version is LATEST so we have to find the latest one from the result if ( artifacts.size() > 1 && StringUtils.equals( version, LATEST_KEYWORD ) ) { TreeMap<String, Artifact> artifactPerVersion = new TreeMap<>( VersionComparator.getInstance() ); for ( Artifact artifact : artifacts ) { artifactPerVersion.put( artifact.getVersion(), artifact ); } return Response.temporaryRedirect( new URI( artifactPerVersion.lastEntry().getValue().getUrl() ) ).build(); } Artifact artifact = artifacts.get( 0 ); return Response.temporaryRedirect( new URI( artifact.getUrl() ) ).build(); } catch ( Exception e ) { throw new ArchivaRestServiceException( e.getMessage(), e ); } } //------------------------------------- // internal //------------------------------------- protected List<Artifact> getArtifacts( SearchResults searchResults ) throws ArchivaRestServiceException { if ( searchResults == null || searchResults.isEmpty() ) { return Collections.emptyList(); } List<Artifact> artifacts = new ArrayList<>( searchResults.getReturnedHitsCount() ); for ( SearchResultHit hit : searchResults.getHits() ) { // duplicate Artifact one per available version if ( hit.getVersions().size() > 0 ) { for ( String version : hit.getVersions() ) { Artifact versionned = getModelMapper().map( hit, Artifact.class ); if ( StringUtils.isNotBlank( version ) ) { versionned.setVersion( version ); versionned.setUrl( getArtifactUrl( versionned ) ); artifacts.add( versionned ); } } } } return artifacts; } }