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