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