]> source.dussan.org Git - archiva.git/blob
fccc0ffd03fd94abc9aa0339c3da6e9146d576dd
[archiva.git] /
1 package org.apache.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.admin.model.RepositoryAdminException;
25 import org.apache.archiva.admin.model.admin.ArchivaAdministration;
26 import org.apache.archiva.admin.model.beans.ManagedRepository;
27 import org.apache.archiva.admin.model.managed.ManagedRepositoryAdmin;
28 import org.apache.archiva.audit.AuditEvent;
29 import org.apache.archiva.audit.Auditable;
30 import org.apache.archiva.checksum.ChecksumAlgorithm;
31 import org.apache.archiva.checksum.ChecksummedFile;
32 import org.apache.archiva.common.utils.VersionComparator;
33 import org.apache.archiva.common.utils.VersionUtil;
34 import org.apache.archiva.maven2.metadata.MavenMetadataReader;
35 import org.apache.archiva.model.ArchivaRepositoryMetadata;
36 import org.apache.archiva.model.ArtifactReference;
37 import org.apache.archiva.model.SnapshotVersion;
38 import org.apache.archiva.repository.ManagedRepositoryContent;
39 import org.apache.archiva.repository.RepositoryContentFactory;
40 import org.apache.archiva.repository.RepositoryException;
41 import org.apache.archiva.repository.RepositoryNotFoundException;
42 import org.apache.archiva.repository.metadata.MetadataTools;
43 import org.apache.archiva.repository.metadata.RepositoryMetadataException;
44 import org.apache.archiva.repository.metadata.RepositoryMetadataWriter;
45 import org.apache.archiva.scheduler.ArchivaTaskScheduler;
46 import org.apache.archiva.scheduler.repository.RepositoryTask;
47 import org.apache.archiva.security.AccessDeniedException;
48 import org.apache.archiva.security.ArchivaSecurityException;
49 import org.apache.archiva.security.PrincipalNotFoundException;
50 import org.apache.archiva.security.UserRepositories;
51 import org.apache.archiva.xml.XMLException;
52 import org.apache.commons.io.FilenameUtils;
53 import org.apache.commons.io.IOUtils;
54 import org.apache.commons.lang.StringUtils;
55 import org.apache.maven.model.Model;
56 import org.apache.maven.model.io.xpp3.MavenXpp3Writer;
57 import org.codehaus.plexus.taskqueue.TaskQueueException;
58 import org.codehaus.plexus.util.IOUtil;
59 import org.springframework.context.annotation.Scope;
60 import org.springframework.stereotype.Controller;
61
62 import javax.inject.Inject;
63 import javax.inject.Named;
64 import java.io.File;
65 import java.io.FileInputStream;
66 import java.io.FileOutputStream;
67 import java.io.FileWriter;
68 import java.io.IOException;
69 import java.text.DateFormat;
70 import java.text.SimpleDateFormat;
71 import java.util.ArrayList;
72 import java.util.Calendar;
73 import java.util.Collections;
74 import java.util.Date;
75 import java.util.List;
76 import java.util.TimeZone;
77
78 /**
79  * Upload an artifact using Jakarta file upload in webwork. If set by the user a pom will also be generated. Metadata
80  * will also be updated if one exists, otherwise it would be created.
81  */
82 @SuppressWarnings( "serial" )
83 @Controller( "uploadAction" )
84 @Scope( "prototype" )
85 public class UploadAction
86     extends AbstractActionSupport
87     implements Validateable, Preparable, Auditable
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 temporary file representing the pom to be deployed alongside the artifact.
121      */
122     private File pomFile;
123
124     /**
125      * The repository where the artifact is to be deployed.
126      */
127     private String repositoryId;
128
129     /**
130      * Flag whether to generate a pom for the artifact or not.
131      */
132     private boolean generatePom;
133
134     /**
135      * List of managed repositories to deploy to.
136      */
137     private List<String> managedRepoIdList;
138
139     @Inject
140     private ManagedRepositoryAdmin managedRepositoryAdmin;
141
142     @Inject
143     private UserRepositories userRepositories;
144
145     @Inject
146     private ArchivaAdministration archivaAdministration;
147
148     @Inject
149     private RepositoryContentFactory repositoryFactory;
150
151     @Inject
152     @Named( value = "archivaTaskScheduler#repository" )
153     private ArchivaTaskScheduler scheduler;
154
155     private ChecksumAlgorithm[] algorithms = new ChecksumAlgorithm[]{ ChecksumAlgorithm.SHA1, ChecksumAlgorithm.MD5 };
156
157     public void setArtifact( File file )
158     {
159         this.artifactFile = file;
160     }
161
162     public void setArtifactContentType( String contentType )
163     {
164         StringUtils.trim( contentType );
165     }
166
167     public void setArtifactFileName( String filename )
168     {
169         StringUtils.trim( filename );
170     }
171
172     public void setPom( File file )
173     {
174         this.pomFile = file;
175     }
176
177     public void setPomContentType( String contentType )
178     {
179         StringUtils.trim( contentType );
180     }
181
182     public void setPomFileName( String filename )
183     {
184         StringUtils.trim( filename );
185     }
186
187     public String getGroupId()
188     {
189         return groupId;
190     }
191
192     public void setGroupId( String groupId )
193     {
194         this.groupId = StringUtils.trim( groupId );
195     }
196
197     public String getArtifactId()
198     {
199         return artifactId;
200     }
201
202     public void setArtifactId( String artifactId )
203     {
204         this.artifactId = StringUtils.trim( artifactId );
205     }
206
207     public String getVersion()
208     {
209         return version;
210     }
211
212     public void setVersion( String version )
213     {
214         this.version = StringUtils.trim( version );
215     }
216
217     public String getPackaging()
218     {
219         return packaging;
220     }
221
222     public void setPackaging( String packaging )
223     {
224         this.packaging = StringUtils.trim( packaging );
225     }
226
227     public String getClassifier()
228     {
229         return classifier;
230     }
231
232     public void setClassifier( String classifier )
233     {
234         this.classifier = StringUtils.trim( classifier );
235     }
236
237     public String getRepositoryId()
238     {
239         return repositoryId;
240     }
241
242     public void setRepositoryId( String repositoryId )
243     {
244         this.repositoryId = repositoryId;
245     }
246
247     public boolean isGeneratePom()
248     {
249         return generatePom;
250     }
251
252     public void setGeneratePom( boolean generatePom )
253     {
254         this.generatePom = generatePom;
255     }
256
257     public List<String> getManagedRepoIdList()
258     {
259         return managedRepoIdList;
260     }
261
262     public void setManagedRepoIdList( List<String> managedRepoIdList )
263     {
264         this.managedRepoIdList = managedRepoIdList;
265     }
266
267     public void prepare()
268     {
269         managedRepoIdList = getManagableRepos();
270     }
271
272     public String input()
273     {
274         return INPUT;
275     }
276
277     private void reset()
278     {
279         // reset the fields so the form is clear when 
280         // the action returns to the jsp page
281         groupId = "";
282         artifactId = "";
283         version = "";
284         packaging = "";
285         classifier = "";
286         artifactFile = null;
287         pomFile = null;
288         repositoryId = "";
289         generatePom = false;
290     }
291
292     public String doUpload()
293     {
294         try
295         {
296             ManagedRepository repoConfig = managedRepositoryAdmin.getManagedRepository( repositoryId );
297
298             ArtifactReference artifactReference = new ArtifactReference();
299             artifactReference.setArtifactId( artifactId );
300             artifactReference.setGroupId( groupId );
301             artifactReference.setVersion( version );
302             artifactReference.setClassifier( classifier );
303             artifactReference.setType( packaging );
304
305             ManagedRepositoryContent repository = repositoryFactory.getManagedRepositoryContent( repositoryId );
306
307             String artifactPath = repository.toPath( artifactReference );
308
309             int lastIndex = artifactPath.lastIndexOf( '/' );
310
311             String path = artifactPath.substring( 0, lastIndex );
312             File targetPath = new File( repoConfig.getLocation(), path );
313
314             log.debug( "artifactPath: {} found targetPath: {}", artifactPath, targetPath );
315
316             Date lastUpdatedTimestamp = Calendar.getInstance().getTime();
317             int newBuildNumber = -1;
318             String timestamp = null;
319
320             File versionMetadataFile = new File( targetPath, MetadataTools.MAVEN_METADATA );
321             ArchivaRepositoryMetadata versionMetadata = getMetadata( versionMetadataFile );
322
323             if ( VersionUtil.isSnapshot( version ) )
324             {
325                 TimeZone timezone = TimeZone.getTimeZone( "UTC" );
326                 DateFormat fmt = new SimpleDateFormat( "yyyyMMdd.HHmmss" );
327                 fmt.setTimeZone( timezone );
328                 timestamp = fmt.format( lastUpdatedTimestamp );
329                 if ( versionMetadata.getSnapshotVersion() != null )
330                 {
331                     newBuildNumber = versionMetadata.getSnapshotVersion().getBuildNumber() + 1;
332                 }
333                 else
334                 {
335                     newBuildNumber = 1;
336                 }
337             }
338
339             if ( !targetPath.exists() )
340             {
341                 targetPath.mkdirs();
342             }
343
344             String filename = artifactPath.substring( lastIndex + 1 );
345             if ( VersionUtil.isSnapshot( version ) )
346             {
347                 filename = filename.replaceAll( "SNAPSHOT", timestamp + "-" + newBuildNumber );
348             }
349
350             boolean fixChecksums =
351                 !( archivaAdministration.getKnownContentConsumers().contains( "create-missing-checksums" ) );
352
353             try
354             {
355                 File targetFile = new File( targetPath, filename );
356                 if ( targetFile.exists() && !VersionUtil.isSnapshot( version ) && repoConfig.isBlockRedeployments() )
357                 {
358                     addActionError(
359                         "Overwriting released artifacts in repository '" + repoConfig.getId() + "' is not allowed." );
360                     return ERROR;
361                 }
362                 else
363                 {
364                     copyFile( artifactFile, targetPath, filename, fixChecksums );
365                     triggerAuditEvent( repository.getId(), path + "/" + filename, AuditEvent.UPLOAD_FILE );
366                     queueRepositoryTask( repository.getId(), targetFile );
367                 }
368             }
369             catch ( IOException ie )
370             {
371                 addActionError( "Error encountered while uploading file: " + ie.getMessage() );
372                 return ERROR;
373             }
374
375             String pomFilename = filename;
376             if ( classifier != null && !"".equals( classifier ) )
377             {
378                 pomFilename = StringUtils.remove( pomFilename, "-" + classifier );
379             }
380             pomFilename = FilenameUtils.removeExtension( pomFilename ) + ".pom";
381
382             if ( generatePom )
383             {
384                 try
385                 {
386                     File generatedPomFile = createPom( targetPath, pomFilename );
387                     triggerAuditEvent( repoConfig.getId(), path + "/" + pomFilename, AuditEvent.UPLOAD_FILE );
388                     if ( fixChecksums )
389                     {
390                         fixChecksums( generatedPomFile );
391                     }
392                     queueRepositoryTask( repoConfig.getId(), generatedPomFile );
393                 }
394                 catch ( IOException ie )
395                 {
396                     addActionError( "Error encountered while writing pom file: " + ie.getMessage() );
397                     return ERROR;
398                 }
399             }
400
401             if ( pomFile != null && pomFile.length() > 0 )
402             {
403                 try
404                 {
405                     copyFile( pomFile, targetPath, pomFilename, fixChecksums );
406                     triggerAuditEvent( repoConfig.getId(), path + "/" + pomFilename, AuditEvent.UPLOAD_FILE );
407                     queueRepositoryTask( repoConfig.getId(), new File( targetPath, pomFilename ) );
408                 }
409                 catch ( IOException ie )
410                 {
411                     addActionError( "Error encountered while uploading pom file: " + ie.getMessage() );
412                     return ERROR;
413                 }
414
415             }
416
417             // explicitly update only if metadata-updater consumer is not enabled!
418             if ( !archivaAdministration.getKnownContentConsumers().contains( "metadata-updater" ) )
419             {
420                 updateProjectMetadata( targetPath.getAbsolutePath(), lastUpdatedTimestamp, timestamp, newBuildNumber,
421                                        fixChecksums );
422
423                 if ( VersionUtil.isSnapshot( version ) )
424                 {
425                     updateVersionMetadata( versionMetadata, versionMetadataFile, lastUpdatedTimestamp, timestamp,
426                                            newBuildNumber, fixChecksums );
427                 }
428             }
429
430             String msg = "Artifact \'" + groupId + ":" + artifactId + ":" + version
431                 + "\' was successfully deployed to repository \'" + repositoryId + "\'";
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         catch ( RepositoryAdminException e )
449         {
450             addActionError( "RepositoryAdmin exception: " + e.getMessage() );
451             return ERROR;
452         }
453     }
454
455     private void fixChecksums( File file )
456     {
457         ChecksummedFile checksum = new ChecksummedFile( file );
458         checksum.fixChecksums( algorithms );
459     }
460
461     private void copyFile( File sourceFile, File targetPath, String targetFilename, boolean fixChecksums )
462         throws IOException
463     {
464         FileOutputStream out = new FileOutputStream( new File( targetPath, targetFilename ) );
465         FileInputStream input = new FileInputStream( sourceFile );
466
467         try
468         {
469             IOUtils.copy( input, out );
470         }
471         finally
472         {
473             out.close();
474             input.close();
475         }
476
477         if ( fixChecksums )
478         {
479             fixChecksums( new File( targetPath, targetFilename ) );
480         }
481     }
482
483     private File createPom( File targetPath, String filename )
484         throws IOException
485     {
486         Model projectModel = new Model();
487         projectModel.setModelVersion( "4.0.0" );
488         projectModel.setGroupId( groupId );
489         projectModel.setArtifactId( artifactId );
490         projectModel.setVersion( version );
491         projectModel.setPackaging( packaging );
492
493         File pomFile = new File( targetPath, filename );
494         MavenXpp3Writer writer = new MavenXpp3Writer();
495         FileWriter w = new FileWriter( pomFile );
496         try
497         {
498             writer.write( w, projectModel );
499         }
500         finally
501         {
502             IOUtil.close( w );
503         }
504
505         return pomFile;
506     }
507
508     private ArchivaRepositoryMetadata getMetadata( File metadataFile )
509         throws RepositoryMetadataException
510     {
511         ArchivaRepositoryMetadata metadata = new ArchivaRepositoryMetadata();
512         if ( metadataFile.exists() )
513         {
514             try
515             {
516                 metadata = MavenMetadataReader.read( metadataFile );
517             }
518             catch ( XMLException e )
519             {
520                 throw new RepositoryMetadataException( e.getMessage(), e );
521             }
522         }
523         return metadata;
524     }
525
526
527     /**
528      * Update version level metadata for snapshot artifacts. If it does not exist, create the metadata and fix checksums
529      * if necessary.
530      */
531     private void updateVersionMetadata( ArchivaRepositoryMetadata metadata, File metadataFile,
532                                         Date lastUpdatedTimestamp, String timestamp, int buildNumber,
533                                         boolean fixChecksums )
534         throws RepositoryMetadataException
535     {
536         if ( !metadataFile.exists() )
537         {
538             metadata.setGroupId( groupId );
539             metadata.setArtifactId( artifactId );
540             metadata.setVersion( version );
541         }
542
543         if ( metadata.getSnapshotVersion() == null )
544         {
545             metadata.setSnapshotVersion( new SnapshotVersion() );
546         }
547
548         metadata.getSnapshotVersion().setBuildNumber( buildNumber );
549         metadata.getSnapshotVersion().setTimestamp( timestamp );
550         metadata.setLastUpdatedTimestamp( lastUpdatedTimestamp );
551
552         RepositoryMetadataWriter.write( metadata, metadataFile );
553
554         if ( fixChecksums )
555         {
556             fixChecksums( metadataFile );
557         }
558     }
559
560     /**
561      * Update artifact level metadata. If it does not exist, create the metadata and fix checksums if necessary.
562      */
563     private void updateProjectMetadata( String targetPath, Date lastUpdatedTimestamp, String timestamp, int buildNumber,
564                                         boolean fixChecksums )
565         throws RepositoryMetadataException
566     {
567         List<String> availableVersions = new ArrayList<String>();
568         String latestVersion = version;
569
570         File projectDir = new File( targetPath ).getParentFile();
571         File projectMetadataFile = new File( projectDir, MetadataTools.MAVEN_METADATA );
572
573         ArchivaRepositoryMetadata projectMetadata = getMetadata( projectMetadataFile );
574
575         if ( projectMetadataFile.exists() )
576         {
577             availableVersions = projectMetadata.getAvailableVersions();
578
579             Collections.sort( availableVersions, VersionComparator.getInstance() );
580
581             if ( !availableVersions.contains( version ) )
582             {
583                 availableVersions.add( version );
584             }
585
586             latestVersion = availableVersions.get( availableVersions.size() - 1 );
587         }
588         else
589         {
590             availableVersions.add( version );
591
592             projectMetadata.setGroupId( groupId );
593             projectMetadata.setArtifactId( artifactId );
594         }
595
596         if ( projectMetadata.getGroupId() == null )
597         {
598             projectMetadata.setGroupId( groupId );
599         }
600
601         if ( projectMetadata.getArtifactId() == null )
602         {
603             projectMetadata.setArtifactId( artifactId );
604         }
605
606         projectMetadata.setLatestVersion( latestVersion );
607         projectMetadata.setLastUpdatedTimestamp( lastUpdatedTimestamp );
608         projectMetadata.setAvailableVersions( availableVersions );
609
610         if ( !VersionUtil.isSnapshot( version ) )
611         {
612             projectMetadata.setReleasedVersion( latestVersion );
613         }
614
615         RepositoryMetadataWriter.write( projectMetadata, projectMetadataFile );
616
617         if ( fixChecksums )
618         {
619             fixChecksums( projectMetadataFile );
620         }
621     }
622
623     public void validate()
624     {
625         try
626         {
627             // is this enough check for the repository permission?
628             if ( !userRepositories.isAuthorizedToUploadArtifacts( getPrincipal(), repositoryId ) )
629             {
630                 addActionError( "User is not authorized to upload in repository " + repositoryId );
631             }
632
633             if ( artifactFile == null || artifactFile.length() == 0 )
634             {
635                 addActionError( "Please add a file to upload." );
636             }
637
638             if ( version == null || !VersionUtil.isVersion( version ) )
639             {
640                 addActionError( "Invalid version." );
641             }
642         }
643         catch ( PrincipalNotFoundException pe )
644         {
645             addActionError( pe.getMessage() );
646         }
647         catch ( ArchivaSecurityException ae )
648         {
649             addActionError( ae.getMessage() );
650         }
651     }
652
653     private List<String> getManagableRepos()
654     {
655         try
656         {
657             return userRepositories.getManagableRepositoryIds( getPrincipal() );
658         }
659         catch ( PrincipalNotFoundException e )
660         {
661             log.warn( e.getMessage(), e );
662         }
663         catch ( AccessDeniedException e )
664         {
665             log.warn( e.getMessage(), e );
666             // TODO: pass this onto the screen.
667         }
668         catch ( ArchivaSecurityException e )
669         {
670             log.warn( e.getMessage(), e );
671         }
672         return Collections.emptyList();
673     }
674
675     private void queueRepositoryTask( String repositoryId, File localFile )
676     {
677         RepositoryTask task = new RepositoryTask();
678         task.setRepositoryId( repositoryId );
679         task.setResourceFile( localFile );
680         task.setUpdateRelatedArtifacts( true );
681         task.setScanAll( false );
682
683         try
684         {
685             scheduler.queueTask( task );
686         }
687         catch ( TaskQueueException e )
688         {
689             log.error( "Unable to queue repository task to execute consumers on resource file ['" + localFile.getName()
690                            + "']." );
691         }
692     }
693
694     public void setScheduler( ArchivaTaskScheduler scheduler )
695     {
696         this.scheduler = scheduler;
697     }
698
699     public void setRepositoryFactory( RepositoryContentFactory repositoryFactory )
700     {
701         this.repositoryFactory = repositoryFactory;
702     }
703
704     public ManagedRepositoryAdmin getManagedRepositoryAdmin()
705     {
706         return managedRepositoryAdmin;
707     }
708
709     public void setManagedRepositoryAdmin( ManagedRepositoryAdmin managedRepositoryAdmin )
710     {
711         this.managedRepositoryAdmin = managedRepositoryAdmin;
712     }
713
714     public ArchivaAdministration getArchivaAdministration()
715     {
716         return archivaAdministration;
717     }
718
719     public void setArchivaAdministration( ArchivaAdministration archivaAdministration )
720     {
721         this.archivaAdministration = archivaAdministration;
722     }
723 }