1 package org.apache.archiva.web.action;
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 com.opensymphony.xwork2.Preparable;
23 import com.opensymphony.xwork2.Validateable;
24 import org.apache.archiva.admin.model.RepositoryAdminException;
25 import org.apache.archiva.admin.model.beans.ManagedRepository;
26 import org.apache.archiva.admin.model.managed.ManagedRepositoryAdmin;
27 import org.apache.archiva.audit.AuditEvent;
28 import org.apache.archiva.audit.Auditable;
29 import org.apache.archiva.checksum.ChecksumAlgorithm;
30 import org.apache.archiva.checksum.ChecksummedFile;
31 import org.apache.archiva.metadata.model.ArtifactMetadata;
32 import org.apache.archiva.metadata.repository.MetadataRepository;
33 import org.apache.archiva.metadata.repository.MetadataRepositoryException;
34 import org.apache.archiva.metadata.repository.MetadataResolutionException;
35 import org.apache.archiva.metadata.repository.RepositorySession;
36 import org.apache.archiva.model.ArtifactReference;
37 import org.apache.archiva.repository.events.RepositoryListener;
38 import org.apache.archiva.security.AccessDeniedException;
39 import org.apache.archiva.security.ArchivaSecurityException;
40 import org.apache.archiva.security.PrincipalNotFoundException;
41 import org.apache.archiva.security.UserRepositories;
42 import org.apache.commons.lang.StringUtils;
43 import org.apache.archiva.common.utils.VersionComparator;
44 import org.apache.archiva.common.utils.VersionUtil;
45 import org.apache.archiva.model.ArchivaRepositoryMetadata;
46 import org.apache.archiva.model.VersionedReference;
47 import org.apache.archiva.repository.ContentNotFoundException;
48 import org.apache.archiva.repository.ManagedRepositoryContent;
49 import org.apache.archiva.repository.RepositoryContentFactory;
50 import org.apache.archiva.repository.RepositoryException;
51 import org.apache.archiva.repository.RepositoryNotFoundException;
52 import org.apache.archiva.repository.metadata.MetadataTools;
53 import org.apache.archiva.repository.metadata.RepositoryMetadataException;
54 import org.apache.archiva.repository.metadata.RepositoryMetadataReader;
55 import org.apache.archiva.repository.metadata.RepositoryMetadataWriter;
56 import org.springframework.context.annotation.Scope;
57 import org.springframework.stereotype.Controller;
59 import javax.annotation.PostConstruct;
60 import javax.inject.Inject;
62 import java.text.DateFormat;
63 import java.text.SimpleDateFormat;
64 import java.util.ArrayList;
65 import java.util.Calendar;
66 import java.util.Collection;
67 import java.util.Collections;
68 import java.util.Date;
69 import java.util.List;
70 import java.util.TimeZone;
73 * Delete an artifact. Metadata will be updated if one exists, otherwise it would be created.
75 @Controller( "deleteArtifactAction" )
77 public class DeleteArtifactAction
78 extends AbstractActionSupport
79 implements Validateable, Preparable, Auditable
82 * The groupId of the artifact to be deleted.
84 private String groupId;
87 * The artifactId of the artifact to be deleted.
89 private String artifactId;
92 * The version of the artifact to be deleted.
94 private String version;
98 * The classifier of the artifact to be deleted (optionnal)
100 private String classifier;
104 * The type of the artifact to be deleted (optionnal) (default jar)
109 * The repository where the artifact is to be deleted.
111 private String repositoryId;
114 * List of managed repositories to delete from.
116 private List<String> managedRepos;
119 private UserRepositories userRepositories;
122 private RepositoryContentFactory repositoryFactory;
125 private List<RepositoryListener> listeners;
128 private ManagedRepositoryAdmin managedRepositoryAdmin;
130 private ChecksumAlgorithm[] algorithms = new ChecksumAlgorithm[]{ ChecksumAlgorithm.SHA1, ChecksumAlgorithm.MD5 };
133 public void initialize()
138 public String getGroupId()
143 public void setGroupId( String groupId )
145 this.groupId = groupId;
148 public String getArtifactId()
153 public void setArtifactId( String artifactId )
155 this.artifactId = artifactId;
158 public String getVersion()
163 public void setVersion( String version )
165 this.version = version;
168 public String getRepositoryId()
173 public void setRepositoryId( String repositoryId )
175 this.repositoryId = repositoryId;
178 public List<String> getManagedRepos()
183 public void setManagedRepos( List<String> managedRepos )
185 this.managedRepos = managedRepos;
188 public void prepare()
190 managedRepos = getManagableRepos();
193 public String getClassifier()
198 public void setClassifier( String classifier )
200 this.classifier = classifier;
203 public String getType()
208 public void setType( String type )
213 public String input()
220 // reset the fields so the form is clear when
221 // the action returns to the jsp page
230 public String doDelete()
233 RepositorySession repositorySession = repositorySessionFactory.createSession();
236 Date lastUpdatedTimestamp = Calendar.getInstance().getTime();
238 TimeZone timezone = TimeZone.getTimeZone( "UTC" );
239 DateFormat fmt = new SimpleDateFormat( "yyyyMMdd.HHmmss" );
240 fmt.setTimeZone( timezone );
241 ManagedRepository repoConfig = getManagedRepositoryAdmin().getManagedRepository( repositoryId );
243 VersionedReference ref = new VersionedReference();
244 ref.setArtifactId( artifactId );
245 ref.setGroupId( groupId );
246 ref.setVersion( version );
248 ManagedRepositoryContent repository = repositoryFactory.getManagedRepositoryContent( repositoryId );
250 if ( StringUtils.isNotBlank( classifier ) )
252 if (StringUtils.isBlank( type ))
254 addFieldError( "type", "You must configure a type when using classifier" );
257 ArtifactReference artifactReference = new ArtifactReference();
258 artifactReference.setArtifactId( artifactId );
259 artifactReference.setGroupId( groupId );
260 artifactReference.setVersion( version );
261 artifactReference.setClassifier( classifier );
262 artifactReference.setType( type );
263 repository.deleteArtifact( artifactReference );
265 String msg = "Artifact \'" + groupId + ":" + artifactId + ":" + classifier + ":" + version
266 + "\' was successfully deleted from repository \'" + repositoryId + "\'";
268 addActionMessage( msg );
271 // TODO cleanup facet which contains classifier information
276 String path = repository.toMetadataPath( ref );
277 int index = path.lastIndexOf( '/' );
278 path = path.substring( 0, index );
279 File targetPath = new File( repoConfig.getLocation(), path );
281 if ( !targetPath.exists() )
283 throw new ContentNotFoundException( groupId + ":" + artifactId + ":" + version );
286 // TODO: this should be in the storage mechanism so that it is all tied together
287 // delete from file system
288 repository.deleteVersion( ref );
290 File metadataFile = getMetadata( targetPath.getAbsolutePath() );
291 ArchivaRepositoryMetadata metadata = getMetadata( metadataFile );
293 updateMetadata( metadata, metadataFile, lastUpdatedTimestamp );
295 MetadataRepository metadataRepository = repositorySession.getRepository();
297 Collection<ArtifactMetadata> artifacts =
298 metadataRepository.getArtifacts( repositoryId, groupId, artifactId, version );
300 for ( ArtifactMetadata artifact : artifacts )
302 // TODO: mismatch between artifact (snapshot) version and project (base) version here
303 if ( artifact.getVersion().equals( version ) )
305 metadataRepository.removeArtifact( artifact.getRepositoryId(), artifact.getNamespace(),
306 artifact.getProject(), artifact.getVersion(), artifact.getId() );
308 // TODO: move into the metadata repository proper - need to differentiate attachment of
309 // repository metadata to an artifact
310 for ( RepositoryListener listener : listeners )
312 listener.deleteArtifact( metadataRepository, repository.getId(), artifact.getNamespace(),
313 artifact.getProject(), artifact.getVersion(), artifact.getId() );
316 triggerAuditEvent( repositoryId, path, AuditEvent.REMOVE_FILE );
319 repositorySession.save();
321 catch ( ContentNotFoundException e )
323 addActionError( "Artifact does not exist: " + e.getMessage() );
326 catch ( RepositoryNotFoundException e )
328 addActionError( "Target repository cannot be found: " + e.getMessage() );
331 catch ( RepositoryException e )
333 addActionError( "Repository exception: " + e.getMessage() );
336 catch ( MetadataResolutionException e )
338 addActionError( "Repository exception: " + e.getMessage() );
341 catch ( MetadataRepositoryException e )
343 addActionError( "Repository exception: " + e.getMessage() );
346 catch ( RepositoryAdminException e )
348 addActionError( "RepositoryAdmin exception: " + e.getMessage() );
353 repositorySession.close();
356 String msg = "Artifact \'" + groupId + ":" + artifactId + ":" + version
357 + "\' was successfully deleted from repository \'" + repositoryId + "\'";
359 addActionMessage( msg );
365 private File getMetadata( String targetPath )
367 String artifactPath = targetPath.substring( 0, targetPath.lastIndexOf( File.separatorChar ) );
369 return new File( artifactPath, MetadataTools.MAVEN_METADATA );
372 private ArchivaRepositoryMetadata getMetadata( File metadataFile )
373 throws RepositoryMetadataException
375 ArchivaRepositoryMetadata metadata = new ArchivaRepositoryMetadata();
376 if ( metadataFile.exists() )
378 metadata = RepositoryMetadataReader.read( metadataFile );
384 * Update artifact level metadata. Creates one if metadata does not exist after artifact deletion.
388 private void updateMetadata( ArchivaRepositoryMetadata metadata, File metadataFile, Date lastUpdatedTimestamp )
389 throws RepositoryMetadataException
391 List<String> availableVersions = new ArrayList<String>();
392 String latestVersion = "";
394 if ( metadataFile.exists() )
396 if ( metadata.getAvailableVersions() != null )
398 availableVersions = metadata.getAvailableVersions();
400 if ( availableVersions.size() > 0 )
402 Collections.sort( availableVersions, VersionComparator.getInstance() );
404 if ( availableVersions.contains( version ) )
406 availableVersions.remove( availableVersions.indexOf( version ) );
408 if ( availableVersions.size() > 0 )
410 latestVersion = availableVersions.get( availableVersions.size() - 1 );
416 if ( metadata.getGroupId() == null )
418 metadata.setGroupId( groupId );
420 if ( metadata.getArtifactId() == null )
422 metadata.setArtifactId( artifactId );
425 if ( !VersionUtil.isSnapshot( version ) )
427 if ( metadata.getReleasedVersion() != null && metadata.getReleasedVersion().equals( version ) )
429 metadata.setReleasedVersion( latestVersion );
433 metadata.setLatestVersion( latestVersion );
434 metadata.setLastUpdatedTimestamp( lastUpdatedTimestamp );
435 metadata.setAvailableVersions( availableVersions );
437 RepositoryMetadataWriter.write( metadata, metadataFile );
438 ChecksummedFile checksum = new ChecksummedFile( metadataFile );
439 checksum.fixChecksums( algorithms );
442 public void validate()
446 if ( !userRepositories.isAuthorizedToDeleteArtifacts( getPrincipal(), repositoryId ) )
448 addActionError( "User is not authorized to delete artifacts in repository '" + repositoryId + "'." );
451 if ( ( version.length() > 0 ) && ( !VersionUtil.isVersion( version ) ) )
453 addActionError( "Invalid version." );
456 catch ( AccessDeniedException e )
458 addActionError( e.getMessage() );
460 catch ( ArchivaSecurityException e )
462 addActionError( e.getMessage() );
465 // trims all request parameter values, since the trailing/leading white-spaces are ignored during validation.
466 trimAllRequestParameterValues();
469 private List<String> getManagableRepos()
473 return userRepositories.getManagableRepositoryIds( getPrincipal() );
475 catch ( PrincipalNotFoundException e )
477 log.warn( e.getMessage(), e );
479 catch ( AccessDeniedException e )
481 log.warn( e.getMessage(), e );
482 // TODO: pass this onto the screen.
484 catch ( ArchivaSecurityException e )
486 log.warn( e.getMessage(), e );
488 return Collections.emptyList();
491 private void trimAllRequestParameterValues()
493 if ( StringUtils.isNotEmpty( groupId ) )
495 groupId = groupId.trim();
498 if ( StringUtils.isNotEmpty( artifactId ) )
500 artifactId = artifactId.trim();
503 if ( StringUtils.isNotEmpty( version ) )
505 version = version.trim();
508 if ( StringUtils.isNotEmpty( repositoryId ) )
510 repositoryId = repositoryId.trim();
514 public List<RepositoryListener> getListeners()
519 public void setRepositoryFactory( RepositoryContentFactory repositoryFactory )
521 this.repositoryFactory = repositoryFactory;
524 public ManagedRepositoryAdmin getManagedRepositoryAdmin()
526 return managedRepositoryAdmin;
529 public void setManagedRepositoryAdmin( ManagedRepositoryAdmin managedRepositoryAdmin )
531 this.managedRepositoryAdmin = managedRepositoryAdmin;