]> source.dussan.org Git - archiva.git/blob
6df43d78c409ddff5b19b613d1cc92071e28f5bc
[archiva.git] /
1 package org.apache.archiva.consumers.core.repository;
2
3 /*
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
11  *
12  *  http://www.apache.org/licenses/LICENSE-2.0
13  *
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
19  * under the License.
20  */
21
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;
36
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;
42 import java.util.Map;
43 import java.util.Set;
44
45 /**
46  * Base class for all repository purge tasks.
47  */
48 public abstract class AbstractRepositoryPurge
49     implements RepositoryPurge
50 {
51     protected Logger log = LoggerFactory.getLogger( getClass( ) );
52
53     protected final ManagedRepositoryContent repository;
54
55     protected final RepositorySession repositorySession;
56
57     protected final List<RepositoryListener> listeners;
58
59     private Logger logger = LoggerFactory.getLogger( "org.apache.archiva.AuditLog" );
60
61     private static final char DELIM = ' ';
62
63     public AbstractRepositoryPurge( ManagedRepositoryContent repository, RepositorySession repositorySession,
64                                     List<RepositoryListener> listeners )
65     {
66         this.repository = repository;
67         this.repositorySession = repositorySession;
68         this.listeners = listeners;
69     }
70
71     /*
72      * We have to track namespace, project, project version, artifact version and classifier
73      * There is no metadata class that contains all these properties.
74      */
75     class ArtifactInfo
76     {
77         final String namespace;
78         final String name;
79         final String projectVersion;
80         String version;
81         String classifier;
82
83         ArtifactInfo( String namespace, String name, String projectVersion, String version )
84         {
85             this.namespace = namespace;
86             this.name = name;
87             this.projectVersion = projectVersion;
88             this.version = version;
89         }
90
91         ArtifactInfo( String namespace, String name, String projectVersion )
92         {
93             this.namespace = namespace;
94             this.name = name;
95             this.projectVersion = projectVersion;
96         }
97
98         /*
99          * Creates a info object without version and classifier
100          */
101         ArtifactInfo projectVersionLevel( )
102         {
103             return new ArtifactInfo( this.namespace, this.name, this.projectVersion );
104         }
105
106         public void setClassifier( String classifier )
107         {
108             this.classifier = classifier;
109         }
110
111         public String getNamespace( )
112         {
113             return namespace;
114         }
115
116         public String getName( )
117         {
118             return name;
119         }
120
121         public String getProjectVersion( )
122         {
123             return projectVersion;
124         }
125
126         public String getVersion( )
127         {
128             return version;
129         }
130
131         public String getClassifier( )
132         {
133             return classifier;
134         }
135
136         public boolean hasClassifier( )
137         {
138             return classifier != null && !"".equals( classifier );
139         }
140
141         @Override
142         public boolean equals( Object o )
143         {
144             if ( this == o ) return true;
145             if ( o == null || getClass( ) != o.getClass( ) ) return false;
146
147             ArtifactInfo that = (ArtifactInfo) o;
148
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;
154         }
155
156         @Override
157         public int hashCode( )
158         {
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 );
164             return result;
165         }
166
167         @Override
168         public String toString( )
169         {
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( '\'' );
176             sb.append( '}' );
177             return sb.toString( );
178         }
179     }
180
181     /**
182      * Purge the repo. Update db and index of removed artifacts.
183      *
184      * @param references
185      */
186     protected void purge( Set<ArtifactReference> references )
187     {
188         if ( references != null && !references.isEmpty( ) )
189         {
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 )
194             {
195                 String baseVersion = VersionUtil.getBaseVersion( reference.getVersion( ) );
196                 // Needed for tracking in the hashmap
197                 String metaBaseId = reference.getGroupId( ) + "/" + reference.getArtifactId( ) + "/" + baseVersion;
198
199                 if ( !metaResolved.containsKey( metaBaseId ) )
200                 {
201                     try
202                     {
203                         metaResolved.put( metaBaseId, metadataRepository.getArtifacts(repositorySession, repository.getId( ),
204                             reference.getGroupId( ), reference.getArtifactId( ), baseVersion ) );
205                     }
206                     catch ( MetadataResolutionException e )
207                     {
208                         log.error( "Error during metadata retrieval {}: {}", metaBaseId, e.getMessage( ) );
209                     }
210                 }
211                 StorageAsset artifactFile = repository.toFile( reference );
212
213                 for ( RepositoryListener listener : listeners )
214                 {
215                     listener.deleteArtifact( metadataRepository, repository.getId( ), reference.getGroupId( ),
216                         reference.getArtifactId( ), reference.getVersion( ),
217                             artifactFile.getName( ));
218                 }
219                 try
220                 {
221                     artifactFile.getStorage().removeAsset(artifactFile);
222                     log.debug( "File deleted: {}", artifactFile );
223                 }
224                 catch ( IOException e )
225                 {
226                     log.error( "Could not delete file {}: {}", artifactFile.toString(), e.getMessage( ), e );
227                     continue;
228                 }
229                 try
230                 {
231                     repository.deleteArtifact( reference );
232                 }
233                 catch ( ContentNotFoundException e )
234                 {
235                     log.warn( "skip error deleting artifact {}: {}", reference, e.getMessage( ) );
236                 }
237
238                 boolean snapshotVersion = VersionUtil.isSnapshot( reference.getVersion( ) );
239
240
241                 // If this is a snapshot we have to search for artifacts with the same version. And remove all of them.
242                 if ( snapshotVersion )
243                 {
244                     Collection<ArtifactMetadata> artifacts =
245                         metaResolved.get( metaBaseId );
246                     if ( artifacts != null )
247                     {
248                         // cleanup snapshots metadata
249                         for ( ArtifactMetadata artifactMetadata : artifacts )
250                         {
251                             // Artifact metadata and reference version should match.
252                             if ( artifactMetadata.getVersion( ).equals( reference.getVersion( ) ) )
253                             {
254                                 ArtifactInfo info = new ArtifactInfo( artifactMetadata.getNamespace( ), artifactMetadata.getProject( ), artifactMetadata.getProjectVersion( ), artifactMetadata.getVersion( ) );
255                                 if ( StringUtils.isNotBlank( reference.getClassifier( ) ) )
256                                 {
257                                     info.setClassifier( reference.getClassifier( ) );
258                                     metaRemovalList.put( info, artifactMetadata );
259                                 }
260                                 else
261                                 {
262                                     // metadataRepository.removeTimestampedArtifact( artifactMetadata, baseVersion );
263                                     metaRemovalList.put( info, artifactMetadata );
264                                 }
265                             }
266                         }
267                     }
268                 }
269                 else // otherwise we delete the artifact version
270                 {
271                     ArtifactInfo info = new ArtifactInfo( reference.getGroupId( ), reference.getArtifactId( ), baseVersion, reference.getVersion( ) );
272                     for ( ArtifactMetadata metadata : metaResolved.get( metaBaseId ) )
273                     {
274                         metaRemovalList.put( info, metadata );
275                     }
276                 }
277                 triggerAuditEvent( repository.getRepository( ).getId( ), ArtifactReference.toKey( reference ),
278                     AuditEvent.PURGE_ARTIFACT );
279                 purgeSupportFiles( artifactFile );
280             }
281             purgeMetadata( metadataRepository, metaRemovalList );
282             try
283             {
284                 repositorySession.save( );
285             }
286             catch ( org.apache.archiva.metadata.repository.MetadataSessionException e )
287             {
288                 e.printStackTrace( );
289             }
290
291         }
292     }
293
294     /*
295      * Purges the metadata. First removes the artifacts. After that empty versions will be removed.
296      */
297     private void purgeMetadata( MetadataRepository metadataRepository, Map<ArtifactInfo, ArtifactMetadata> dataList )
298     {
299         Set<ArtifactInfo> projectLevelMetadata = new HashSet<>( );
300         for ( Map.Entry<ArtifactInfo, ArtifactMetadata> infoEntry : dataList.entrySet( ) )
301         {
302             ArtifactInfo info = infoEntry.getKey( );
303             try
304             {
305                 removeArtifact( metadataRepository, info, infoEntry.getValue( ) );
306                 log.debug( "Removed artifact from MetadataRepository {}", info );
307             }
308             catch ( MetadataRepositoryException e )
309             {
310                 log.error( "Could not remove artifact from MetadataRepository {}: {}", info, e.getMessage( ), e );
311             }
312             projectLevelMetadata.add( info.projectVersionLevel( ) );
313         }
314         try {
315             repositorySession.save( );
316         } catch (MetadataSessionException e) {
317             log.error("Could not save sesion {}", e.getMessage());
318         }
319         Collection<ArtifactMetadata> artifacts = null;
320         // Get remaining artifacts and remove project if empty
321         for ( ArtifactInfo info : projectLevelMetadata )
322         {
323             try
324             {
325                 artifacts = metadataRepository.getArtifacts(repositorySession , repository.getId( ), info.getNamespace( ),
326                     info.getName( ), info.getProjectVersion( ) );
327                 if ( artifacts.size( ) == 0 )
328                 {
329                     metadataRepository.removeProjectVersion(repositorySession , repository.getId( ),
330                         info.getNamespace( ), info.getName( ), info.getProjectVersion( ) );
331                     log.debug( "Removed project version from MetadataRepository {}", info );
332                 }
333             }
334             catch ( MetadataResolutionException | MetadataRepositoryException e )
335             {
336                 log.error( "Could not remove project version from MetadataRepository {}: {}", info, e.getMessage( ), e );
337             }
338         }
339         try {
340             repositorySession.save( );
341         } catch (MetadataSessionException e) {
342             log.error("Could not save sesion {}", e.getMessage());
343
344         }
345
346     }
347
348     /*
349      * Removes the artifact from the metadataRepository. If a classifier is set, the facet will be removed.
350      */
351     private void removeArtifact( MetadataRepository metadataRepository, ArtifactInfo artifactInfo, ArtifactMetadata artifactMetadata ) throws MetadataRepositoryException
352     {
353         if ( artifactInfo.hasClassifier( ) )
354         {
355             // cleanup facet which contains classifier information
356             MavenArtifactFacet mavenArtifactFacet =
357                 (MavenArtifactFacet) artifactMetadata.getFacet(
358                     MavenArtifactFacet.FACET_ID );
359
360             if ( StringUtils.equals( artifactInfo.classifier,
361                 mavenArtifactFacet.getClassifier( ) ) )
362             {
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 );
371                 try {
372                     repositorySession.save( );
373                 } catch (MetadataSessionException e) {
374                     log.error("Could not save session {}", e.getMessage());
375                 }
376             }
377         }
378         else
379         {
380             metadataRepository.removeTimestampedArtifact(repositorySession , artifactMetadata, artifactInfo.getProjectVersion( ) );
381         }
382     }
383
384     private void deleteSilently( StorageAsset path )
385     {
386         try
387         {
388             path.getStorage().removeAsset(path);
389             triggerAuditEvent( repository.getRepository( ).getId( ), path.toString( ), AuditEvent.PURGE_FILE );
390         }
391         catch ( IOException e )
392         {
393             log.error( "Error occured during file deletion {}: {} ", path, e.getMessage( ), e );
394         }
395     }
396
397     /**
398      * <p>
399      * This find support files for the artifactFile and deletes them.
400      * </p>
401      * <p>
402      * Support Files are things like ".sha1", ".md5", ".asc", etc.
403      * </p>
404      *
405      * @param artifactFile the file to base off of.
406      */
407     private void purgeSupportFiles( StorageAsset artifactFile )
408     {
409         StorageAsset parentDir = artifactFile.getParent( );
410
411         if ( !parentDir.exists() )
412         {
413             return;
414         }
415
416         final String artifactName = artifactFile.getName( );
417
418         try
419         {
420
421             StorageUtil.recurse(parentDir, a -> {
422                 if (!a.isContainer() && a.getName().startsWith(artifactName)) deleteSilently(a);
423             }, true, 3 );
424         }
425         catch ( IOException e )
426         {
427             log.error( "Purge of support files failed {}: {}", artifactFile, e.getMessage( ), e );
428         }
429
430     }
431
432     private void triggerAuditEvent( String repoId, String resource, String action )
433     {
434         String msg =
435             repoId + DELIM + "<system-purge>" + DELIM + "<system>" + DELIM + '\"' + resource + '\"' + DELIM + '\"' +
436                 action + '\"';
437
438         logger.info( msg );
439     }
440 }