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