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.*;
27 import org.apache.archiva.model.ArtifactReference;
28 import org.apache.archiva.repository.ContentNotFoundException;
29 import org.apache.archiva.repository.ManagedRepositoryContent;
30 import org.apache.archiva.metadata.audit.RepositoryListener;
31 import org.apache.archiva.repository.storage.StorageAsset;
32 import org.apache.archiva.repository.storage.StorageUtil;
33 import org.apache.commons.lang3.StringUtils;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
37 import java.io.IOException;
38 import java.util.Collection;
39 import java.util.HashMap;
40 import java.util.HashSet;
41 import java.util.List;
46 * Base class for all repository purge tasks.
48 public abstract class AbstractRepositoryPurge
49 implements RepositoryPurge
51 protected Logger log = LoggerFactory.getLogger( getClass( ) );
53 protected final ManagedRepositoryContent repository;
55 protected final RepositorySession repositorySession;
57 protected final List<RepositoryListener> listeners;
59 private Logger logger = LoggerFactory.getLogger( "org.apache.archiva.AuditLog" );
61 private static final char DELIM = ' ';
63 public AbstractRepositoryPurge( ManagedRepositoryContent repository, RepositorySession repositorySession,
64 List<RepositoryListener> listeners )
66 this.repository = repository;
67 this.repositorySession = repositorySession;
68 this.listeners = listeners;
72 * We have to track namespace, project, project version, artifact version and classifier
73 * There is no metadata class that contains all these properties.
77 final String namespace;
79 final String projectVersion;
83 ArtifactInfo( String namespace, String name, String projectVersion, String version )
85 this.namespace = namespace;
87 this.projectVersion = projectVersion;
88 this.version = version;
91 ArtifactInfo( String namespace, String name, String projectVersion )
93 this.namespace = namespace;
95 this.projectVersion = projectVersion;
99 * Creates a info object without version and classifier
101 ArtifactInfo projectVersionLevel( )
103 return new ArtifactInfo( this.namespace, this.name, this.projectVersion );
106 public void setClassifier( String classifier )
108 this.classifier = classifier;
111 public String getNamespace( )
116 public String getName( )
121 public String getProjectVersion( )
123 return projectVersion;
126 public String getVersion( )
131 public String getClassifier( )
136 public boolean hasClassifier( )
138 return classifier != null && !"".equals( classifier );
142 public boolean equals( Object o )
144 if ( this == o ) return true;
145 if ( o == null || getClass( ) != o.getClass( ) ) return false;
147 ArtifactInfo that = (ArtifactInfo) o;
149 if ( !namespace.equals( that.namespace ) ) return false;
150 if ( !name.equals( that.name ) ) return false;
151 if ( !projectVersion.equals( that.projectVersion ) ) return false;
152 if ( !( version != null ? version.equals( that.version ) : that.version == null ) ) return false;
153 return classifier != null ? classifier.equals( that.classifier ) : that.classifier == null;
157 public int hashCode( )
159 int result = namespace.hashCode( );
160 result = 31 * result + name.hashCode( );
161 result = 31 * result + projectVersion.hashCode( );
162 result = 31 * result + ( version != null ? version.hashCode( ) : 0 );
163 result = 31 * result + ( classifier != null ? classifier.hashCode( ) : 0 );
168 public String toString( )
170 final StringBuilder sb = new StringBuilder( "ArtifactInfo{" );
171 sb.append( "namespace='" ).append( namespace ).append( '\'' );
172 sb.append( ", name='" ).append( name ).append( '\'' );
173 sb.append( ", projectVersion='" ).append( projectVersion ).append( '\'' );
174 sb.append( ", version='" ).append( version ).append( '\'' );
175 sb.append( ", classifier='" ).append( classifier ).append( '\'' );
177 return sb.toString( );
182 * Purge the repo. Update db and index of removed artifacts.
186 protected void purge( Set<ArtifactReference> references )
188 if ( references != null && !references.isEmpty( ) )
190 MetadataRepository metadataRepository = repositorySession.getRepository( );
191 Map<ArtifactInfo, ArtifactMetadata> metaRemovalList = new HashMap<>( );
192 Map<String, Collection<ArtifactMetadata>> metaResolved = new HashMap<>( );
193 for ( ArtifactReference reference : references )
195 String baseVersion = VersionUtil.getBaseVersion( reference.getVersion( ) );
196 // Needed for tracking in the hashmap
197 String metaBaseId = reference.getGroupId( ) + "/" + reference.getArtifactId( ) + "/" + baseVersion;
199 if ( !metaResolved.containsKey( metaBaseId ) )
203 metaResolved.put( metaBaseId, metadataRepository.getArtifacts(repositorySession, repository.getId( ),
204 reference.getGroupId( ), reference.getArtifactId( ), baseVersion ) );
206 catch ( MetadataResolutionException e )
208 log.error( "Error during metadata retrieval {}: {}", metaBaseId, e.getMessage( ) );
211 StorageAsset artifactFile = repository.toFile( reference );
213 for ( RepositoryListener listener : listeners )
215 listener.deleteArtifact( metadataRepository, repository.getId( ), reference.getGroupId( ),
216 reference.getArtifactId( ), reference.getVersion( ),
217 artifactFile.getName( ));
221 artifactFile.getStorage().removeAsset(artifactFile);
222 log.debug( "File deleted: {}", artifactFile );
224 catch ( IOException e )
226 log.error( "Could not delete file {}: {}", artifactFile.toString(), e.getMessage( ), e );
231 repository.deleteArtifact( reference );
233 catch ( ContentNotFoundException e )
235 log.warn( "skip error deleting artifact {}: {}", reference, e.getMessage( ) );
238 boolean snapshotVersion = VersionUtil.isSnapshot( reference.getVersion( ) );
241 // If this is a snapshot we have to search for artifacts with the same version. And remove all of them.
242 if ( snapshotVersion )
244 Collection<ArtifactMetadata> artifacts =
245 metaResolved.get( metaBaseId );
246 if ( artifacts != null )
248 // cleanup snapshots metadata
249 for ( ArtifactMetadata artifactMetadata : artifacts )
251 // Artifact metadata and reference version should match.
252 if ( artifactMetadata.getVersion( ).equals( reference.getVersion( ) ) )
254 ArtifactInfo info = new ArtifactInfo( artifactMetadata.getNamespace( ), artifactMetadata.getProject( ), artifactMetadata.getProjectVersion( ), artifactMetadata.getVersion( ) );
255 if ( StringUtils.isNotBlank( reference.getClassifier( ) ) )
257 info.setClassifier( reference.getClassifier( ) );
258 metaRemovalList.put( info, artifactMetadata );
262 // metadataRepository.removeTimestampedArtifact( artifactMetadata, baseVersion );
263 metaRemovalList.put( info, artifactMetadata );
269 else // otherwise we delete the artifact version
271 ArtifactInfo info = new ArtifactInfo( reference.getGroupId( ), reference.getArtifactId( ), baseVersion, reference.getVersion( ) );
272 for ( ArtifactMetadata metadata : metaResolved.get( metaBaseId ) )
274 metaRemovalList.put( info, metadata );
277 triggerAuditEvent( repository.getRepository( ).getId( ), ArtifactReference.toKey( reference ),
278 AuditEvent.PURGE_ARTIFACT );
279 purgeSupportFiles( artifactFile );
281 purgeMetadata( metadataRepository, metaRemovalList );
284 repositorySession.save( );
286 catch ( org.apache.archiva.metadata.repository.MetadataSessionException e )
288 e.printStackTrace( );
295 * Purges the metadata. First removes the artifacts. After that empty versions will be removed.
297 private void purgeMetadata( MetadataRepository metadataRepository, Map<ArtifactInfo, ArtifactMetadata> dataList )
299 Set<ArtifactInfo> projectLevelMetadata = new HashSet<>( );
300 for ( Map.Entry<ArtifactInfo, ArtifactMetadata> infoEntry : dataList.entrySet( ) )
302 ArtifactInfo info = infoEntry.getKey( );
305 removeArtifact( metadataRepository, info, infoEntry.getValue( ) );
306 log.debug( "Removed artifact from MetadataRepository {}", info );
308 catch ( MetadataRepositoryException e )
310 log.error( "Could not remove artifact from MetadataRepository {}: {}", info, e.getMessage( ), e );
312 projectLevelMetadata.add( info.projectVersionLevel( ) );
315 repositorySession.save( );
316 } catch (MetadataSessionException e) {
317 log.error("Could not save sesion {}", e.getMessage());
319 Collection<ArtifactMetadata> artifacts = null;
320 // Get remaining artifacts and remove project if empty
321 for ( ArtifactInfo info : projectLevelMetadata )
325 artifacts = metadataRepository.getArtifacts(repositorySession , repository.getId( ), info.getNamespace( ),
326 info.getName( ), info.getProjectVersion( ) );
327 if ( artifacts.size( ) == 0 )
329 metadataRepository.removeProjectVersion(repositorySession , repository.getId( ),
330 info.getNamespace( ), info.getName( ), info.getProjectVersion( ) );
331 log.debug( "Removed project version from MetadataRepository {}", info );
334 catch ( MetadataResolutionException | MetadataRepositoryException e )
336 log.error( "Could not remove project version from MetadataRepository {}: {}", info, e.getMessage( ), e );
340 repositorySession.save( );
341 } catch (MetadataSessionException e) {
342 log.error("Could not save sesion {}", e.getMessage());
349 * Removes the artifact from the metadataRepository. If a classifier is set, the facet will be removed.
351 private void removeArtifact( MetadataRepository metadataRepository, ArtifactInfo artifactInfo, ArtifactMetadata artifactMetadata ) throws MetadataRepositoryException
353 if ( artifactInfo.hasClassifier( ) )
355 // cleanup facet which contains classifier information
356 MavenArtifactFacet mavenArtifactFacet =
357 (MavenArtifactFacet) artifactMetadata.getFacet(
358 MavenArtifactFacet.FACET_ID );
360 if ( StringUtils.equals( artifactInfo.classifier,
361 mavenArtifactFacet.getClassifier( ) ) )
363 artifactMetadata.removeFacet( MavenArtifactFacet.FACET_ID );
364 String groupId = artifactInfo.getNamespace( ), artifactId =
365 artifactInfo.getName( ),
366 version = artifactInfo.getProjectVersion( );
367 MavenArtifactFacet mavenArtifactFacetToCompare = new MavenArtifactFacet( );
368 mavenArtifactFacetToCompare.setClassifier( artifactInfo.getClassifier( ) );
369 metadataRepository.removeFacetFromArtifact(repositorySession , repository.getId( ), groupId,
370 artifactId, version, mavenArtifactFacetToCompare );
372 repositorySession.save( );
373 } catch (MetadataSessionException e) {
374 log.error("Could not save session {}", e.getMessage());
380 metadataRepository.removeTimestampedArtifact(repositorySession , artifactMetadata, artifactInfo.getProjectVersion( ) );
384 private void deleteSilently( StorageAsset path )
388 path.getStorage().removeAsset(path);
389 triggerAuditEvent( repository.getRepository( ).getId( ), path.toString( ), AuditEvent.PURGE_FILE );
391 catch ( IOException e )
393 log.error( "Error occured during file deletion {}: {} ", path, e.getMessage( ), e );
399 * This find support files for the artifactFile and deletes them.
402 * Support Files are things like ".sha1", ".md5", ".asc", etc.
405 * @param artifactFile the file to base off of.
407 private void purgeSupportFiles( StorageAsset artifactFile )
409 StorageAsset parentDir = artifactFile.getParent( );
411 if ( !parentDir.exists() )
416 final String artifactName = artifactFile.getName( );
421 StorageUtil.recurse(parentDir, a -> {
422 if (!a.isContainer() && a.getName().startsWith(artifactName)) deleteSilently(a);
425 catch ( IOException e )
427 log.error( "Purge of support files failed {}: {}", artifactFile, e.getMessage( ), e );
432 private void triggerAuditEvent( String repoId, String resource, String action )
435 repoId + DELIM + "<system-purge>" + DELIM + "<system>" + DELIM + '\"' + resource + '\"' + DELIM + '\"' +