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