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.admin.model.RepositoryAdminException; import org.apache.archiva.admin.model.admin.ArchivaAdministration; import org.apache.archiva.admin.model.beans.ManagedRepository; import org.apache.archiva.admin.model.managed.ManagedRepositoryAdmin; import org.apache.archiva.metadata.model.facets.AuditEvent; import org.apache.archiva.checksum.ChecksumAlgorithm; import org.apache.archiva.checksum.ChecksummedFile; import org.apache.archiva.common.plexusbridge.MavenIndexerUtils; import org.apache.archiva.common.plexusbridge.PlexusSisuBridge; import org.apache.archiva.common.utils.VersionComparator; import org.apache.archiva.common.utils.VersionUtil; import org.apache.archiva.maven2.metadata.MavenMetadataReader; import org.apache.archiva.maven2.model.Artifact; import org.apache.archiva.metadata.model.ArtifactMetadata; import org.apache.archiva.metadata.model.maven2.MavenArtifactFacet; import org.apache.archiva.metadata.repository.MetadataRepository; import org.apache.archiva.metadata.repository.MetadataRepositoryException; import org.apache.archiva.metadata.repository.MetadataResolutionException; import org.apache.archiva.metadata.repository.RepositorySession; import org.apache.archiva.metadata.repository.RepositorySessionFactory; import org.apache.archiva.model.ArchivaRepositoryMetadata; import org.apache.archiva.model.ArtifactReference; import org.apache.archiva.model.VersionedReference; import org.apache.archiva.redback.authentication.AuthenticationResult; import org.apache.archiva.redback.authorization.AuthorizationException; import org.apache.archiva.redback.components.cache.Cache; import org.apache.archiva.redback.components.taskqueue.TaskQueueException; import org.apache.archiva.redback.system.DefaultSecuritySession; import org.apache.archiva.redback.system.SecuritySession; import org.apache.archiva.redback.system.SecuritySystem; import org.apache.archiva.redback.users.User; import org.apache.archiva.redback.users.UserManagerException; import org.apache.archiva.redback.users.UserNotFoundException; import org.apache.archiva.repository.ContentNotFoundException; import org.apache.archiva.repository.ManagedRepositoryContent; import org.apache.archiva.repository.RepositoryContentFactory; import org.apache.archiva.repository.RepositoryException; import org.apache.archiva.repository.RepositoryNotFoundException; import org.apache.archiva.repository.events.RepositoryListener; import org.apache.archiva.repository.metadata.MetadataTools; import org.apache.archiva.repository.metadata.RepositoryMetadataException; import org.apache.archiva.repository.metadata.RepositoryMetadataWriter; import org.apache.archiva.repository.scanner.RepositoryScanStatistics; import org.apache.archiva.repository.scanner.RepositoryScanner; import org.apache.archiva.repository.scanner.RepositoryScannerException; import org.apache.archiva.repository.scanner.RepositoryScannerInstance; import org.apache.archiva.rest.api.model.ArtifactTransferRequest; import org.apache.archiva.rest.api.model.StringList; import org.apache.archiva.rest.api.services.ArchivaRestServiceException; import org.apache.archiva.rest.api.services.RepositoriesService; import org.apache.archiva.scheduler.ArchivaTaskScheduler; import org.apache.archiva.scheduler.indexing.ArchivaIndexingTaskExecutor; import org.apache.archiva.scheduler.indexing.ArtifactIndexingTask; import org.apache.archiva.scheduler.indexing.DownloadRemoteIndexException; import org.apache.archiva.scheduler.indexing.DownloadRemoteIndexScheduler; import org.apache.archiva.scheduler.repository.model.RepositoryTask; import org.apache.archiva.security.ArchivaSecurityException; import org.apache.archiva.security.common.ArchivaRoleConstants; import org.apache.archiva.xml.XMLException; import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang.StringUtils; import org.apache.maven.index.context.IndexingContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import javax.inject.Inject; import javax.inject.Named; import javax.ws.rs.core.Response; import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.StandardCopyOption; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Set; import java.util.TimeZone; /** * @author Olivier Lamy * @since 1.4-M1 */ @Service("repositoriesService#rest") public class DefaultRepositoriesService extends AbstractRestService implements RepositoriesService { private Logger log = LoggerFactory.getLogger( getClass() ); @Inject @Named(value = "taskExecutor#indexing") private ArchivaIndexingTaskExecutor archivaIndexingTaskExecutor; @Inject private ManagedRepositoryAdmin managedRepositoryAdmin; @Inject private PlexusSisuBridge plexusSisuBridge; @Inject private MavenIndexerUtils mavenIndexerUtils; @Inject private SecuritySystem securitySystem; @Inject private RepositoryContentFactory repositoryFactory; @Inject @Named(value = "archivaTaskScheduler#repository") private ArchivaTaskScheduler scheduler; @Inject private DownloadRemoteIndexScheduler downloadRemoteIndexScheduler; @Inject @Named(value = "repositorySessionFactory") protected RepositorySessionFactory repositorySessionFactory; @Inject @Autowired(required = false) protected List<RepositoryListener> listeners = new ArrayList<RepositoryListener>(); @Inject private RepositoryScanner repoScanner; /** * Cache used for namespaces */ @Inject @Named(value = "cache#namespaces") private Cache<String, Collection<String>> namespacesCache; private ChecksumAlgorithm[] algorithms = new ChecksumAlgorithm[]{ ChecksumAlgorithm.SHA1, ChecksumAlgorithm.MD5 }; @Override public Boolean scanRepository( String repositoryId, boolean fullScan ) { return doScanRepository( repositoryId, fullScan ); } @Override public Boolean alreadyScanning( String repositoryId ) { // check queue first to make sure it doesn't get dequeued between calls if ( repositoryTaskScheduler.isProcessingRepositoryTask( repositoryId ) ) { return true; } for ( RepositoryScannerInstance scan : repoScanner.getInProgressScans() ) { if ( scan.getRepository().getId().equals( repositoryId ) ) { return true; } } return false; } @Override public Boolean removeScanningTaskFromQueue( String repositoryId ) { RepositoryTask task = new RepositoryTask(); task.setRepositoryId( repositoryId ); try { return repositoryTaskScheduler.unQueueTask( task ); } catch ( TaskQueueException e ) { log.error( "failed to unschedule scanning of repo with id {}", repositoryId, e ); return false; } } @Override public Boolean scanRepositoryNow( String repositoryId, boolean fullScan ) throws ArchivaRestServiceException { try { ManagedRepository repository = managedRepositoryAdmin.getManagedRepository( repositoryId ); IndexingContext context = managedRepositoryAdmin.createIndexContext( repository ); ArtifactIndexingTask task = new ArtifactIndexingTask( repository, null, ArtifactIndexingTask.Action.FINISH, context ); task.setExecuteOnEntireRepo( true ); task.setOnlyUpdate( !fullScan ); archivaIndexingTaskExecutor.executeTask( task ); scheduler.queueTask( new RepositoryTask( repositoryId, fullScan ) ); return Boolean.TRUE; } catch ( Exception e ) { log.error( e.getMessage(), e ); throw new ArchivaRestServiceException( e.getMessage(), e ); } } @Override public Boolean scheduleDownloadRemoteIndex( String repositoryId, boolean now, boolean fullDownload ) throws ArchivaRestServiceException { try { downloadRemoteIndexScheduler.scheduleDownloadRemote( repositoryId, now, fullDownload ); } catch ( DownloadRemoteIndexException e ) { log.error( e.getMessage(), e ); throw new ArchivaRestServiceException( e.getMessage(), e ); } return Boolean.TRUE; } @Override public Boolean copyArtifact( ArtifactTransferRequest artifactTransferRequest ) throws ArchivaRestServiceException { // check parameters String userName = getAuditInformation().getUser().getUsername(); if ( StringUtils.isBlank( userName ) ) { throw new ArchivaRestServiceException( "copyArtifact call: userName not found", null ); } if ( StringUtils.isBlank( artifactTransferRequest.getRepositoryId() ) ) { throw new ArchivaRestServiceException( "copyArtifact call: sourceRepositoryId cannot be null", null ); } if ( StringUtils.isBlank( artifactTransferRequest.getTargetRepositoryId() ) ) { throw new ArchivaRestServiceException( "copyArtifact call: targetRepositoryId cannot be null", null ); } ManagedRepository source = null; try { source = managedRepositoryAdmin.getManagedRepository( artifactTransferRequest.getRepositoryId() ); } catch ( RepositoryAdminException e ) { throw new ArchivaRestServiceException( e.getMessage(), e ); } if ( source == null ) { throw new ArchivaRestServiceException( "cannot find repository with id " + artifactTransferRequest.getRepositoryId(), null ); } ManagedRepository target = null; try { target = managedRepositoryAdmin.getManagedRepository( artifactTransferRequest.getTargetRepositoryId() ); } catch ( RepositoryAdminException e ) { throw new ArchivaRestServiceException( e.getMessage(), e ); } if ( target == null ) { throw new ArchivaRestServiceException( "cannot find repository with id " + artifactTransferRequest.getTargetRepositoryId(), null ); } if ( StringUtils.isBlank( artifactTransferRequest.getGroupId() ) ) { throw new ArchivaRestServiceException( "groupId is mandatory", null ); } if ( StringUtils.isBlank( artifactTransferRequest.getArtifactId() ) ) { throw new ArchivaRestServiceException( "artifactId is mandatory", null ); } if ( StringUtils.isBlank( artifactTransferRequest.getVersion() ) ) { throw new ArchivaRestServiceException( "version is mandatory", null ); } if ( VersionUtil.isSnapshot( artifactTransferRequest.getVersion() ) ) { throw new ArchivaRestServiceException( "copy of SNAPSHOT not supported", null ); } // end check parameters User user = null; try { user = securitySystem.getUserManager().findUser( userName ); } catch ( UserNotFoundException e ) { throw new ArchivaRestServiceException( "user " + userName + " not found", e ); } catch ( UserManagerException e ) { throw new ArchivaRestServiceException( "ArchivaRestServiceException:" + e.getMessage(), e ); } // check karma on source : read AuthenticationResult authn = new AuthenticationResult( true, userName, null ); SecuritySession securitySession = new DefaultSecuritySession( authn, user ); try { boolean authz = securitySystem.isAuthorized( securitySession, ArchivaRoleConstants.OPERATION_REPOSITORY_ACCESS, artifactTransferRequest.getRepositoryId() ); if ( !authz ) { throw new ArchivaRestServiceException( "not authorized to access repo:" + artifactTransferRequest.getRepositoryId(), null ); } } catch ( AuthorizationException e ) { log.error( "error reading permission: " + e.getMessage(), e ); throw new ArchivaRestServiceException( e.getMessage(), e ); } // check karma on target: write try { boolean authz = securitySystem.isAuthorized( securitySession, ArchivaRoleConstants.OPERATION_REPOSITORY_UPLOAD, artifactTransferRequest.getTargetRepositoryId() ); if ( !authz ) { throw new ArchivaRestServiceException( "not authorized to write to repo:" + artifactTransferRequest.getTargetRepositoryId(), null ); } } catch ( AuthorizationException e ) { log.error( "error reading permission: " + e.getMessage(), e ); throw new ArchivaRestServiceException( e.getMessage(), e ); } // sounds good we can continue ! ArtifactReference artifactReference = new ArtifactReference(); artifactReference.setArtifactId( artifactTransferRequest.getArtifactId() ); artifactReference.setGroupId( artifactTransferRequest.getGroupId() ); artifactReference.setVersion( artifactTransferRequest.getVersion() ); artifactReference.setClassifier( artifactTransferRequest.getClassifier() ); String packaging = StringUtils.trim( artifactTransferRequest.getPackaging() ); artifactReference.setType( StringUtils.isEmpty( packaging ) ? "jar" : packaging ); try { ManagedRepositoryContent sourceRepository = repositoryFactory.getManagedRepositoryContent( artifactTransferRequest.getRepositoryId() ); String artifactSourcePath = sourceRepository.toPath( artifactReference ); if ( StringUtils.isEmpty( artifactSourcePath ) ) { log.error( "cannot find artifact " + artifactTransferRequest.toString() ); throw new ArchivaRestServiceException( "cannot find artifact " + artifactTransferRequest.toString(), null ); } File artifactFile = new File( source.getLocation(), artifactSourcePath ); if ( !artifactFile.exists() ) { log.error( "cannot find artifact " + artifactTransferRequest.toString() ); throw new ArchivaRestServiceException( "cannot find artifact " + artifactTransferRequest.toString(), null ); } ManagedRepositoryContent targetRepository = repositoryFactory.getManagedRepositoryContent( artifactTransferRequest.getTargetRepositoryId() ); String artifactPath = targetRepository.toPath( artifactReference ); int lastIndex = artifactPath.lastIndexOf( '/' ); String path = artifactPath.substring( 0, lastIndex ); File targetPath = new File( target.getLocation(), path ); Date lastUpdatedTimestamp = Calendar.getInstance().getTime(); int newBuildNumber = 1; String timestamp = null; File versionMetadataFile = new File( targetPath, MetadataTools.MAVEN_METADATA ); /* unused */ getMetadata( versionMetadataFile ); if ( !targetPath.exists() ) { targetPath.mkdirs(); } String filename = artifactPath.substring( lastIndex + 1 ); boolean fixChecksums = !( archivaAdministration.getKnownContentConsumers().contains( "create-missing-checksums" ) ); File targetFile = new File( targetPath, filename ); if ( targetFile.exists() && target.isBlockRedeployments() ) { throw new ArchivaRestServiceException( "artifact already exists in target repo: " + artifactTransferRequest.getTargetRepositoryId() + " and redeployment blocked", null ); } else { copyFile( artifactFile, targetPath, filename, fixChecksums ); queueRepositoryTask( target.getId(), targetFile ); } // copy source pom to target repo String pomFilename = filename; if ( StringUtils.isNotBlank( artifactTransferRequest.getClassifier() ) ) { pomFilename = StringUtils.remove( pomFilename, "-" + artifactTransferRequest.getClassifier() ); } pomFilename = FilenameUtils.removeExtension( pomFilename ) + ".pom"; File pomFile = new File( new File( source.getLocation(), artifactSourcePath.substring( 0, artifactPath.lastIndexOf( '/' ) ) ), pomFilename ); if ( pomFile != null && pomFile.length() > 0 ) { copyFile( pomFile, targetPath, pomFilename, fixChecksums ); queueRepositoryTask( target.getId(), new File( targetPath, pomFilename ) ); } // explicitly update only if metadata-updater consumer is not enabled! if ( !archivaAdministration.getKnownContentConsumers().contains( "metadata-updater" ) ) { updateProjectMetadata( targetPath.getAbsolutePath(), lastUpdatedTimestamp, timestamp, newBuildNumber, fixChecksums, artifactTransferRequest ); } String msg = "Artifact \'" + artifactTransferRequest.getGroupId() + ":" + artifactTransferRequest.getArtifactId() + ":" + artifactTransferRequest.getVersion() + "\' was successfully deployed to repository \'" + artifactTransferRequest.getTargetRepositoryId() + "\'"; log.debug("copyArtifact {}", msg); } catch ( RepositoryException e ) { log.error( "RepositoryException: " + e.getMessage(), e ); throw new ArchivaRestServiceException( e.getMessage(), e ); } catch ( RepositoryAdminException e ) { log.error( "RepositoryAdminException: " + e.getMessage(), e ); throw new ArchivaRestServiceException( e.getMessage(), e ); } catch ( IOException e ) { log.error( "IOException: " + e.getMessage(), e ); throw new ArchivaRestServiceException( e.getMessage(), e ); } return true; } private void queueRepositoryTask( String repositoryId, File localFile ) { RepositoryTask task = new RepositoryTask(); task.setRepositoryId( repositoryId ); task.setResourceFile( localFile ); task.setUpdateRelatedArtifacts( true ); //task.setScanAll( true ); try { scheduler.queueTask( task ); } catch ( TaskQueueException e ) { log.error( "Unable to queue repository task to execute consumers on resource file ['" + localFile.getName() + "']." ); } } private ArchivaRepositoryMetadata getMetadata( File metadataFile ) throws RepositoryMetadataException { ArchivaRepositoryMetadata metadata = new ArchivaRepositoryMetadata(); if ( metadataFile.exists() ) { try { metadata = MavenMetadataReader.read( metadataFile ); } catch ( XMLException e ) { throw new RepositoryMetadataException( e.getMessage(), e ); } } return metadata; } private File getMetadata( String targetPath ) { String artifactPath = targetPath.substring( 0, targetPath.lastIndexOf( File.separatorChar ) ); return new File( artifactPath, MetadataTools.MAVEN_METADATA ); } private void copyFile( File sourceFile, File targetPath, String targetFilename, boolean fixChecksums ) throws IOException { Files.copy( sourceFile.toPath(), new File( targetPath, targetFilename ).toPath(), StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.COPY_ATTRIBUTES ); if ( fixChecksums ) { fixChecksums( new File( targetPath, targetFilename ) ); } } private void fixChecksums( File file ) { ChecksummedFile checksum = new ChecksummedFile( file ); checksum.fixChecksums( algorithms ); } private void updateProjectMetadata( String targetPath, Date lastUpdatedTimestamp, String timestamp, int buildNumber, boolean fixChecksums, ArtifactTransferRequest artifactTransferRequest ) throws RepositoryMetadataException { List<String> availableVersions = new ArrayList<>(); String latestVersion = artifactTransferRequest.getVersion(); File projectDir = new File( targetPath ).getParentFile(); File projectMetadataFile = new File( projectDir, MetadataTools.MAVEN_METADATA ); ArchivaRepositoryMetadata projectMetadata = getMetadata( projectMetadataFile ); if ( projectMetadataFile.exists() ) { availableVersions = projectMetadata.getAvailableVersions(); Collections.sort( availableVersions, VersionComparator.getInstance() ); if ( !availableVersions.contains( artifactTransferRequest.getVersion() ) ) { availableVersions.add( artifactTransferRequest.getVersion() ); } latestVersion = availableVersions.get( availableVersions.size() - 1 ); } else { availableVersions.add( artifactTransferRequest.getVersion() ); projectMetadata.setGroupId( artifactTransferRequest.getGroupId() ); projectMetadata.setArtifactId( artifactTransferRequest.getArtifactId() ); } if ( projectMetadata.getGroupId() == null ) { projectMetadata.setGroupId( artifactTransferRequest.getGroupId() ); } if ( projectMetadata.getArtifactId() == null ) { projectMetadata.setArtifactId( artifactTransferRequest.getArtifactId() ); } projectMetadata.setLatestVersion( latestVersion ); projectMetadata.setLastUpdatedTimestamp( lastUpdatedTimestamp ); projectMetadata.setAvailableVersions( availableVersions ); if ( !VersionUtil.isSnapshot( artifactTransferRequest.getVersion() ) ) { projectMetadata.setReleasedVersion( latestVersion ); } RepositoryMetadataWriter.write( projectMetadata, projectMetadataFile ); if ( fixChecksums ) { fixChecksums( projectMetadataFile ); } } @Override public Boolean removeProjectVersion( String repositoryId, String namespace, String projectId, String version ) throws ArchivaRestServiceException { // if not a generic we can use the standard way to delete artifact if ( !VersionUtil.isGenericSnapshot( version ) ) { Artifact artifact = new Artifact( namespace, projectId, version ); artifact.setRepositoryId( repositoryId ); artifact.setContext( repositoryId ); return deleteArtifact( artifact ); } if ( StringUtils.isEmpty( repositoryId ) ) { throw new ArchivaRestServiceException( "repositoryId cannot be null", 400, null ); } if ( !isAuthorizedToDeleteArtifacts( repositoryId ) ) { throw new ArchivaRestServiceException( "not authorized to delete artifacts", 403, null ); } if ( StringUtils.isEmpty( namespace ) ) { throw new ArchivaRestServiceException( "groupId cannot be null", 400, null ); } if ( StringUtils.isEmpty( projectId ) ) { throw new ArchivaRestServiceException( "artifactId cannot be null", 400, null ); } if ( StringUtils.isEmpty( version ) ) { throw new ArchivaRestServiceException( "version cannot be null", 400, null ); } RepositorySession repositorySession = repositorySessionFactory.createSession(); try { ManagedRepositoryContent repository = repositoryFactory.getManagedRepositoryContent( repositoryId ); VersionedReference ref = new VersionedReference(); ref.setArtifactId( projectId ); ref.setGroupId( namespace ); ref.setVersion( version ); repository.deleteVersion( ref ); /* ProjectReference projectReference = new ProjectReference(); projectReference.setGroupId( namespace ); projectReference.setArtifactId( projectId ); repository.getVersions( ) */ ArtifactReference artifactReference = new ArtifactReference(); artifactReference.setGroupId( namespace ); artifactReference.setArtifactId( projectId ); artifactReference.setVersion( version ); MetadataRepository metadataRepository = repositorySession.getRepository(); Set<ArtifactReference> related = repository.getRelatedArtifacts( artifactReference ); log.debug( "related: {}", related ); for ( ArtifactReference artifactRef : related ) { repository.deleteArtifact( artifactRef ); } Collection<ArtifactMetadata> artifacts = metadataRepository.getArtifacts( repositoryId, namespace, projectId, version ); for ( ArtifactMetadata artifactMetadata : artifacts ) { metadataRepository.removeArtifact( artifactMetadata, version ); } metadataRepository.removeProjectVersion( repositoryId, namespace, projectId, version ); } catch ( MetadataRepositoryException e ) { throw new ArchivaRestServiceException( "Repository exception: " + e.getMessage(), 500, e ); } catch ( MetadataResolutionException e ) { throw new ArchivaRestServiceException( "Repository exception: " + e.getMessage(), 500, e ); } catch ( RepositoryException e ) { throw new ArchivaRestServiceException( "Repository exception: " + e.getMessage(), 500, e ); } finally { repositorySession.save(); repositorySession.close(); } return Boolean.TRUE; } @Override public Boolean deleteArtifact( Artifact artifact ) throws ArchivaRestServiceException { String repositoryId = artifact.getContext(); // some rest call can use context or repositoryId // so try both!! if ( StringUtils.isEmpty( repositoryId ) ) { repositoryId = artifact.getRepositoryId(); } if ( StringUtils.isEmpty( repositoryId ) ) { throw new ArchivaRestServiceException( "repositoryId cannot be null", 400, null ); } if ( !isAuthorizedToDeleteArtifacts( repositoryId ) ) { throw new ArchivaRestServiceException( "not authorized to delete artifacts", 403, null ); } if ( artifact == null ) { throw new ArchivaRestServiceException( "artifact cannot be null", 400, null ); } if ( StringUtils.isEmpty( artifact.getGroupId() ) ) { throw new ArchivaRestServiceException( "artifact.groupId cannot be null", 400, null ); } if ( StringUtils.isEmpty( artifact.getArtifactId() ) ) { throw new ArchivaRestServiceException( "artifact.artifactId cannot be null", 400, null ); } // TODO more control on artifact fields boolean snapshotVersion = VersionUtil.isSnapshot( artifact.getVersion() ) | VersionUtil.isGenericSnapshot( artifact.getVersion() ); RepositorySession repositorySession = repositorySessionFactory.createSession(); try { Date lastUpdatedTimestamp = Calendar.getInstance().getTime(); TimeZone timezone = TimeZone.getTimeZone( "UTC" ); DateFormat fmt = new SimpleDateFormat( "yyyyMMdd.HHmmss" ); fmt.setTimeZone( timezone ); ManagedRepository repoConfig = managedRepositoryAdmin.getManagedRepository( repositoryId ); VersionedReference ref = new VersionedReference(); ref.setArtifactId( artifact.getArtifactId() ); ref.setGroupId( artifact.getGroupId() ); ref.setVersion( artifact.getVersion() ); ManagedRepositoryContent repository = repositoryFactory.getManagedRepositoryContent( repositoryId ); ArtifactReference artifactReference = new ArtifactReference(); artifactReference.setArtifactId( artifact.getArtifactId() ); artifactReference.setGroupId( artifact.getGroupId() ); artifactReference.setVersion( artifact.getVersion() ); artifactReference.setClassifier( artifact.getClassifier() ); artifactReference.setType( artifact.getPackaging() ); MetadataRepository metadataRepository = repositorySession.getRepository(); String path = repository.toMetadataPath( ref ); if ( StringUtils.isNotBlank( artifact.getClassifier() ) ) { if ( StringUtils.isBlank( artifact.getPackaging() ) ) { throw new ArchivaRestServiceException( "You must configure a type/packaging when using classifier", 400, null ); } repository.deleteArtifact( artifactReference ); } else { int index = path.lastIndexOf( '/' ); path = path.substring( 0, index ); File targetPath = new File( repoConfig.getLocation(), path ); if ( !targetPath.exists() ) { //throw new ContentNotFoundException( // artifact.getGroupId() + ":" + artifact.getArtifactId() + ":" + artifact.getVersion() ); log.warn( "targetPath {} not found skip file deletion", targetPath ); } // TODO: this should be in the storage mechanism so that it is all tied together // delete from file system if ( !snapshotVersion ) { repository.deleteVersion( ref ); } else { Set<ArtifactReference> related = repository.getRelatedArtifacts( artifactReference ); log.debug( "related: {}", related ); for ( ArtifactReference artifactRef : related ) { repository.deleteArtifact( artifactRef ); } } File metadataFile = getMetadata( targetPath.getAbsolutePath() ); ArchivaRepositoryMetadata metadata = getMetadata( metadataFile ); updateMetadata( metadata, metadataFile, lastUpdatedTimestamp, artifact ); } Collection<ArtifactMetadata> artifacts = Collections.emptyList(); if ( snapshotVersion ) { String baseVersion = VersionUtil.getBaseVersion( artifact.getVersion() ); artifacts = metadataRepository.getArtifacts( repositoryId, artifact.getGroupId(), artifact.getArtifactId(), baseVersion ); } else { artifacts = metadataRepository.getArtifacts( repositoryId, artifact.getGroupId(), artifact.getArtifactId(), artifact.getVersion() ); } log.debug( "artifacts: {}", artifacts ); if ( artifacts.isEmpty() ) { if ( !snapshotVersion ) { // verify metata repository doesn't contains anymore the version Collection<String> projectVersions = metadataRepository.getProjectVersions( repositoryId, artifact.getGroupId(), artifact.getArtifactId() ); if ( projectVersions.contains( artifact.getVersion() ) ) { log.warn( "artifact not found when deleted but version still here ! so force cleanup" ); metadataRepository.removeProjectVersion( repositoryId, artifact.getGroupId(), artifact.getArtifactId(), artifact.getVersion() ); } } } for ( ArtifactMetadata artifactMetadata : artifacts ) { // TODO: mismatch between artifact (snapshot) version and project (base) version here if ( artifactMetadata.getVersion().equals( artifact.getVersion() ) ) { if ( StringUtils.isNotBlank( artifact.getClassifier() ) ) { if ( StringUtils.isBlank( artifact.getPackaging() ) ) { throw new ArchivaRestServiceException( "You must configure a type/packaging when using classifier", 400, null ); } // cleanup facet which contains classifier information MavenArtifactFacet mavenArtifactFacet = (MavenArtifactFacet) artifactMetadata.getFacet( MavenArtifactFacet.FACET_ID ); if ( StringUtils.equals( artifact.getClassifier(), mavenArtifactFacet.getClassifier() ) ) { artifactMetadata.removeFacet( MavenArtifactFacet.FACET_ID ); String groupId = artifact.getGroupId(), artifactId = artifact.getArtifactId(), version = artifact.getVersion(); MavenArtifactFacet mavenArtifactFacetToCompare = new MavenArtifactFacet(); mavenArtifactFacetToCompare.setClassifier( artifact.getClassifier() ); metadataRepository.removeArtifact( repositoryId, groupId, artifactId, version, mavenArtifactFacetToCompare ); metadataRepository.save(); } } else { if ( snapshotVersion ) { metadataRepository.removeArtifact( artifactMetadata, VersionUtil.getBaseVersion( artifact.getVersion() ) ); } else { metadataRepository.removeArtifact( artifactMetadata.getRepositoryId(), artifactMetadata.getNamespace(), artifactMetadata.getProject(), artifact.getVersion(), artifactMetadata.getId() ); } } // TODO: move into the metadata repository proper - need to differentiate attachment of // repository metadata to an artifact for ( RepositoryListener listener : listeners ) { listener.deleteArtifact( metadataRepository, repository.getId(), artifactMetadata.getNamespace(), artifactMetadata.getProject(), artifactMetadata.getVersion(), artifactMetadata.getId() ); } triggerAuditEvent( repositoryId, path, AuditEvent.REMOVE_FILE ); } } } catch ( ContentNotFoundException e ) { throw new ArchivaRestServiceException( "Artifact does not exist: " + e.getMessage(), 400, e ); } catch ( RepositoryNotFoundException e ) { throw new ArchivaRestServiceException( "Target repository cannot be found: " + e.getMessage(), 400, e ); } catch ( RepositoryException e ) { throw new ArchivaRestServiceException( "Repository exception: " + e.getMessage(), 500, e ); } catch ( MetadataResolutionException e ) { throw new ArchivaRestServiceException( "Repository exception: " + e.getMessage(), 500, e ); } catch ( MetadataRepositoryException e ) { throw new ArchivaRestServiceException( "Repository exception: " + e.getMessage(), 500, e ); } catch ( RepositoryAdminException e ) { throw new ArchivaRestServiceException( "RepositoryAdmin exception: " + e.getMessage(), 500, e ); } finally { repositorySession.save(); repositorySession.close(); } return Boolean.TRUE; } @Override public Boolean deleteGroupId( String groupId, String repositoryId ) throws ArchivaRestServiceException { if ( StringUtils.isEmpty( repositoryId ) ) { throw new ArchivaRestServiceException( "repositoryId cannot be null", 400, null ); } if ( !isAuthorizedToDeleteArtifacts( repositoryId ) ) { throw new ArchivaRestServiceException( "not authorized to delete artifacts", 403, null ); } if ( StringUtils.isEmpty( groupId ) ) { throw new ArchivaRestServiceException( "groupId cannot be null", 400, null ); } RepositorySession repositorySession = repositorySessionFactory.createSession(); try { ManagedRepositoryContent repository = repositoryFactory.getManagedRepositoryContent( repositoryId ); repository.deleteGroupId( groupId ); MetadataRepository metadataRepository = repositorySession.getRepository(); metadataRepository.removeNamespace( repositoryId, groupId ); // just invalidate cache entry String cacheKey = repositoryId + "-" + groupId; namespacesCache.remove( cacheKey ); namespacesCache.remove( repositoryId ); metadataRepository.save(); } catch ( MetadataRepositoryException e ) { log.error( e.getMessage(), e ); throw new ArchivaRestServiceException( "Repository exception: " + e.getMessage(), 500, e ); } catch ( RepositoryException e ) { log.error( e.getMessage(), e ); throw new ArchivaRestServiceException( "Repository exception: " + e.getMessage(), 500, e ); } finally { repositorySession.close(); } return true; } @Override public Boolean deleteProject( String groupId, String projectId, String repositoryId ) throws ArchivaRestServiceException { if ( StringUtils.isEmpty( repositoryId ) ) { throw new ArchivaRestServiceException( "repositoryId cannot be null", 400, null ); } if ( !isAuthorizedToDeleteArtifacts( repositoryId ) ) { throw new ArchivaRestServiceException( "not authorized to delete artifacts", 403, null ); } if ( StringUtils.isEmpty( groupId ) ) { throw new ArchivaRestServiceException( "groupId cannot be null", 400, null ); } if ( StringUtils.isEmpty( projectId ) ) { throw new ArchivaRestServiceException( "artifactId cannot be null", 400, null ); } RepositorySession repositorySession = repositorySessionFactory.createSession(); try { ManagedRepositoryContent repository = repositoryFactory.getManagedRepositoryContent( repositoryId ); repository.deleteProject( groupId, projectId ); } catch ( ContentNotFoundException e ) { log.warn( "skip ContentNotFoundException: {}", e.getMessage() ); } catch ( RepositoryException e ) { log.error( e.getMessage(), e ); throw new ArchivaRestServiceException( "Repository exception: " + e.getMessage(), 500, e ); } try { MetadataRepository metadataRepository = repositorySession.getRepository(); metadataRepository.removeProject( repositoryId, groupId, projectId ); metadataRepository.save(); } catch ( MetadataRepositoryException e ) { log.error( e.getMessage(), e ); throw new ArchivaRestServiceException( "Repository exception: " + e.getMessage(), 500, e ); } finally { repositorySession.close(); } return true; } @Override public Boolean isAuthorizedToDeleteArtifacts( String repoId ) throws ArchivaRestServiceException { String userName = getAuditInformation().getUser() == null ? "guest" : getAuditInformation().getUser().getUsername(); try { return userRepositories.isAuthorizedToDeleteArtifacts( userName, repoId ); } catch ( ArchivaSecurityException e ) { throw new ArchivaRestServiceException( e.getMessage(), Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e ); } } @Override public RepositoryScanStatistics scanRepositoryDirectoriesNow( String repositoryId ) throws ArchivaRestServiceException { long sinceWhen = RepositoryScanner.FRESH_SCAN; try { return repoScanner.scan( getManagedRepositoryAdmin().getManagedRepository( repositoryId ), sinceWhen ); } catch ( RepositoryScannerException e ) { log.error( e.getMessage(), e ); throw new ArchivaRestServiceException( "RepositoryScannerException exception: " + e.getMessage(), 500, e ); } catch ( RepositoryAdminException e ) { log.error( e.getMessage(), e ); throw new ArchivaRestServiceException( "RepositoryScannerException exception: " + e.getMessage(), 500, e ); } } /** * Update artifact level metadata. Creates one if metadata does not exist after artifact deletion. * * @param metadata */ private void updateMetadata( ArchivaRepositoryMetadata metadata, File metadataFile, Date lastUpdatedTimestamp, Artifact artifact ) throws RepositoryMetadataException { List<String> availableVersions = new ArrayList<>(); String latestVersion = ""; if ( metadataFile.exists() ) { if ( metadata.getAvailableVersions() != null ) { availableVersions = metadata.getAvailableVersions(); if ( availableVersions.size() > 0 ) { Collections.sort( availableVersions, VersionComparator.getInstance() ); if ( availableVersions.contains( artifact.getVersion() ) ) { availableVersions.remove( availableVersions.indexOf( artifact.getVersion() ) ); } if ( availableVersions.size() > 0 ) { latestVersion = availableVersions.get( availableVersions.size() - 1 ); } } } } if ( metadata.getGroupId() == null ) { metadata.setGroupId( artifact.getGroupId() ); } if ( metadata.getArtifactId() == null ) { metadata.setArtifactId( artifact.getArtifactId() ); } if ( !VersionUtil.isSnapshot( artifact.getVersion() ) ) { if ( metadata.getReleasedVersion() != null && metadata.getReleasedVersion().equals( artifact.getVersion() ) ) { metadata.setReleasedVersion( latestVersion ); } } metadata.setLatestVersion( latestVersion ); metadata.setLastUpdatedTimestamp( lastUpdatedTimestamp ); metadata.setAvailableVersions( availableVersions ); RepositoryMetadataWriter.write( metadata, metadataFile ); ChecksummedFile checksum = new ChecksummedFile( metadataFile ); checksum.fixChecksums( algorithms ); } @Override public StringList getRunningRemoteDownloadIds() { return new StringList( downloadRemoteIndexScheduler.getRunningRemoteDownloadIds() ); } public ManagedRepositoryAdmin getManagedRepositoryAdmin() { return managedRepositoryAdmin; } public void setManagedRepositoryAdmin( ManagedRepositoryAdmin managedRepositoryAdmin ) { this.managedRepositoryAdmin = managedRepositoryAdmin; } public RepositoryContentFactory getRepositoryFactory() { return repositoryFactory; } public void setRepositoryFactory( RepositoryContentFactory repositoryFactory ) { this.repositoryFactory = repositoryFactory; } public RepositorySessionFactory getRepositorySessionFactory() { return repositorySessionFactory; } public void setRepositorySessionFactory( RepositorySessionFactory repositorySessionFactory ) { this.repositorySessionFactory = repositorySessionFactory; } public List<RepositoryListener> getListeners() { return listeners; } public void setListeners( List<RepositoryListener> listeners ) { this.listeners = listeners; } public ArchivaAdministration getArchivaAdministration() { return archivaAdministration; } public void setArchivaAdministration( ArchivaAdministration archivaAdministration ) { this.archivaAdministration = archivaAdministration; } }