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