]> source.dussan.org Git - archiva.git/blob
09a332eb3dde29f3474af1ab0e0a2ebcd93c609a
[archiva.git] /
1 package org.apache.archiva.web.action;
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 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.repository.events.RepositoryListener;
37 import org.apache.archiva.security.AccessDeniedException;
38 import org.apache.archiva.security.ArchivaSecurityException;
39 import org.apache.archiva.security.PrincipalNotFoundException;
40 import org.apache.archiva.security.UserRepositories;
41 import org.apache.commons.lang.StringUtils;
42 import org.apache.archiva.common.utils.VersionComparator;
43 import org.apache.archiva.common.utils.VersionUtil;
44 import org.apache.archiva.model.ArchivaRepositoryMetadata;
45 import org.apache.archiva.model.VersionedReference;
46 import org.apache.archiva.repository.ContentNotFoundException;
47 import org.apache.archiva.repository.ManagedRepositoryContent;
48 import org.apache.archiva.repository.RepositoryContentFactory;
49 import org.apache.archiva.repository.RepositoryException;
50 import org.apache.archiva.repository.RepositoryNotFoundException;
51 import org.apache.archiva.repository.metadata.MetadataTools;
52 import org.apache.archiva.repository.metadata.RepositoryMetadataException;
53 import org.apache.archiva.repository.metadata.RepositoryMetadataReader;
54 import org.apache.archiva.repository.metadata.RepositoryMetadataWriter;
55 import org.springframework.context.annotation.Scope;
56 import org.springframework.stereotype.Controller;
57
58 import javax.annotation.PostConstruct;
59 import javax.inject.Inject;
60 import java.io.File;
61 import java.text.DateFormat;
62 import java.text.SimpleDateFormat;
63 import java.util.ArrayList;
64 import java.util.Calendar;
65 import java.util.Collection;
66 import java.util.Collections;
67 import java.util.Date;
68 import java.util.List;
69 import java.util.TimeZone;
70
71 /**
72  * Delete an artifact. Metadata will be updated if one exists, otherwise it would be created.
73  */
74 @Controller( "deleteArtifactAction" )
75 @Scope( "prototype" )
76 public class DeleteArtifactAction
77     extends AbstractActionSupport
78     implements Validateable, Preparable, Auditable
79 {
80     /**
81      * The groupId of the artifact to be deleted.
82      */
83     private String groupId;
84
85     /**
86      * The artifactId of the artifact to be deleted.
87      */
88     private String artifactId;
89
90     /**
91      * The version of the artifact to be deleted.
92      */
93     private String version;
94
95     /**
96      * The repository where the artifact is to be deleted.
97      */
98     private String repositoryId;
99
100     /**
101      * List of managed repositories to delete from.
102      */
103     private List<String> managedRepos;
104
105     @Inject
106     private UserRepositories userRepositories;
107
108     @Inject
109     private RepositoryContentFactory repositoryFactory;
110
111     @Inject
112     private List<RepositoryListener> listeners;
113
114     @Inject
115     private ManagedRepositoryAdmin managedRepositoryAdmin;
116
117     private ChecksumAlgorithm[] algorithms = new ChecksumAlgorithm[]{ ChecksumAlgorithm.SHA1, ChecksumAlgorithm.MD5 };
118
119     @PostConstruct
120     public void initialize()
121     {
122         super.initialize();
123     }
124
125     public String getGroupId()
126     {
127         return groupId;
128     }
129
130     public void setGroupId( String groupId )
131     {
132         this.groupId = groupId;
133     }
134
135     public String getArtifactId()
136     {
137         return artifactId;
138     }
139
140     public void setArtifactId( String artifactId )
141     {
142         this.artifactId = artifactId;
143     }
144
145     public String getVersion()
146     {
147         return version;
148     }
149
150     public void setVersion( String version )
151     {
152         this.version = version;
153     }
154
155     public String getRepositoryId()
156     {
157         return repositoryId;
158     }
159
160     public void setRepositoryId( String repositoryId )
161     {
162         this.repositoryId = repositoryId;
163     }
164
165     public List<String> getManagedRepos()
166     {
167         return managedRepos;
168     }
169
170     public void setManagedRepos( List<String> managedRepos )
171     {
172         this.managedRepos = managedRepos;
173     }
174
175     public void prepare()
176     {
177         managedRepos = getManagableRepos();
178     }
179
180     public String input()
181     {
182         return INPUT;
183     }
184
185     private void reset()
186     {
187         // reset the fields so the form is clear when 
188         // the action returns to the jsp page
189         groupId = "";
190         artifactId = "";
191         version = "";
192         repositoryId = "";
193     }
194
195     public String doDelete()
196     {
197
198         RepositorySession repositorySession = repositorySessionFactory.createSession();
199         try
200         {
201             Date lastUpdatedTimestamp = Calendar.getInstance().getTime();
202
203             TimeZone timezone = TimeZone.getTimeZone( "UTC" );
204             DateFormat fmt = new SimpleDateFormat( "yyyyMMdd.HHmmss" );
205             fmt.setTimeZone( timezone );
206             ManagedRepository repoConfig = getManagedRepositoryAdmin().getManagedRepository( repositoryId );
207
208             VersionedReference ref = new VersionedReference();
209             ref.setArtifactId( artifactId );
210             ref.setGroupId( groupId );
211             ref.setVersion( version );
212             ManagedRepositoryContent repository = repositoryFactory.getManagedRepositoryContent( repositoryId );
213
214             String path = repository.toMetadataPath( ref );
215             int index = path.lastIndexOf( '/' );
216             path = path.substring( 0, index );
217             File targetPath = new File( repoConfig.getLocation(), path );
218
219             if ( !targetPath.exists() )
220             {
221                 throw new ContentNotFoundException( groupId + ":" + artifactId + ":" + version );
222             }
223
224             // TODO: this should be in the storage mechanism so that it is all tied together
225             // delete from file system
226             repository.deleteVersion( ref );
227
228             File metadataFile = getMetadata( targetPath.getAbsolutePath() );
229             ArchivaRepositoryMetadata metadata = getMetadata( metadataFile );
230
231             updateMetadata( metadata, metadataFile, lastUpdatedTimestamp );
232
233             MetadataRepository metadataRepository = repositorySession.getRepository();
234             Collection<ArtifactMetadata> artifacts =
235                 metadataRepository.getArtifacts( repositoryId, groupId, artifactId, version );
236
237             for ( ArtifactMetadata artifact : artifacts )
238             {
239                 // TODO: mismatch between artifact (snapshot) version and project (base) version here
240                 if ( artifact.getVersion().equals( version ) )
241                 {
242                     metadataRepository.removeArtifact( artifact.getRepositoryId(), artifact.getNamespace(),
243                                                        artifact.getProject(), artifact.getVersion(), artifact.getId() );
244
245                     // TODO: move into the metadata repository proper - need to differentiate attachment of
246                     //       repository metadata to an artifact
247                     for ( RepositoryListener listener : listeners )
248                     {
249                         listener.deleteArtifact( metadataRepository, repository.getId(), artifact.getNamespace(),
250                                                  artifact.getProject(), artifact.getVersion(), artifact.getId() );
251                     }
252
253                     triggerAuditEvent( repositoryId, path, AuditEvent.REMOVE_FILE );
254                 }
255             }
256             repositorySession.save();
257         }
258         catch ( ContentNotFoundException e )
259         {
260             addActionError( "Artifact does not exist: " + e.getMessage() );
261             return ERROR;
262         }
263         catch ( RepositoryNotFoundException e )
264         {
265             addActionError( "Target repository cannot be found: " + e.getMessage() );
266             return ERROR;
267         }
268         catch ( RepositoryException e )
269         {
270             addActionError( "Repository exception: " + e.getMessage() );
271             return ERROR;
272         }
273         catch ( MetadataResolutionException e )
274         {
275             addActionError( "Repository exception: " + e.getMessage() );
276             return ERROR;
277         }
278         catch ( MetadataRepositoryException e )
279         {
280             addActionError( "Repository exception: " + e.getMessage() );
281             return ERROR;
282         }
283         catch ( RepositoryAdminException e )
284         {
285             addActionError( "RepositoryAdmin exception: " + e.getMessage() );
286             return ERROR;
287         }
288         finally
289         {
290             repositorySession.close();
291         }
292
293         String msg = "Artifact \'" + groupId + ":" + artifactId + ":" + version
294             + "\' was successfully deleted from repository \'" + repositoryId + "\'";
295
296         addActionMessage( msg );
297
298         reset();
299         return SUCCESS;
300     }
301
302     private File getMetadata( String targetPath )
303     {
304         String artifactPath = targetPath.substring( 0, targetPath.lastIndexOf( File.separatorChar ) );
305
306         return new File( artifactPath, MetadataTools.MAVEN_METADATA );
307     }
308
309     private ArchivaRepositoryMetadata getMetadata( File metadataFile )
310         throws RepositoryMetadataException
311     {
312         ArchivaRepositoryMetadata metadata = new ArchivaRepositoryMetadata();
313         if ( metadataFile.exists() )
314         {
315             metadata = RepositoryMetadataReader.read( metadataFile );
316         }
317         return metadata;
318     }
319
320     /**
321      * Update artifact level metadata. Creates one if metadata does not exist after artifact deletion.
322      *
323      * @param metadata
324      */
325     private void updateMetadata( ArchivaRepositoryMetadata metadata, File metadataFile, Date lastUpdatedTimestamp )
326         throws RepositoryMetadataException
327     {
328         List<String> availableVersions = new ArrayList<String>();
329         String latestVersion = "";
330
331         if ( metadataFile.exists() )
332         {
333             if ( metadata.getAvailableVersions() != null )
334             {
335                 availableVersions = metadata.getAvailableVersions();
336
337                 if ( availableVersions.size() > 0 )
338                 {
339                     Collections.sort( availableVersions, VersionComparator.getInstance() );
340
341                     if ( availableVersions.contains( version ) )
342                     {
343                         availableVersions.remove( availableVersions.indexOf( version ) );
344                     }
345                     if ( availableVersions.size() > 0 )
346                     {
347                         latestVersion = availableVersions.get( availableVersions.size() - 1 );
348                     }
349                 }
350             }
351         }
352
353         if ( metadata.getGroupId() == null )
354         {
355             metadata.setGroupId( groupId );
356         }
357         if ( metadata.getArtifactId() == null )
358         {
359             metadata.setArtifactId( artifactId );
360         }
361
362         if ( !VersionUtil.isSnapshot( version ) )
363         {
364             if ( metadata.getReleasedVersion() != null && metadata.getReleasedVersion().equals( version ) )
365             {
366                 metadata.setReleasedVersion( latestVersion );
367             }
368         }
369
370         metadata.setLatestVersion( latestVersion );
371         metadata.setLastUpdatedTimestamp( lastUpdatedTimestamp );
372         metadata.setAvailableVersions( availableVersions );
373
374         RepositoryMetadataWriter.write( metadata, metadataFile );
375         ChecksummedFile checksum = new ChecksummedFile( metadataFile );
376         checksum.fixChecksums( algorithms );
377     }
378
379     public void validate()
380     {
381         try
382         {
383             if ( !userRepositories.isAuthorizedToDeleteArtifacts( getPrincipal(), repositoryId ) )
384             {
385                 addActionError( "User is not authorized to delete artifacts in repository '" + repositoryId + "'." );
386             }
387
388             if ( ( version.length() > 0 ) && ( !VersionUtil.isVersion( version ) ) )
389             {
390                 addActionError( "Invalid version." );
391             }
392         }
393         catch ( AccessDeniedException e )
394         {
395             addActionError( e.getMessage() );
396         }
397         catch ( ArchivaSecurityException e )
398         {
399             addActionError( e.getMessage() );
400         }
401
402         // trims all request parameter values, since the trailing/leading white-spaces are ignored during validation.
403         trimAllRequestParameterValues();
404     }
405
406     private List<String> getManagableRepos()
407     {
408         try
409         {
410             return userRepositories.getManagableRepositoryIds( getPrincipal() );
411         }
412         catch ( PrincipalNotFoundException e )
413         {
414             log.warn( e.getMessage(), e );
415         }
416         catch ( AccessDeniedException e )
417         {
418             log.warn( e.getMessage(), e );
419             // TODO: pass this onto the screen.
420         }
421         catch ( ArchivaSecurityException e )
422         {
423             log.warn( e.getMessage(), e );
424         }
425         return Collections.emptyList();
426     }
427
428     private void trimAllRequestParameterValues()
429     {
430         if ( StringUtils.isNotEmpty( groupId ) )
431         {
432             groupId = groupId.trim();
433         }
434
435         if ( StringUtils.isNotEmpty( artifactId ) )
436         {
437             artifactId = artifactId.trim();
438         }
439
440         if ( StringUtils.isNotEmpty( version ) )
441         {
442             version = version.trim();
443         }
444
445         if ( StringUtils.isNotEmpty( repositoryId ) )
446         {
447             repositoryId = repositoryId.trim();
448         }
449     }
450
451     public List<RepositoryListener> getListeners()
452     {
453         return listeners;
454     }
455
456     public void setRepositoryFactory( RepositoryContentFactory repositoryFactory )
457     {
458         this.repositoryFactory = repositoryFactory;
459     }
460
461     public ManagedRepositoryAdmin getManagedRepositoryAdmin()
462     {
463         return managedRepositoryAdmin;
464     }
465
466     public void setManagedRepositoryAdmin( ManagedRepositoryAdmin managedRepositoryAdmin )
467     {
468         this.managedRepositoryAdmin = managedRepositoryAdmin;
469     }
470 }