001package org.apache.archiva.consumers.core.repository; 002 003/* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022import org.apache.archiva.common.utils.VersionComparator; 023import org.apache.archiva.common.utils.VersionUtil; 024import org.apache.archiva.metadata.repository.MetadataRepository; 025import org.apache.archiva.metadata.repository.MetadataRepositoryException; 026import org.apache.archiva.metadata.repository.RepositorySession; 027import org.apache.archiva.model.ArtifactReference; 028import org.apache.archiva.model.ProjectReference; 029import org.apache.archiva.model.VersionedReference; 030import org.apache.archiva.repository.ContentNotFoundException; 031import org.apache.archiva.repository.LayoutException; 032import org.apache.archiva.repository.ManagedRepositoryContent; 033import org.apache.archiva.repository.ReleaseScheme; 034import org.apache.archiva.repository.RepositoryException; 035import org.apache.archiva.repository.RepositoryRegistry; 036import org.apache.archiva.metadata.audit.RepositoryListener; 037import org.apache.archiva.repository.metadata.base.MetadataTools; 038import org.apache.archiva.repository.metadata.RepositoryMetadataException; 039 040import java.io.IOException; 041import java.nio.file.Files; 042import java.nio.file.Path; 043import java.nio.file.Paths; 044import java.util.ArrayList; 045import java.util.Collection; 046import java.util.Collections; 047import java.util.List; 048 049/** 050 * <p> 051 * This will look in a single managed repository, and purge any snapshots that are present 052 * that have a corresponding released version on the same repository. 053 * </p> 054 * <p> 055 * So, if you have the following (presented in the m2/default layout form) ... 056 * <pre> 057 * /com/foo/foo-tool/1.0-SNAPSHOT/foo-tool-1.0-SNAPSHOT.jar 058 * /com/foo/foo-tool/1.1-SNAPSHOT/foo-tool-1.1-SNAPSHOT.jar 059 * /com/foo/foo-tool/1.2.1-SNAPSHOT/foo-tool-1.2.1-SNAPSHOT.jar 060 * /com/foo/foo-tool/1.2.1/foo-tool-1.2.1.jar 061 * /com/foo/foo-tool/2.0-SNAPSHOT/foo-tool-2.0-SNAPSHOT.jar 062 * /com/foo/foo-tool/2.0/foo-tool-2.0.jar 063 * /com/foo/foo-tool/2.1-SNAPSHOT/foo-tool-2.1-SNAPSHOT.jar 064 * </pre> 065 * then the current highest ranked released (non-snapshot) version is 2.0, which means 066 * the snapshots from 1.0-SNAPSHOT, 1.1-SNAPSHOT, 1.2.1-SNAPSHOT, and 2.0-SNAPSHOT can 067 * be purged. Leaving 2.1-SNAPSHOT in alone. 068 */ 069public class CleanupReleasedSnapshotsRepositoryPurge 070 extends AbstractRepositoryPurge 071{ 072 private MetadataTools metadataTools; 073 074 private RepositoryRegistry repositoryRegistry; 075 076 public CleanupReleasedSnapshotsRepositoryPurge( ManagedRepositoryContent repository, MetadataTools metadataTools, 077 RepositoryRegistry repositoryRegistry, 078 RepositorySession repositorySession, 079 List<RepositoryListener> listeners ) 080 { 081 super( repository, repositorySession, listeners ); 082 this.metadataTools = metadataTools; 083 this.repositoryRegistry = repositoryRegistry; 084 } 085 086 @Override 087 public void process( String path ) 088 throws RepositoryPurgeException 089 { 090 try 091 { 092 Path artifactFile = Paths.get( repository.getRepoRoot( ), path ); 093 094 if ( !Files.exists(artifactFile) ) 095 { 096 // Nothing to do here, file doesn't exist, skip it. 097 return; 098 } 099 100 ArtifactReference artifactRef = repository.toArtifactReference( path ); 101 102 if ( !VersionUtil.isSnapshot( artifactRef.getVersion( ) ) ) 103 { 104 // Nothing to do here, not a snapshot, skip it. 105 return; 106 } 107 108 ProjectReference reference = new ProjectReference( ); 109 reference.setGroupId( artifactRef.getGroupId( ) ); 110 reference.setArtifactId( artifactRef.getArtifactId( ) ); 111 112 // Gether the released versions 113 List<String> releasedVersions = new ArrayList<>( ); 114 115 Collection<org.apache.archiva.repository.ManagedRepository> repos = repositoryRegistry.getManagedRepositories( ); 116 for ( org.apache.archiva.repository.ManagedRepository repo : repos ) 117 { 118 119 if ( repo.getActiveReleaseSchemes().contains( ReleaseScheme.RELEASE )) 120 { 121 try 122 { 123 ManagedRepositoryContent repoContent = repo.getContent(); 124 for ( String version : repoContent.getVersions( reference ) ) 125 { 126 if ( !VersionUtil.isSnapshot( version ) ) 127 { 128 releasedVersions.add( version ); 129 } 130 } 131 } 132 catch ( RepositoryException e ) 133 { 134 // swallow 135 } 136 } 137 } 138 139 Collections.sort( releasedVersions, VersionComparator.getInstance( ) ); 140 141 // Now clean out any version that is earlier than the highest released version. 142 boolean needsMetadataUpdate = false; 143 144 VersionedReference versionRef = new VersionedReference( ); 145 versionRef.setGroupId( artifactRef.getGroupId( ) ); 146 versionRef.setArtifactId( artifactRef.getArtifactId( ) ); 147 148 MetadataRepository metadataRepository = repositorySession.getRepository( ); 149 150 if ( releasedVersions.contains( VersionUtil.getReleaseVersion( artifactRef.getVersion( ) ) ) ) 151 { 152 versionRef.setVersion( artifactRef.getVersion( ) ); 153 repository.deleteVersion( versionRef ); 154 155 for ( RepositoryListener listener : listeners ) 156 { 157 listener.deleteArtifact( metadataRepository, repository.getId( ), artifactRef.getGroupId( ), 158 artifactRef.getArtifactId( ), artifactRef.getVersion( ), 159 artifactFile.getFileName().toString() ); 160 } 161 metadataRepository.removeProjectVersion( repositorySession, repository.getId( ), 162 artifactRef.getGroupId( ), artifactRef.getArtifactId( ), artifactRef.getVersion( ) ); 163 164 needsMetadataUpdate = true; 165 } 166 167 if ( needsMetadataUpdate ) 168 { 169 updateMetadata( artifactRef ); 170 } 171 } 172 catch ( LayoutException e ) 173 { 174 log.debug( "Not processing file that is not an artifact: {}", e.getMessage( ) ); 175 } 176 catch ( ContentNotFoundException e ) 177 { 178 throw new RepositoryPurgeException( e.getMessage( ), e ); 179 } 180 catch ( MetadataRepositoryException e ) 181 { 182 log.error( "Could not remove metadata during cleanup of released snapshots of {}", path, e ); 183 } 184 } 185 186 187 /* 188 * TODO: Uses a deprecated API, but if we use the API with location string, it does not work as expected 189 * -> not sure what needs to be changed here. 190 */ 191 @SuppressWarnings( "deprecation" ) 192 private void updateMetadata( ArtifactReference artifact ) 193 { 194 VersionedReference versionRef = new VersionedReference( ); 195 versionRef.setGroupId( artifact.getGroupId( ) ); 196 versionRef.setArtifactId( artifact.getArtifactId( ) ); 197 versionRef.setVersion( artifact.getVersion( ) ); 198 199 ProjectReference projectRef = new ProjectReference( ); 200 projectRef.setGroupId( artifact.getGroupId( ) ); 201 projectRef.setArtifactId( artifact.getArtifactId( ) ); 202 203 try 204 { 205 metadataTools.updateMetadata( repository, versionRef ); 206 } 207 catch ( ContentNotFoundException e ) 208 { 209 // Ignore. (Just means we have no snapshot versions left to reference). 210 } 211 catch ( RepositoryMetadataException e ) 212 { 213 // Ignore. 214 } 215 catch ( IOException e ) 216 { 217 // Ignore. 218 } 219 catch ( LayoutException e ) 220 { 221 // Ignore. 222 } 223 224 try 225 { 226 metadataTools.updateMetadata( repository, projectRef ); 227 } 228 catch ( ContentNotFoundException e ) 229 { 230 // Ignore. (Just means we have no snapshot versions left to reference). 231 } 232 catch ( RepositoryMetadataException e ) 233 { 234 // Ignore. 235 } 236 catch ( IOException e ) 237 { 238 // Ignore. 239 } 240 catch ( LayoutException e ) 241 { 242 // Ignore. 243 } 244 } 245}