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