]> source.dussan.org Git - archiva.git/blob
c39850c1a66de45dc62bdd008821544d89f9d76d
[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.io.FileInputStream;
24 import java.io.FileOutputStream;
25 import java.io.IOException;
26 import java.text.DateFormat;
27 import java.text.SimpleDateFormat;
28 import java.util.ArrayList;
29 import java.util.Calendar;
30 import java.util.Date;
31 import java.util.Collections;
32 import java.util.List;
33 import java.util.TimeZone;
34
35 import org.apache.archiva.checksum.ChecksumAlgorithm;
36 import org.apache.archiva.checksum.ChecksummedFile;
37 import org.apache.maven.archiva.common.utils.VersionComparator;
38 import org.apache.maven.archiva.common.utils.VersionUtil;
39 import org.apache.maven.archiva.configuration.ArchivaConfiguration;
40 import org.apache.maven.archiva.configuration.ManagedRepositoryConfiguration;
41 import org.apache.maven.archiva.model.ArchivaProjectModel;
42 import org.apache.maven.archiva.model.ArchivaRepositoryMetadata;
43 import org.apache.maven.archiva.model.ArtifactReference;
44 import org.apache.maven.archiva.model.SnapshotVersion;
45 import org.apache.maven.archiva.repository.ManagedRepositoryContent;
46 import org.apache.maven.archiva.repository.RepositoryContentFactory;
47 import org.apache.maven.archiva.repository.RepositoryException;
48 import org.apache.maven.archiva.repository.RepositoryNotFoundException;
49 import org.apache.maven.archiva.repository.scanner.RepositoryContentConsumers;
50 import org.apache.maven.archiva.repository.audit.AuditEvent;
51 import org.apache.maven.archiva.repository.audit.AuditListener;
52 import org.apache.maven.archiva.repository.audit.Auditable;
53 import org.apache.maven.archiva.repository.metadata.MetadataTools;
54 import org.apache.maven.archiva.repository.metadata.RepositoryMetadataException;
55 import org.apache.maven.archiva.repository.metadata.RepositoryMetadataReader;
56 import org.apache.maven.archiva.repository.metadata.RepositoryMetadataWriter;
57 import org.apache.maven.archiva.repository.project.ProjectModelException;
58 import org.apache.maven.archiva.repository.project.ProjectModelWriter;
59 import org.apache.maven.archiva.repository.project.writers.ProjectModel400Writer;
60 import org.apache.maven.archiva.security.AccessDeniedException;
61 import org.apache.maven.archiva.security.ArchivaSecurityException;
62 import org.apache.maven.archiva.security.PrincipalNotFoundException;
63 import org.apache.maven.archiva.security.UserRepositories;
64 import org.apache.maven.archiva.security.ArchivaXworkUser;
65
66 import org.apache.struts2.ServletActionContext;
67 import com.opensymphony.xwork2.ActionContext;
68 import com.opensymphony.xwork2.Preparable;
69 import com.opensymphony.xwork2.Validateable;
70 import org.apache.commons.io.FilenameUtils;
71 import org.apache.commons.lang.StringUtils;
72
73 /**
74  * Upload an artifact using Jakarta file upload in webwork. If set by the user a pom will also be generated. Metadata
75  * will also be updated if one exists, otherwise it would be created.
76  * 
77  * @plexus.component role="com.opensymphony.xwork2.Action" role-hint="uploadAction" instantiation-strategy="per-lookup"
78  */
79 public class UploadAction
80     extends PlexusActionSupport
81     implements Validateable, Preparable, Auditable
82 {
83     /**
84       * @plexus.requirement
85       */
86      private RepositoryContentConsumers consumers;
87      
88      /**
89       * @plexus.requirement
90       */
91      private ArchivaXworkUser archivaXworkUser;
92     
93     /**
94      * The groupId of the artifact to be deployed.
95      */
96     private String groupId;
97
98     /**
99      * The artifactId of the artifact to be deployed.
100      */
101     private String artifactId;
102
103     /**
104      * The version of the artifact to be deployed.
105      */
106     private String version;
107
108     /**
109      * The packaging of the artifact to be deployed.
110      */
111     private String packaging;
112
113     /**
114      * The classifier of the artifact to be deployed.
115      */
116     private String classifier;
117
118     /**
119      * The temporary file representing the artifact to be deployed.
120      */
121     private File artifactFile;
122
123     /**
124      * The temporary file representing the pom to be deployed alongside the artifact.
125      */
126     private File pomFile;
127
128     /**
129      * The repository where the artifact is to be deployed.
130      */
131     private String repositoryId;
132
133     /**
134      * Flag whether to generate a pom for the artifact or not.
135      */
136     private boolean generatePom;
137
138     /**
139      * List of managed repositories to deploy to.
140      */
141     private List<String> managedRepoIdList;
142
143     /**
144      * @plexus.requirement
145      */
146     private UserRepositories userRepositories;
147
148     /**
149      * @plexus.requirement role-hint="default"
150      */
151     private ArchivaConfiguration configuration;
152
153     /**
154      * @plexus.requirement
155      */
156     private RepositoryContentFactory repositoryFactory;
157     
158     /**
159      * @plexus.requirement role="org.apache.maven.archiva.repository.audit.AuditListener"
160      */
161     private List<AuditListener> auditListeners = new ArrayList<AuditListener>();
162     
163     private ChecksumAlgorithm[] algorithms = new ChecksumAlgorithm[] { ChecksumAlgorithm.SHA1, ChecksumAlgorithm.MD5 };
164
165     private ProjectModelWriter pomWriter = new ProjectModel400Writer();
166     
167     public void setArtifact( File file )
168     {
169         this.artifactFile = file;
170     }
171
172     public void setArtifactContentType( String contentType )
173     {
174         StringUtils.trim( contentType );
175     }
176
177     public void setArtifactFileName( String filename )
178     {
179         StringUtils.trim( filename );
180     }
181
182     public void setPom( File file )
183     {
184         this.pomFile = file;
185     }
186
187     public void setPomContentType( String contentType )
188     {
189         StringUtils.trim( contentType );
190     }
191
192     public void setPomFileName( String filename )
193     {
194         StringUtils.trim( filename );
195     }
196
197     public String getGroupId()
198     {
199         return groupId;
200     }
201
202     public void setGroupId( String groupId )
203     {
204         this.groupId = StringUtils.trim( groupId );
205     }
206
207     public String getArtifactId()
208     {
209         return artifactId;
210     }
211
212     public void setArtifactId( String artifactId )
213     {
214         this.artifactId = StringUtils.trim( artifactId );
215     }
216
217     public String getVersion()
218     {
219         return version;
220     }
221
222     public void setVersion( String version )
223     {
224         this.version = StringUtils.trim( version );
225     }
226
227     public String getPackaging()
228     {
229         return packaging;
230     }
231
232     public void setPackaging( String packaging )
233     {
234         this.packaging = StringUtils.trim( packaging );
235     }
236
237     public String getClassifier()
238     {
239         return classifier;
240     }
241
242     public void setClassifier( String classifier )
243     {
244         this.classifier = StringUtils.trim( classifier );
245     }
246
247     public String getRepositoryId()
248     {
249         return repositoryId;
250     }
251
252     public void setRepositoryId( String repositoryId )
253     {
254         this.repositoryId = repositoryId;
255     }
256
257     public boolean isGeneratePom()
258     {
259         return generatePom;
260     }
261
262     public void setGeneratePom( boolean generatePom )
263     {
264         this.generatePom = generatePom;
265     }
266
267     public List<String> getManagedRepoIdList()
268     {
269         return managedRepoIdList;
270     }
271
272     public void setManagedRepoIdList( List<String> managedRepoIdList )
273     {
274         this.managedRepoIdList = managedRepoIdList;
275     }
276
277     public void prepare()
278     {
279         managedRepoIdList = getManagableRepos();
280     }
281
282     public String input()
283     {
284         return INPUT;
285     }
286
287     private void reset()
288     {
289         // reset the fields so the form is clear when 
290         // the action returns to the jsp page
291         groupId = "";
292         artifactId = "";
293         version = "";
294         packaging = "";
295         classifier = "";
296         artifactFile = null;
297         pomFile = null;
298         repositoryId = "";
299         generatePom = false;
300     }
301     
302     public String doUpload()
303     {
304         try
305         {
306             ManagedRepositoryConfiguration repoConfig =
307                 configuration.getConfiguration().findManagedRepositoryById( repositoryId );
308
309             ArtifactReference artifactReference = new ArtifactReference();
310             artifactReference.setArtifactId( artifactId );
311             artifactReference.setGroupId( groupId );
312             artifactReference.setVersion( version );
313             artifactReference.setClassifier( classifier );
314             artifactReference.setType( packaging );
315
316             ManagedRepositoryContent repository = repositoryFactory.getManagedRepositoryContent( repositoryId );
317
318             String artifactPath = repository.toPath( artifactReference );
319
320             int lastIndex = artifactPath.lastIndexOf( '/' );
321
322             File targetPath = new File( repoConfig.getLocation(), artifactPath.substring( 0, lastIndex ) );
323
324             Date lastUpdatedTimestamp = Calendar.getInstance().getTime();
325             int newBuildNumber = -1;
326             String timestamp = null;
327             
328             File metadataFile = getMetadata( targetPath.getAbsolutePath() );
329             ArchivaRepositoryMetadata metadata = getMetadata( metadataFile );
330
331             if (VersionUtil.isSnapshot(version))
332             {
333                 TimeZone timezone = TimeZone.getTimeZone( "UTC" );
334                 DateFormat fmt = new SimpleDateFormat( "yyyyMMdd.HHmmss" );
335                 fmt.setTimeZone( timezone );
336                 timestamp = fmt.format( lastUpdatedTimestamp );
337                 if ( metadata.getSnapshotVersion() != null )
338                 {
339                     newBuildNumber = metadata.getSnapshotVersion().getBuildNumber() + 1;
340                 }
341                 else
342                 {
343                         metadata.setSnapshotVersion( new SnapshotVersion() );
344                         newBuildNumber = 1;
345                 }
346             }
347
348             if ( !targetPath.exists() )
349             {
350                 targetPath.mkdirs();
351             }
352
353             String filename = artifactPath.substring( lastIndex + 1 );
354             if ( VersionUtil.isSnapshot( version ) )
355             {
356                 filename = filename.replaceAll( "SNAPSHOT", timestamp + "-" + newBuildNumber );
357             }
358
359             try
360             {
361                 copyFile( artifactFile, targetPath, filename );
362                 consumers.executeConsumers( repoConfig, repository.toFile( artifactReference ) );
363             }
364             catch ( IOException ie )
365             {
366                 addActionError( "Error encountered while uploading file: " + ie.getMessage() );
367                 return ERROR;
368             }
369
370             String pomFilename = filename;
371             if( classifier != null && !"".equals( classifier ) )
372             {
373                 pomFilename = StringUtils.remove( pomFilename, "-" + classifier );
374             }
375             pomFilename = FilenameUtils.removeExtension( pomFilename ) + ".pom";
376                 
377             if ( generatePom )
378             {
379                 try
380                 {
381                     File generatedPomFile = createPom( targetPath, pomFilename );
382                     consumers.executeConsumers( repoConfig, generatedPomFile );
383                 }
384                 catch ( IOException ie )
385                 {
386                     addActionError( "Error encountered while writing pom file: " + ie.getMessage() );
387                     return ERROR;
388                 }
389                 catch ( ProjectModelException pe )
390                 {
391                     addActionError( "Error encountered while generating pom file: " + pe.getMessage() );
392                     return ERROR;
393                 }
394             }
395             
396             if ( pomFile != null && pomFile.length() > 0 ) 
397             {
398                 try
399                 {                    
400                     copyFile( pomFile, targetPath, pomFilename );
401                     consumers.executeConsumers( repoConfig, new File( targetPath, pomFilename ) );
402                 }
403                 catch ( IOException ie )
404                 {
405                     addActionError( "Error encountered while uploading pom file: " + ie.getMessage() );
406                     return ERROR;
407                 }
408                 
409             }
410
411             updateMetadata( metadata, metadataFile, lastUpdatedTimestamp, timestamp, newBuildNumber );
412
413             String msg = "Artifact \'" + groupId + ":" + artifactId + ":" + version +
414                 "\' was successfully deployed to repository \'" + repositoryId + "\'";
415                         
416             triggerAuditEvent( getPrincipal(), repositoryId, groupId + ":" + artifactId + ":" + version, AuditEvent.UPLOAD_FILE );
417             
418             addActionMessage( msg );
419
420             reset();
421             return SUCCESS;
422         }
423         catch ( RepositoryNotFoundException re )
424         {
425             addActionError( "Target repository cannot be found: " + re.getMessage() );
426             return ERROR;
427         }
428         catch ( RepositoryException rep )
429         {
430             addActionError( "Repository exception: " + rep.getMessage() );
431             return ERROR;
432         }
433     }
434
435     @SuppressWarnings("unchecked")
436     private String getPrincipal()
437     {
438         return archivaXworkUser.getActivePrincipal( ActionContext.getContext().getSession() );
439     }
440
441     private void copyFile( File sourceFile, File targetPath, String targetFilename )
442         throws IOException
443     {
444         FileOutputStream out = new FileOutputStream( new File( targetPath, targetFilename ) );
445
446         try
447         {
448             FileInputStream input = new FileInputStream( sourceFile );
449             int i = 0;
450             while ( ( i = input.read() ) != -1 )
451             {
452                 out.write( i );
453             }
454             out.flush();
455         }
456         finally
457         {
458             out.close();
459         }
460     }
461
462     private File createPom( File targetPath, String filename )
463         throws IOException, ProjectModelException
464     {
465         ArchivaProjectModel projectModel = new ArchivaProjectModel();
466         projectModel.setGroupId( groupId );
467         projectModel.setArtifactId( artifactId );
468         projectModel.setVersion( version );
469         projectModel.setPackaging( packaging );
470         
471         File pomFile = new File( targetPath, filename);        
472         pomWriter.write( projectModel, pomFile );
473
474         return pomFile;
475     }
476
477     private File getMetadata( String targetPath )
478     {
479         String artifactPath = targetPath.substring( 0, targetPath.lastIndexOf( File.separatorChar ) );
480
481         return new File( artifactPath, MetadataTools.MAVEN_METADATA );
482     }
483
484     private ArchivaRepositoryMetadata getMetadata( File metadataFile )
485         throws RepositoryMetadataException
486     {
487         ArchivaRepositoryMetadata metadata = new ArchivaRepositoryMetadata();
488         if ( metadataFile.exists() )
489         {
490             metadata = RepositoryMetadataReader.read( metadataFile );
491         }
492         return metadata;
493     }
494
495     /**
496      * Update artifact level metadata. If it does not exist, create the metadata.
497      * 
498      * @param metadata
499      */
500     private void updateMetadata( ArchivaRepositoryMetadata metadata, File metadataFile, Date lastUpdatedTimestamp,
501                                  String timestamp, int buildNumber )
502         throws RepositoryMetadataException
503     {
504         List<String> availableVersions = new ArrayList<String>();
505         String latestVersion = version;
506
507         if ( metadataFile.exists() )
508         {
509             availableVersions = metadata.getAvailableVersions();
510
511             Collections.sort( availableVersions, VersionComparator.getInstance() );
512
513             if ( !availableVersions.contains( version ) )
514             {
515                 availableVersions.add( version );
516             }
517
518             latestVersion = availableVersions.get( availableVersions.size() - 1 );
519         }
520         else
521         {
522             availableVersions.add( version );
523
524             metadata.setGroupId( groupId );
525             metadata.setArtifactId( artifactId );
526         }
527
528         if ( metadata.getGroupId() == null )
529         {
530                 metadata.setGroupId( groupId );
531         }
532         if ( metadata.getArtifactId() == null )
533         {
534                 metadata.setArtifactId( artifactId );
535         }
536
537         metadata.setLatestVersion( latestVersion );
538         metadata.setLastUpdatedTimestamp( lastUpdatedTimestamp );
539         metadata.setAvailableVersions( availableVersions );
540
541         if ( !VersionUtil.isSnapshot( version ) )
542         {
543             metadata.setReleasedVersion( latestVersion );
544         }
545         else
546         {
547             metadata.getSnapshotVersion().setBuildNumber( buildNumber );
548
549             metadata.getSnapshotVersion().setTimestamp( timestamp );
550         }
551
552         RepositoryMetadataWriter.write( metadata, metadataFile );
553         ChecksummedFile checksum = new ChecksummedFile( metadataFile );
554         checksum.fixChecksums( algorithms );
555     }
556
557     public void validate()
558     {
559         try
560         {
561             // is this enough check for the repository permission?
562             if ( !userRepositories.isAuthorizedToUploadArtifacts( getPrincipal(), repositoryId ) )
563             {
564                 addActionError( "User is not authorized to upload in repository " + repositoryId );
565             }
566
567             if ( artifactFile == null || artifactFile.length() == 0 )
568             {
569                 addActionError( "Please add a file to upload." );
570             }
571             
572             if ( !VersionUtil.isVersion( version ) )
573             {
574                 addActionError( "Invalid version." );
575             }            
576         }
577         catch ( PrincipalNotFoundException pe )
578         {
579             addActionError( pe.getMessage() );
580         }
581         catch ( ArchivaSecurityException ae )
582         {
583             addActionError( ae.getMessage() );
584         }
585     }
586     
587     public void addAuditListener( AuditListener listener )
588     {
589         this.auditListeners.add( listener );
590     }
591
592     public void clearAuditListeners()
593     {
594         this.auditListeners.clear();
595     }
596
597     public void removeAuditListener( AuditListener listener )
598     {
599         this.auditListeners.remove( listener );
600     }
601     
602     private List<String> getManagableRepos()
603     {
604         try
605         {
606             return userRepositories.getManagableRepositoryIds( getPrincipal() );
607         }
608         catch ( PrincipalNotFoundException e )
609         {
610             log.warn( e.getMessage(), e );
611         }
612         catch ( AccessDeniedException e )
613         {
614             log.warn( e.getMessage(), e );
615             // TODO: pass this onto the screen.
616         }
617         catch ( ArchivaSecurityException e )
618         {
619             log.warn( e.getMessage(), e );
620         }
621         return Collections.emptyList();
622     }
623
624     private void triggerAuditEvent( String user, String repositoryId, String resource, String action )
625     {
626         AuditEvent event = new AuditEvent( repositoryId, user, resource, action );
627         event.setRemoteIP( ServletActionContext.getRequest().getRemoteAddr() );
628         
629         for ( AuditListener listener : auditListeners )
630         {
631             listener.auditEvent( event );
632         }
633     }
634 }