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