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