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