]> source.dussan.org Git - archiva.git/blob
60cfc49c8a801551274a0cca3ac673f206b9dfc3
[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.scheduler.ArchivaTaskScheduler;
33 import org.apache.archiva.scheduler.repository.RepositoryTask;
34 import org.apache.archiva.security.AccessDeniedException;
35 import org.apache.archiva.security.ArchivaSecurityException;
36 import org.apache.archiva.security.PrincipalNotFoundException;
37 import org.apache.archiva.security.UserRepositories;
38 import org.apache.commons.io.FilenameUtils;
39 import org.apache.commons.io.IOUtils;
40 import org.apache.commons.lang.StringUtils;
41 import org.apache.archiva.common.utils.VersionComparator;
42 import org.apache.archiva.common.utils.VersionUtil;
43 import org.apache.archiva.model.ArchivaRepositoryMetadata;
44 import org.apache.archiva.model.ArtifactReference;
45 import org.apache.archiva.model.SnapshotVersion;
46 import org.apache.archiva.repository.ManagedRepositoryContent;
47 import org.apache.archiva.repository.RepositoryContentFactory;
48 import org.apache.archiva.repository.RepositoryException;
49 import org.apache.archiva.repository.RepositoryNotFoundException;
50 import org.apache.archiva.repository.metadata.MetadataTools;
51 import org.apache.archiva.repository.metadata.RepositoryMetadataException;
52 import org.apache.archiva.repository.metadata.RepositoryMetadataReader;
53 import org.apache.archiva.repository.metadata.RepositoryMetadataWriter;
54 import org.apache.maven.model.Model;
55 import org.apache.maven.model.io.xpp3.MavenXpp3Writer;
56 import org.apache.taglibs.standard.tlv.JstlBaseTLV;
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             Date lastUpdatedTimestamp = Calendar.getInstance().getTime();
315             int newBuildNumber = -1;
316             String timestamp = null;
317
318             File versionMetadataFile = new File( targetPath, MetadataTools.MAVEN_METADATA );
319             ArchivaRepositoryMetadata versionMetadata = getMetadata( versionMetadataFile );
320
321             if ( VersionUtil.isSnapshot( version ) )
322             {
323                 TimeZone timezone = TimeZone.getTimeZone( "UTC" );
324                 DateFormat fmt = new SimpleDateFormat( "yyyyMMdd.HHmmss" );
325                 fmt.setTimeZone( timezone );
326                 timestamp = fmt.format( lastUpdatedTimestamp );
327                 if ( versionMetadata.getSnapshotVersion() != null )
328                 {
329                     newBuildNumber = versionMetadata.getSnapshotVersion().getBuildNumber() + 1;
330                 }
331                 else
332                 {
333                     newBuildNumber = 1;
334                 }
335             }
336
337             if ( !targetPath.exists() )
338             {
339                 targetPath.mkdirs();
340             }
341
342             String filename = artifactPath.substring( lastIndex + 1 );
343             if ( VersionUtil.isSnapshot( version ) )
344             {
345                 filename = filename.replaceAll( "SNAPSHOT", timestamp + "-" + newBuildNumber );
346             }
347
348             boolean fixChecksums =
349                 !( archivaAdministration.getKnownContentConsumers().contains( "create-missing-checksums" ) );
350
351             try
352             {
353                 File targetFile = new File( targetPath, filename );
354                 if ( targetFile.exists() && !VersionUtil.isSnapshot( version ) && repoConfig.isBlockRedeployments() )
355                 {
356                     addActionError(
357                         "Overwriting released artifacts in repository '" + repoConfig.getId() + "' is not allowed." );
358                     return ERROR;
359                 }
360                 else
361                 {
362                     copyFile( artifactFile, targetPath, filename, fixChecksums );
363                     triggerAuditEvent( repository.getId(), path + "/" + filename, AuditEvent.UPLOAD_FILE );
364                     queueRepositoryTask( repository.getId(), targetFile );
365                 }
366             }
367             catch ( IOException ie )
368             {
369                 addActionError( "Error encountered while uploading file: " + ie.getMessage() );
370                 return ERROR;
371             }
372
373             String pomFilename = filename;
374             if ( classifier != null && !"".equals( classifier ) )
375             {
376                 pomFilename = StringUtils.remove( pomFilename, "-" + classifier );
377             }
378             pomFilename = FilenameUtils.removeExtension( pomFilename ) + ".pom";
379
380             if ( generatePom )
381             {
382                 try
383                 {
384                     File generatedPomFile = createPom( targetPath, pomFilename );
385                     triggerAuditEvent( repoConfig.getId(), path + "/" + pomFilename, AuditEvent.UPLOAD_FILE );
386                     if ( fixChecksums )
387                     {
388                         fixChecksums( generatedPomFile );
389                     }
390                     queueRepositoryTask( repoConfig.getId(), generatedPomFile );
391                 }
392                 catch ( IOException ie )
393                 {
394                     addActionError( "Error encountered while writing pom file: " + ie.getMessage() );
395                     return ERROR;
396                 }
397             }
398
399             if ( pomFile != null && pomFile.length() > 0 )
400             {
401                 try
402                 {
403                     copyFile( pomFile, targetPath, pomFilename, fixChecksums );
404                     triggerAuditEvent( repoConfig.getId(), path + "/" + pomFilename, AuditEvent.UPLOAD_FILE );
405                     queueRepositoryTask( repoConfig.getId(), new File( targetPath, pomFilename ) );
406                 }
407                 catch ( IOException ie )
408                 {
409                     addActionError( "Error encountered while uploading pom file: " + ie.getMessage() );
410                     return ERROR;
411                 }
412
413             }
414
415             // explicitly update only if metadata-updater consumer is not enabled!
416             if ( !archivaAdministration.getKnownContentConsumers().contains( "metadata-updater" ) )
417             {
418                 updateProjectMetadata( targetPath.getAbsolutePath(), lastUpdatedTimestamp, timestamp, newBuildNumber,
419                                        fixChecksums );
420
421                 if ( VersionUtil.isSnapshot( version ) )
422                 {
423                     updateVersionMetadata( versionMetadata, versionMetadataFile, lastUpdatedTimestamp, timestamp,
424                                            newBuildNumber, fixChecksums );
425                 }
426             }
427
428             String msg = "Artifact \'" + groupId + ":" + artifactId + ":" + version
429                 + "\' was successfully deployed to repository \'" + repositoryId + "\'";
430
431             addActionMessage( msg );
432
433             reset();
434             return SUCCESS;
435         }
436         catch ( RepositoryNotFoundException re )
437         {
438             addActionError( "Target repository cannot be found: " + re.getMessage() );
439             return ERROR;
440         }
441         catch ( RepositoryException rep )
442         {
443             addActionError( "Repository exception: " + rep.getMessage() );
444             return ERROR;
445         }
446         catch ( RepositoryAdminException e )
447         {
448             addActionError( "RepositoryAdmin exception: " + e.getMessage() );
449             return ERROR;
450         }
451     }
452
453     private void fixChecksums( File file )
454     {
455         ChecksummedFile checksum = new ChecksummedFile( file );
456         checksum.fixChecksums( algorithms );
457     }
458
459     private void copyFile( File sourceFile, File targetPath, String targetFilename, boolean fixChecksums )
460         throws IOException
461     {
462         FileOutputStream out = new FileOutputStream( new File( targetPath, targetFilename ) );
463         FileInputStream input = new FileInputStream( sourceFile );
464
465         try
466         {
467             IOUtils.copy( input, out );
468         }
469         finally
470         {
471             out.close();
472             input.close();
473         }
474
475         if ( fixChecksums )
476         {
477             fixChecksums( new File( targetPath, targetFilename ) );
478         }
479     }
480
481     private File createPom( File targetPath, String filename )
482         throws IOException
483     {
484         Model projectModel = new Model();
485         projectModel.setModelVersion( "4.0.0" );
486         projectModel.setGroupId( groupId );
487         projectModel.setArtifactId( artifactId );
488         projectModel.setVersion( version );
489         projectModel.setPackaging( packaging );
490
491         File pomFile = new File( targetPath, filename );
492         MavenXpp3Writer writer = new MavenXpp3Writer();
493         FileWriter w = new FileWriter( pomFile );
494         try
495         {
496             writer.write( w, projectModel );
497         }
498         finally
499         {
500             IOUtil.close( w );
501         }
502
503         return pomFile;
504     }
505
506     private ArchivaRepositoryMetadata getMetadata( File metadataFile )
507         throws RepositoryMetadataException
508     {
509         ArchivaRepositoryMetadata metadata = new ArchivaRepositoryMetadata();
510         if ( metadataFile.exists() )
511         {
512             metadata = RepositoryMetadataReader.read( metadataFile );
513         }
514         return metadata;
515     }
516
517
518     /**
519      * Update version level metadata for snapshot artifacts. If it does not exist, create the metadata and fix checksums
520      * if necessary.
521      */
522     private void updateVersionMetadata( ArchivaRepositoryMetadata metadata, File metadataFile,
523                                         Date lastUpdatedTimestamp, String timestamp, int buildNumber,
524                                         boolean fixChecksums )
525         throws RepositoryMetadataException
526     {
527         if ( !metadataFile.exists() )
528         {
529             metadata.setGroupId( groupId );
530             metadata.setArtifactId( artifactId );
531             metadata.setVersion( version );
532         }
533
534         if ( metadata.getSnapshotVersion() == null )
535         {
536             metadata.setSnapshotVersion( new SnapshotVersion() );
537         }
538
539         metadata.getSnapshotVersion().setBuildNumber( buildNumber );
540         metadata.getSnapshotVersion().setTimestamp( timestamp );
541         metadata.setLastUpdatedTimestamp( lastUpdatedTimestamp );
542
543         RepositoryMetadataWriter.write( metadata, metadataFile );
544
545         if ( fixChecksums )
546         {
547             fixChecksums( metadataFile );
548         }
549     }
550
551     /**
552      * Update artifact level metadata. If it does not exist, create the metadata and fix checksums if necessary.
553      */
554     private void updateProjectMetadata( String targetPath, Date lastUpdatedTimestamp, String timestamp, int buildNumber,
555                                         boolean fixChecksums )
556         throws RepositoryMetadataException
557     {
558         List<String> availableVersions = new ArrayList<String>();
559         String latestVersion = version;
560
561         File projectDir = new File( targetPath ).getParentFile();
562         File projectMetadataFile = new File( projectDir, MetadataTools.MAVEN_METADATA );
563
564         ArchivaRepositoryMetadata projectMetadata = getMetadata( projectMetadataFile );
565
566         if ( projectMetadataFile.exists() )
567         {
568             availableVersions = projectMetadata.getAvailableVersions();
569
570             Collections.sort( availableVersions, VersionComparator.getInstance() );
571
572             if ( !availableVersions.contains( version ) )
573             {
574                 availableVersions.add( version );
575             }
576
577             latestVersion = availableVersions.get( availableVersions.size() - 1 );
578         }
579         else
580         {
581             availableVersions.add( version );
582
583             projectMetadata.setGroupId( groupId );
584             projectMetadata.setArtifactId( artifactId );
585         }
586
587         if ( projectMetadata.getGroupId() == null )
588         {
589             projectMetadata.setGroupId( groupId );
590         }
591
592         if ( projectMetadata.getArtifactId() == null )
593         {
594             projectMetadata.setArtifactId( artifactId );
595         }
596
597         projectMetadata.setLatestVersion( latestVersion );
598         projectMetadata.setLastUpdatedTimestamp( lastUpdatedTimestamp );
599         projectMetadata.setAvailableVersions( availableVersions );
600
601         if ( !VersionUtil.isSnapshot( version ) )
602         {
603             projectMetadata.setReleasedVersion( latestVersion );
604         }
605
606         RepositoryMetadataWriter.write( projectMetadata, projectMetadataFile );
607
608         if ( fixChecksums )
609         {
610             fixChecksums( projectMetadataFile );
611         }
612     }
613
614     public void validate()
615     {
616         try
617         {
618             // is this enough check for the repository permission?
619             if ( !userRepositories.isAuthorizedToUploadArtifacts( getPrincipal(), repositoryId ) )
620             {
621                 addActionError( "User is not authorized to upload in repository " + repositoryId );
622             }
623
624             if ( artifactFile == null || artifactFile.length() == 0 )
625             {
626                 addActionError( "Please add a file to upload." );
627             }
628
629             if ( version == null || !VersionUtil.isVersion( version ) )
630             {
631                 addActionError( "Invalid version." );
632             }
633         }
634         catch ( PrincipalNotFoundException pe )
635         {
636             addActionError( pe.getMessage() );
637         }
638         catch ( ArchivaSecurityException ae )
639         {
640             addActionError( ae.getMessage() );
641         }
642     }
643
644     private List<String> getManagableRepos()
645     {
646         try
647         {
648             return userRepositories.getManagableRepositoryIds( getPrincipal() );
649         }
650         catch ( PrincipalNotFoundException e )
651         {
652             log.warn( e.getMessage(), e );
653         }
654         catch ( AccessDeniedException e )
655         {
656             log.warn( e.getMessage(), e );
657             // TODO: pass this onto the screen.
658         }
659         catch ( ArchivaSecurityException e )
660         {
661             log.warn( e.getMessage(), e );
662         }
663         return Collections.emptyList();
664     }
665
666     private void queueRepositoryTask( String repositoryId, File localFile )
667     {
668         RepositoryTask task = new RepositoryTask();
669         task.setRepositoryId( repositoryId );
670         task.setResourceFile( localFile );
671         task.setUpdateRelatedArtifacts( true );
672         task.setScanAll( false );
673
674         try
675         {
676             scheduler.queueTask( task );
677         }
678         catch ( TaskQueueException e )
679         {
680             log.error( "Unable to queue repository task to execute consumers on resource file ['" + localFile.getName()
681                            + "']." );
682         }
683     }
684
685     public void setScheduler( ArchivaTaskScheduler scheduler )
686     {
687         this.scheduler = scheduler;
688     }
689
690     public void setRepositoryFactory( RepositoryContentFactory repositoryFactory )
691     {
692         this.repositoryFactory = repositoryFactory;
693     }
694
695     public ManagedRepositoryAdmin getManagedRepositoryAdmin()
696     {
697         return managedRepositoryAdmin;
698     }
699
700     public void setManagedRepositoryAdmin( ManagedRepositoryAdmin managedRepositoryAdmin )
701     {
702         this.managedRepositoryAdmin = managedRepositoryAdmin;
703     }
704
705     public ArchivaAdministration getArchivaAdministration()
706     {
707         return archivaAdministration;
708     }
709
710     public void setArchivaAdministration( ArchivaAdministration archivaAdministration )
711     {
712         this.archivaAdministration = archivaAdministration;
713     }
714 }