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