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