1 package org.apache.archiva.consumers.core.repository;
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
12 * http://www.apache.org/licenses/LICENSE-2.0
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
22 import org.apache.archiva.common.utils.VersionUtil;
23 import org.apache.archiva.metadata.model.ArtifactMetadata;
24 import org.apache.archiva.metadata.model.facets.AuditEvent;
25 import org.apache.archiva.metadata.model.maven2.MavenArtifactFacet;
26 import org.apache.archiva.metadata.repository.MetadataRepository;
27 import org.apache.archiva.metadata.repository.MetadataRepositoryException;
28 import org.apache.archiva.metadata.repository.MetadataResolutionException;
29 import org.apache.archiva.metadata.repository.RepositorySession;
30 import org.apache.archiva.model.ArtifactReference;
31 import org.apache.archiva.repository.ContentNotFoundException;
32 import org.apache.archiva.repository.ManagedRepositoryContent;
33 import org.apache.archiva.repository.events.RepositoryListener;
34 import org.apache.commons.lang.StringUtils;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
38 import java.io.IOException;
39 import java.nio.file.Files;
40 import java.nio.file.Path;
41 import java.util.Collection;
42 import java.util.HashMap;
43 import java.util.HashSet;
44 import java.util.List;
49 * Base class for all repository purge tasks.
51 public abstract class AbstractRepositoryPurge
52 implements RepositoryPurge
54 protected Logger log = LoggerFactory.getLogger( getClass( ) );
56 protected final ManagedRepositoryContent repository;
58 protected final RepositorySession repositorySession;
60 protected final List<RepositoryListener> listeners;
62 private Logger logger = LoggerFactory.getLogger( "org.apache.archiva.AuditLog" );
64 private static final char DELIM = ' ';
66 public AbstractRepositoryPurge( ManagedRepositoryContent repository, RepositorySession repositorySession,
67 List<RepositoryListener> listeners )
69 this.repository = repository;
70 this.repositorySession = repositorySession;
71 this.listeners = listeners;
75 * We have to track namespace, project, project version, artifact version and classifier
76 * There is no metadata class that contains all these properties.
80 final String namespace;
82 final String projectVersion;
86 ArtifactInfo( String namespace, String name, String projectVersion, String version )
88 this.namespace = namespace;
90 this.projectVersion = projectVersion;
91 this.version = version;
94 ArtifactInfo( String namespace, String name, String projectVersion )
96 this.namespace = namespace;
98 this.projectVersion = projectVersion;
102 * Creates a info object without version and classifier
104 ArtifactInfo projectVersionLevel( )
106 return new ArtifactInfo( this.namespace, this.name, this.projectVersion );
109 public void setClassifier( String classifier )
111 this.classifier = classifier;
114 public String getNamespace( )
119 public String getName( )
124 public String getProjectVersion( )
126 return projectVersion;
129 public String getVersion( )
134 public String getClassifier( )
139 public boolean hasClassifier( )
141 return classifier != null && !"".equals( classifier );
145 public boolean equals( Object o )
147 if ( this == o ) return true;
148 if ( o == null || getClass( ) != o.getClass( ) ) return false;
150 ArtifactInfo that = (ArtifactInfo) o;
152 if ( !namespace.equals( that.namespace ) ) return false;
153 if ( !name.equals( that.name ) ) return false;
154 if ( !projectVersion.equals( that.projectVersion ) ) return false;
155 if ( !( version != null ? version.equals( that.version ) : that.version == null ) ) return false;
156 return classifier != null ? classifier.equals( that.classifier ) : that.classifier == null;
160 public int hashCode( )
162 int result = namespace.hashCode( );
163 result = 31 * result + name.hashCode( );
164 result = 31 * result + projectVersion.hashCode( );
165 result = 31 * result + ( version != null ? version.hashCode( ) : 0 );
166 result = 31 * result + ( classifier != null ? classifier.hashCode( ) : 0 );
171 public String toString( )
173 final StringBuilder sb = new StringBuilder( "ArtifactInfo{" );
174 sb.append( "namespace='" ).append( namespace ).append( '\'' );
175 sb.append( ", name='" ).append( name ).append( '\'' );
176 sb.append( ", projectVersion='" ).append( projectVersion ).append( '\'' );
177 sb.append( ", version='" ).append( version ).append( '\'' );
178 sb.append( ", classifier='" ).append( classifier ).append( '\'' );
180 return sb.toString( );
185 * Purge the repo. Update db and index of removed artifacts.
189 protected void purge( Set<ArtifactReference> references )
191 if ( references != null && !references.isEmpty( ) )
193 MetadataRepository metadataRepository = repositorySession.getRepository( );
194 Map<ArtifactInfo, ArtifactMetadata> metaRemovalList = new HashMap<>( );
195 Map<String, Collection<ArtifactMetadata>> metaResolved = new HashMap<>( );
196 for ( ArtifactReference reference : references )
198 String baseVersion = VersionUtil.getBaseVersion( reference.getVersion( ) );
199 // Needed for tracking in the hashmap
200 String metaBaseId = reference.getGroupId( ) + "/" + reference.getArtifactId( ) + "/" + baseVersion;
202 if ( !metaResolved.containsKey( metaBaseId ) )
206 metaResolved.put( metaBaseId, metadataRepository.getArtifacts( repository.getId( ), reference.getGroupId( ),
207 reference.getArtifactId( ), baseVersion ) );
209 catch ( MetadataResolutionException e )
211 log.error( "Error during metadata retrieval {}: {}", metaBaseId, e.getMessage( ) );
214 Path artifactFile = repository.toFile( reference ).toPath( );
216 for ( RepositoryListener listener : listeners )
218 listener.deleteArtifact( metadataRepository, repository.getId( ), reference.getGroupId( ),
219 reference.getArtifactId( ), reference.getVersion( ),
220 artifactFile.getFileName( ).toString( ) );
224 Files.delete( artifactFile );
225 log.debug( "File deleted: {}", artifactFile.toAbsolutePath( ) );
227 catch ( IOException e )
229 log.error( "Could not delete file {}: {}", artifactFile.toAbsolutePath( ), e.getMessage( ), e );
234 repository.deleteArtifact( reference );
236 catch ( ContentNotFoundException e )
238 log.warn( "skip error deleting artifact {}: {}", reference, e.getMessage( ) );
241 boolean snapshotVersion = VersionUtil.isSnapshot( reference.getVersion( ) );
244 // If this is a snapshot we have to search for artifacts with the same version. And remove all of them.
245 if ( snapshotVersion )
247 Collection<ArtifactMetadata> artifacts =
248 metaResolved.get( metaBaseId );
249 if ( artifacts != null )
251 // cleanup snapshots metadata
252 for ( ArtifactMetadata artifactMetadata : artifacts )
254 // Artifact metadata and reference version should match.
255 if ( artifactMetadata.getVersion( ).equals( reference.getVersion( ) ) )
257 ArtifactInfo info = new ArtifactInfo( artifactMetadata.getNamespace( ), artifactMetadata.getProject( ), artifactMetadata.getProjectVersion( ), artifactMetadata.getVersion( ) );
258 if ( StringUtils.isNotBlank( reference.getClassifier( ) ) )
260 info.setClassifier( reference.getClassifier( ) );
261 metaRemovalList.put( info, artifactMetadata );
265 // metadataRepository.removeArtifact( artifactMetadata, baseVersion );
266 metaRemovalList.put( info, artifactMetadata );
272 else // otherwise we delete the artifact version
274 ArtifactInfo info = new ArtifactInfo( reference.getGroupId( ), reference.getArtifactId( ), baseVersion, reference.getVersion( ) );
275 for ( ArtifactMetadata metadata : metaResolved.get( metaBaseId ) )
277 metaRemovalList.put( info, metadata );
280 triggerAuditEvent( repository.getRepository( ).getId( ), ArtifactReference.toKey( reference ),
281 AuditEvent.PURGE_ARTIFACT );
282 purgeSupportFiles( artifactFile );
284 purgeMetadata( metadataRepository, metaRemovalList );
285 repositorySession.save( );
291 * Purges the metadata. First removes the artifacts. After that empty versions will be removed.
293 private void purgeMetadata( MetadataRepository metadataRepository, Map<ArtifactInfo, ArtifactMetadata> dataList )
295 Set<ArtifactInfo> projectLevelMetadata = new HashSet<>( );
296 for ( Map.Entry<ArtifactInfo, ArtifactMetadata> infoEntry : dataList.entrySet( ) )
298 ArtifactInfo info = infoEntry.getKey( );
301 removeArtifact( metadataRepository, info, infoEntry.getValue( ) );
302 log.debug( "Removed artifact from MetadataRepository {}", info );
304 catch ( MetadataRepositoryException e )
306 log.error( "Could not remove artifact from MetadataRepository {}: {}", info, e.getMessage( ), e );
308 projectLevelMetadata.add( info.projectVersionLevel( ) );
310 metadataRepository.save( );
311 Collection<ArtifactMetadata> artifacts = null;
312 // Get remaining artifacts and remove project if empty
313 for ( ArtifactInfo info : projectLevelMetadata )
317 artifacts = metadataRepository.getArtifacts( repository.getId( ), info.getNamespace( ), info.getName( ),
318 info.getProjectVersion( ) );
319 if ( artifacts.size( ) == 0 )
321 metadataRepository.removeProjectVersion( repository.getId( ), info.getNamespace( ),
322 info.getName( ), info.getProjectVersion( ) );
323 log.debug( "Removed project version from MetadataRepository {}", info );
326 catch ( MetadataResolutionException | MetadataRepositoryException e )
328 log.error( "Could not remove project version from MetadataRepository {}: {}", info, e.getMessage( ), e );
331 metadataRepository.save( );
336 * Removes the artifact from the metadataRepository. If a classifier is set, the facet will be removed.
338 private void removeArtifact( MetadataRepository metadataRepository, ArtifactInfo artifactInfo, ArtifactMetadata artifactMetadata ) throws MetadataRepositoryException
340 if ( artifactInfo.hasClassifier( ) )
342 // cleanup facet which contains classifier information
343 MavenArtifactFacet mavenArtifactFacet =
344 (MavenArtifactFacet) artifactMetadata.getFacet(
345 MavenArtifactFacet.FACET_ID );
347 if ( StringUtils.equals( artifactInfo.classifier,
348 mavenArtifactFacet.getClassifier( ) ) )
350 artifactMetadata.removeFacet( MavenArtifactFacet.FACET_ID );
351 String groupId = artifactInfo.getNamespace( ), artifactId =
352 artifactInfo.getName( ),
353 version = artifactInfo.getProjectVersion( );
354 MavenArtifactFacet mavenArtifactFacetToCompare = new MavenArtifactFacet( );
355 mavenArtifactFacetToCompare.setClassifier( artifactInfo.getClassifier( ) );
356 metadataRepository.removeArtifact( repository.getId( ), groupId, artifactId,
357 version, mavenArtifactFacetToCompare );
358 metadataRepository.save( );
363 metadataRepository.removeArtifact( artifactMetadata, artifactInfo.getProjectVersion( ) );
367 private void deleteSilently(Path path) {
370 Files.deleteIfExists( path );
371 triggerAuditEvent( repository.getRepository( ).getId( ), path.toString(), AuditEvent.PURGE_FILE );
373 catch ( IOException e )
375 log.error("Error occured during file deletion {}: {} ",path,e.getMessage(), e);
381 * This find support files for the artifactFile and deletes them.
384 * Support Files are things like ".sha1", ".md5", ".asc", etc.
387 * @param artifactFile the file to base off of.
389 private void purgeSupportFiles( Path artifactFile )
391 Path parentDir = artifactFile.getParent();
393 if ( !Files.exists(parentDir) )
398 final String artifactName = artifactFile.getFileName().toString();
402 Files.find(parentDir, 3,
403 ( path, basicFileAttributes ) -> path.getFileName().toString().startsWith(artifactName)
404 && Files.isRegularFile( path ) ).forEach( this::deleteSilently );
406 catch ( IOException e )
408 log.error("Purge of support files failed {}: {}", artifactFile, e.getMessage(), e);
413 private void triggerAuditEvent( String repoId, String resource, String action )
416 repoId + DELIM + "<system-purge>" + DELIM + "<system>" + DELIM + '\"' + resource + '\"' + DELIM + '\"' +