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.maven.model.MavenArtifactFacet;
26 import org.apache.archiva.metadata.repository.*;
27 import org.apache.archiva.metadata.audit.RepositoryListener;
28 import org.apache.archiva.repository.ManagedRepositoryContent;
29 import org.apache.archiva.repository.content.Artifact;
30 import org.apache.archiva.repository.content.ContentAccessException;
31 import org.apache.archiva.repository.content.ItemNotFoundException;
32 import org.apache.archiva.repository.storage.StorageAsset;
33 import org.apache.archiva.repository.storage.util.StorageUtil;
34 import org.apache.commons.lang3.StringUtils;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
38 import java.io.IOException;
39 import java.util.Collection;
40 import java.util.HashMap;
41 import java.util.HashSet;
42 import java.util.List;
47 * Base class for all repository purge tasks.
49 public abstract class AbstractRepositoryPurge
50 implements RepositoryPurge
52 protected Logger log = LoggerFactory.getLogger( getClass( ) );
54 protected final ManagedRepositoryContent repository;
56 protected final RepositorySession repositorySession;
58 protected final List<RepositoryListener> listeners;
60 private Logger logger = LoggerFactory.getLogger( "org.apache.archiva.AuditLog" );
62 private static final char DELIM = ' ';
64 public AbstractRepositoryPurge( ManagedRepositoryContent repository, RepositorySession repositorySession,
65 List<RepositoryListener> listeners )
67 this.repository = repository;
68 this.repositorySession = repositorySession;
69 this.listeners = listeners;
73 * We have to track namespace, project, project version, artifact version and classifier
74 * There is no metadata class that contains all these properties.
78 final String namespace;
80 final String projectVersion;
84 ArtifactInfo( String namespace, String name, String projectVersion, String version )
86 this.namespace = namespace;
88 this.projectVersion = projectVersion;
89 this.version = version;
92 ArtifactInfo( String namespace, String name, String projectVersion )
94 this.namespace = namespace;
96 this.projectVersion = projectVersion;
100 * Creates a info object without version and classifier
102 ArtifactInfo projectVersionLevel( )
104 return new ArtifactInfo( this.namespace, this.name, this.projectVersion );
107 public void setClassifier( String classifier )
109 this.classifier = classifier;
112 public String getNamespace( )
117 public String getName( )
122 public String getProjectVersion( )
124 return projectVersion;
127 public String getVersion( )
132 public String getClassifier( )
137 public boolean hasClassifier( )
139 return classifier != null && !"".equals( classifier );
143 public boolean equals( Object o )
145 if ( this == o ) return true;
146 if ( o == null || getClass( ) != o.getClass( ) ) return false;
148 ArtifactInfo that = (ArtifactInfo) o;
150 if ( !namespace.equals( that.namespace ) ) return false;
151 if ( !name.equals( that.name ) ) return false;
152 if ( !projectVersion.equals( that.projectVersion ) ) return false;
153 if ( !( version != null ? version.equals( that.version ) : that.version == null ) ) return false;
154 return classifier != null ? classifier.equals( that.classifier ) : that.classifier == null;
158 public int hashCode( )
160 int result = namespace.hashCode( );
161 result = 31 * result + name.hashCode( );
162 result = 31 * result + projectVersion.hashCode( );
163 result = 31 * result + ( version != null ? version.hashCode( ) : 0 );
164 result = 31 * result + ( classifier != null ? classifier.hashCode( ) : 0 );
169 public String toString( )
171 final StringBuilder sb = new StringBuilder( "ArtifactInfo{" );
172 sb.append( "namespace='" ).append( namespace ).append( '\'' );
173 sb.append( ", name='" ).append( name ).append( '\'' );
174 sb.append( ", projectVersion='" ).append( projectVersion ).append( '\'' );
175 sb.append( ", version='" ).append( version ).append( '\'' );
176 sb.append( ", classifier='" ).append( classifier ).append( '\'' );
178 return sb.toString( );
183 * Purge the repo. Update db and index of removed artifacts.
187 protected void purge( Set<Artifact> references )
189 if ( references != null && !references.isEmpty( ) )
191 MetadataRepository metadataRepository = repositorySession.getRepository( );
192 Map<ArtifactInfo, ArtifactMetadata> metaRemovalList = new HashMap<>( );
193 Map<String, Collection<ArtifactMetadata>> metaResolved = new HashMap<>( );
194 for ( Artifact reference : references )
196 String baseVersion = reference.getVersion( ).getId( );
197 String namespace = reference.getVersion( ).getProject( ).getNamespace( ).getId( );
198 // Needed for tracking in the hashmap
199 String metaBaseId = reference.toKey();
201 if ( !metaResolved.containsKey( metaBaseId ) )
205 metaResolved.put( metaBaseId, metadataRepository.getArtifacts(repositorySession, repository.getId( ),
206 namespace, reference.getId( ), baseVersion ) );
208 catch ( MetadataResolutionException e )
210 log.error( "Error during metadata retrieval {}: {}", metaBaseId, e.getMessage( ) );
213 StorageAsset artifactFile = reference.getAsset();
215 for ( RepositoryListener listener : listeners )
217 listener.deleteArtifact( metadataRepository, repository.getId( ), namespace,
218 reference.getId( ), reference.getVersion( ).getId(),
219 artifactFile.getName( ));
221 if (reference.exists())
225 repository.deleteItem( reference );
227 catch ( ContentAccessException e )
229 log.error( "Error while trying to delete artifact {}: {}", reference.toString( ), e.getMessage( ), e );
231 catch ( ItemNotFoundException e )
233 log.error( "Asset deleted from background other thread: {}", e.getMessage( ) );
237 boolean snapshotVersion = VersionUtil.isSnapshot( baseVersion );
240 // If this is a snapshot we have to search for artifacts with the same version. And remove all of them.
241 if ( snapshotVersion )
243 Collection<ArtifactMetadata> artifacts =
244 metaResolved.get( metaBaseId );
245 if ( artifacts != null )
247 // cleanup snapshots metadata
248 for ( ArtifactMetadata artifactMetadata : artifacts )
250 // Artifact metadata and reference version should match.
251 if ( artifactMetadata.getVersion( ).equals( reference.getArtifactVersion( ) ) )
253 ArtifactInfo info = new ArtifactInfo( artifactMetadata.getNamespace( ), artifactMetadata.getProject( ), artifactMetadata.getProjectVersion( ), artifactMetadata.getVersion( ) );
254 if ( StringUtils.isNotBlank( reference.getClassifier( ) ) )
256 info.setClassifier( reference.getClassifier( ) );
257 metaRemovalList.put( info, artifactMetadata );
261 // metadataRepository.removeTimestampedArtifact( artifactMetadata, baseVersion );
262 metaRemovalList.put( info, artifactMetadata );
268 else // otherwise we delete the artifact version
270 ArtifactInfo info = new ArtifactInfo( namespace, reference.getId( ), baseVersion, reference.getArtifactVersion() );
271 for ( ArtifactMetadata metadata : metaResolved.get( metaBaseId ) )
273 metaRemovalList.put( info, metadata );
276 triggerAuditEvent( repository.getRepository( ).getId( ), reference.toKey(),
277 AuditEvent.PURGE_ARTIFACT );
278 // purgeSupportFiles( artifactFile );
280 purgeMetadata( metadataRepository, metaRemovalList );
283 repositorySession.save( );
285 catch ( org.apache.archiva.metadata.repository.MetadataSessionException e )
287 e.printStackTrace( );
294 * Purges the metadata. First removes the artifacts. After that empty versions will be removed.
296 private void purgeMetadata( MetadataRepository metadataRepository, Map<ArtifactInfo, ArtifactMetadata> dataList )
298 Set<ArtifactInfo> projectLevelMetadata = new HashSet<>( );
299 for ( Map.Entry<ArtifactInfo, ArtifactMetadata> infoEntry : dataList.entrySet( ) )
301 ArtifactInfo info = infoEntry.getKey( );
304 removeArtifact( metadataRepository, info, infoEntry.getValue( ) );
305 log.debug( "Removed artifact from MetadataRepository {}", info );
307 catch ( MetadataRepositoryException e )
309 log.error( "Could not remove artifact from MetadataRepository {}: {}", info, e.getMessage( ), e );
311 projectLevelMetadata.add( info.projectVersionLevel( ) );
314 repositorySession.save( );
315 } catch (MetadataSessionException e) {
316 log.error("Could not save sesion {}", e.getMessage());
318 Collection<ArtifactMetadata> artifacts = null;
319 // Get remaining artifacts and remove project if empty
320 for ( ArtifactInfo info : projectLevelMetadata )
324 artifacts = metadataRepository.getArtifacts(repositorySession , repository.getId( ), info.getNamespace( ),
325 info.getName( ), info.getProjectVersion( ) );
326 if ( artifacts.size( ) == 0 )
328 metadataRepository.removeProjectVersion(repositorySession , repository.getId( ),
329 info.getNamespace( ), info.getName( ), info.getProjectVersion( ) );
330 log.debug( "Removed project version from MetadataRepository {}", info );
333 catch ( MetadataResolutionException | MetadataRepositoryException e )
335 log.error( "Could not remove project version from MetadataRepository {}: {}", info, e.getMessage( ), e );
339 repositorySession.save( );
340 } catch (MetadataSessionException e) {
341 log.error("Could not save sesion {}", e.getMessage());
348 * Removes the artifact from the metadataRepository. If a classifier is set, the facet will be removed.
350 private void removeArtifact( MetadataRepository metadataRepository, ArtifactInfo artifactInfo, ArtifactMetadata artifactMetadata ) throws MetadataRepositoryException
352 if ( artifactInfo.hasClassifier( ) )
354 // cleanup facet which contains classifier information
355 MavenArtifactFacet mavenArtifactFacet =
356 (MavenArtifactFacet) artifactMetadata.getFacet(
357 MavenArtifactFacet.FACET_ID );
359 if ( StringUtils.equals( artifactInfo.classifier,
360 mavenArtifactFacet.getClassifier( ) ) )
362 artifactMetadata.removeFacet( MavenArtifactFacet.FACET_ID );
363 String groupId = artifactInfo.getNamespace( ), artifactId =
364 artifactInfo.getName( ),
365 version = artifactInfo.getProjectVersion( );
366 MavenArtifactFacet mavenArtifactFacetToCompare = new MavenArtifactFacet( );
367 mavenArtifactFacetToCompare.setClassifier( artifactInfo.getClassifier( ) );
368 metadataRepository.removeFacetFromArtifact(repositorySession , repository.getId( ), groupId,
369 artifactId, version, mavenArtifactFacetToCompare );
371 repositorySession.save( );
372 } catch (MetadataSessionException e) {
373 log.error("Could not save session {}", e.getMessage());
379 metadataRepository.removeTimestampedArtifact(repositorySession , artifactMetadata, artifactInfo.getProjectVersion( ) );
383 private void deleteSilently( StorageAsset path )
387 path.getStorage().removeAsset(path);
388 triggerAuditEvent( repository.getRepository( ).getId( ), path.toString( ), AuditEvent.PURGE_FILE );
390 catch ( IOException e )
392 log.error( "Error occured during file deletion {}: {} ", path, e.getMessage( ), e );
398 * This find support files for the artifactFile and deletes them.
401 * Support Files are things like ".sha1", ".md5", ".asc", etc.
404 * @param artifactFile the file to base off of.
406 private void purgeSupportFiles( StorageAsset artifactFile )
408 StorageAsset parentDir = artifactFile.getParent( );
410 if ( !parentDir.exists() )
415 final String artifactName = artifactFile.getName( );
417 StorageUtil.walk(parentDir, a -> {
418 if (!a.isContainer() && a.getName().startsWith(artifactName)) deleteSilently(a);
423 private void triggerAuditEvent( String repoId, String resource, String action )
426 repoId + DELIM + "<system-purge>" + DELIM + "<system>" + DELIM + '\"' + resource + '\"' + DELIM + '\"' +