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.metadata.model.facets.AuditEvent;
23 import org.apache.archiva.common.utils.VersionUtil;
24 import org.apache.archiva.metadata.model.ArtifactMetadata;
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;
39 import java.io.FilenameFilter;
40 import java.io.IOException;
41 import java.nio.file.Files;
42 import java.nio.file.Path;
44 import java.util.stream.Collectors;
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<ArtifactReference> 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 ( ArtifactReference reference : references )
196 String baseVersion = VersionUtil.getBaseVersion( reference.getVersion( ) );
197 // Needed for tracking in the hashmap
198 String metaBaseId = reference.getGroupId( ) + "/" + reference.getArtifactId( ) + "/" + baseVersion;
200 if ( !metaResolved.containsKey( metaBaseId ) )
204 metaResolved.put( metaBaseId, metadataRepository.getArtifacts( repository.getId( ), reference.getGroupId( ),
205 reference.getArtifactId( ), baseVersion ) );
207 catch ( MetadataResolutionException e )
209 log.error( "Error during metadata retrieval {}: {}", metaBaseId, e.getMessage( ) );
212 Path artifactFile = repository.toFile( reference ).toPath( );
214 for ( RepositoryListener listener : listeners )
216 listener.deleteArtifact( metadataRepository, repository.getId( ), reference.getGroupId( ),
217 reference.getArtifactId( ), reference.getVersion( ),
218 artifactFile.getFileName( ).toString( ) );
222 Files.delete( artifactFile );
223 log.debug( "File deleted: {}", artifactFile.toAbsolutePath( ) );
225 catch ( IOException e )
227 log.error( "Could not delete file {}: {}", artifactFile.toAbsolutePath( ), e.getMessage( ), e );
232 repository.deleteArtifact( reference );
234 catch ( ContentNotFoundException e )
236 log.warn( "skip error deleting artifact {}: {}", reference, e.getMessage( ) );
239 boolean snapshotVersion = VersionUtil.isSnapshot( reference.getVersion( ) );
242 // If this is a snapshot we have to search for artifacts with the same version. And remove all of them.
243 if ( snapshotVersion )
245 Collection<ArtifactMetadata> artifacts =
246 metaResolved.get( metaBaseId );
247 if ( artifacts != null )
249 // cleanup snapshots metadata
250 for ( ArtifactMetadata artifactMetadata : artifacts )
252 // Artifact metadata and reference version should match.
253 if ( artifactMetadata.getVersion( ).equals( reference.getVersion( ) ) )
255 ArtifactInfo info = new ArtifactInfo( artifactMetadata.getNamespace( ), artifactMetadata.getProject( ), artifactMetadata.getProjectVersion( ), artifactMetadata.getVersion( ) );
256 if ( StringUtils.isNotBlank( reference.getClassifier( ) ) )
258 info.setClassifier( reference.getClassifier( ) );
259 metaRemovalList.put( info, artifactMetadata );
263 // metadataRepository.removeArtifact( artifactMetadata, baseVersion );
264 metaRemovalList.put( info, artifactMetadata );
270 else // otherwise we delete the artifact version
272 ArtifactInfo info = new ArtifactInfo( reference.getGroupId( ), reference.getArtifactId( ), baseVersion, reference.getVersion( ) );
273 for ( ArtifactMetadata metadata : metaResolved.get( metaBaseId ) )
275 metaRemovalList.put( info, metadata );
278 triggerAuditEvent( repository.getRepository( ).getId( ), ArtifactReference.toKey( reference ),
279 AuditEvent.PURGE_ARTIFACT );
280 purgeSupportFiles( artifactFile );
282 purgeMetadata( metadataRepository, metaRemovalList );
283 repositorySession.save( );
289 * Purges the metadata. First removes the artifacts. After that empty versions will be removed.
291 private void purgeMetadata( MetadataRepository metadataRepository, Map<ArtifactInfo, ArtifactMetadata> dataList )
293 Set<ArtifactInfo> projectLevelMetadata = new HashSet<>( );
294 for ( Map.Entry<ArtifactInfo, ArtifactMetadata> infoEntry : dataList.entrySet( ) )
296 ArtifactInfo info = infoEntry.getKey( );
299 removeArtifact( metadataRepository, info, infoEntry.getValue( ) );
300 log.debug( "Removed artifact from MetadataRepository {}", info );
302 catch ( MetadataRepositoryException e )
304 log.error( "Could not remove artifact from MetadataRepository {}: {}", info, e.getMessage( ), e );
306 projectLevelMetadata.add( info.projectVersionLevel( ) );
308 metadataRepository.save( );
309 Collection<ArtifactMetadata> artifacts = null;
310 // Get remaining artifacts and remove project if empty
311 for ( ArtifactInfo info : projectLevelMetadata )
315 artifacts = metadataRepository.getArtifacts( repository.getId( ), info.getNamespace( ), info.getName( ),
316 info.getProjectVersion( ) );
317 if ( artifacts.size( ) == 0 )
319 metadataRepository.removeProjectVersion( repository.getId( ), info.getNamespace( ),
320 info.getName( ), info.getProjectVersion( ) );
321 log.debug( "Removed project version from MetadataRepository {}", info );
324 catch ( MetadataResolutionException | MetadataRepositoryException e )
326 log.error( "Could not remove project version from MetadataRepository {}: {}", info, e.getMessage( ), e );
329 metadataRepository.save( );
334 * Removes the artifact from the metadataRepository. If a classifier is set, the facet will be removed.
336 private void removeArtifact( MetadataRepository metadataRepository, ArtifactInfo artifactInfo, ArtifactMetadata artifactMetadata ) throws MetadataRepositoryException
338 if ( artifactInfo.hasClassifier( ) )
340 // cleanup facet which contains classifier information
341 MavenArtifactFacet mavenArtifactFacet =
342 (MavenArtifactFacet) artifactMetadata.getFacet(
343 MavenArtifactFacet.FACET_ID );
345 if ( StringUtils.equals( artifactInfo.classifier,
346 mavenArtifactFacet.getClassifier( ) ) )
348 artifactMetadata.removeFacet( MavenArtifactFacet.FACET_ID );
349 String groupId = artifactInfo.getNamespace( ), artifactId =
350 artifactInfo.getName( ),
351 version = artifactInfo.getProjectVersion( );
352 MavenArtifactFacet mavenArtifactFacetToCompare = new MavenArtifactFacet( );
353 mavenArtifactFacetToCompare.setClassifier( artifactInfo.getClassifier( ) );
354 metadataRepository.removeArtifact( repository.getId( ), groupId, artifactId,
355 version, mavenArtifactFacetToCompare );
356 metadataRepository.save( );
361 metadataRepository.removeArtifact( artifactMetadata, artifactInfo.getProjectVersion( ) );
365 private void deleteSilently(Path path) {
368 Files.deleteIfExists( path );
369 triggerAuditEvent( repository.getRepository( ).getId( ), path.toString(), AuditEvent.PURGE_FILE );
371 catch ( IOException e )
373 log.error("Error occured during file deletion {}: {} ",path,e.getMessage(), e);
379 * This find support files for the artifactFile and deletes them.
382 * Support Files are things like ".sha1", ".md5", ".asc", etc.
385 * @param artifactFile the file to base off of.
387 private void purgeSupportFiles( Path artifactFile )
389 Path parentDir = artifactFile.getParent();
391 if ( !Files.exists(parentDir) )
396 final String artifactName = artifactFile.getFileName().toString();
400 Files.find(parentDir, 3,
401 ( path, basicFileAttributes ) -> path.getFileName().toString().startsWith(artifactName)
402 && Files.isRegularFile( path ) ).forEach( this::deleteSilently );
404 catch ( IOException e )
406 log.error("Purge of support files failed {}: {}", artifactFile, e.getMessage(), e);
411 private void triggerAuditEvent( String repoId, String resource, String action )
414 repoId + DELIM + "<system-purge>" + DELIM + "<system>" + DELIM + '\"' + resource + '\"' + DELIM + '\"' +