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