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