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