]> source.dussan.org Git - archiva.git/blob
0919ee8a1d37335277cb04f63d214091afa1e91e
[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             File targetPath = new File( repoConfig.getLocation(), artifactPath.substring( 0, lastIndex ) );
309
310             Date lastUpdatedTimestamp = Calendar.getInstance().getTime();
311             int newBuildNumber = -1;
312             String timestamp = null;
313
314             File metadataFile = getMetadata( targetPath.getAbsolutePath() );
315             ArchivaRepositoryMetadata metadata = getMetadata( metadataFile );
316
317             if ( VersionUtil.isSnapshot( version ) )
318             {
319                 TimeZone timezone = TimeZone.getTimeZone( "UTC" );
320                 DateFormat fmt = new SimpleDateFormat( "yyyyMMdd.HHmmss" );
321                 fmt.setTimeZone( timezone );
322                 timestamp = fmt.format( lastUpdatedTimestamp );
323                 if ( metadata.getSnapshotVersion() != null )
324                 {
325                     newBuildNumber = metadata.getSnapshotVersion().getBuildNumber() + 1;
326                 }
327                 else
328                 {
329                     metadata.setSnapshotVersion( new SnapshotVersion() );
330                     newBuildNumber = 1;
331                 }
332             }
333
334             if ( !targetPath.exists() )
335             {
336                 targetPath.mkdirs();
337             }
338
339             String filename = artifactPath.substring( lastIndex + 1 );
340             if ( VersionUtil.isSnapshot( version ) )
341             {
342                 filename = filename.replaceAll( "SNAPSHOT", timestamp + "-" + newBuildNumber );
343             }
344
345             boolean fixChecksums =
346                 !( config.getRepositoryScanning().getKnownContentConsumers().contains( "create-missing-checksums" ) );
347
348             try
349             {
350                 File targetFile = new File( targetPath, filename );
351                 if ( targetFile.exists() && !VersionUtil.isSnapshot( version ) && repoConfig.isBlockRedeployments() )
352                 {
353                     addActionError(
354                         "Overwriting released artifacts in repository '" + repoConfig.getId() + "' is not allowed." );
355                     return ERROR;
356                 }
357                 else
358                 {
359                     copyFile( artifactFile, targetPath, filename, fixChecksums );
360                     queueRepositoryTask( repository.getId(), repository.toFile( artifactReference ) );
361                 }
362             }
363             catch ( IOException ie )
364             {
365                 addActionError( "Error encountered while uploading file: " + ie.getMessage() );
366                 return ERROR;
367             }
368
369             String pomFilename = filename;
370             if ( classifier != null && !"".equals( classifier ) )
371             {
372                 pomFilename = StringUtils.remove( pomFilename, "-" + classifier );
373             }
374             pomFilename = FilenameUtils.removeExtension( pomFilename ) + ".pom";
375
376             if ( generatePom )
377             {
378                 try
379                 {
380                     File generatedPomFile = createPom( targetPath, pomFilename );
381                     if ( fixChecksums )
382                     {
383                         fixChecksums( generatedPomFile );
384                     }
385                     queueRepositoryTask( repoConfig.getId(), generatedPomFile );
386                 }
387                 catch ( IOException ie )
388                 {
389                     addActionError( "Error encountered while writing pom file: " + ie.getMessage() );
390                     return ERROR;
391                 }
392             }
393
394             if ( pomFile != null && pomFile.length() > 0 )
395             {
396                 try
397                 {
398                     copyFile( pomFile, targetPath, pomFilename, fixChecksums );
399                     queueRepositoryTask( repoConfig.getId(), new File( targetPath, pomFilename ) );
400                 }
401                 catch ( IOException ie )
402                 {
403                     addActionError( "Error encountered while uploading pom file: " + ie.getMessage() );
404                     return ERROR;
405                 }
406
407             }
408
409             // explicitly update only if metadata-updater consumer is not enabled!
410             if ( !config.getRepositoryScanning().getKnownContentConsumers().contains( "metadata-updater" ) )
411             {
412                 updateMetadata( metadata, metadataFile, lastUpdatedTimestamp, timestamp, newBuildNumber, fixChecksums );
413             }
414
415             String msg = "Artifact \'" + groupId + ":" + artifactId + ":" + version +
416                 "\' was successfully deployed to repository \'" + repositoryId + "\'";
417
418             triggerAuditEvent( repositoryId, artifactPath, AuditEvent.UPLOAD_FILE );  
419
420             addActionMessage( msg );
421
422             reset();
423             return SUCCESS;
424         }
425         catch ( RepositoryNotFoundException re )
426         {
427             addActionError( "Target repository cannot be found: " + re.getMessage() );
428             return ERROR;
429         }
430         catch ( RepositoryException rep )
431         {
432             addActionError( "Repository exception: " + rep.getMessage() );
433             return ERROR;
434         }
435     }
436
437     private void fixChecksums( File file )
438     {
439         ChecksummedFile checksum = new ChecksummedFile( file );
440         checksum.fixChecksums( algorithms );
441     }
442
443     private void copyFile( File sourceFile, File targetPath, String targetFilename, boolean fixChecksums )
444         throws IOException
445     {
446         FileOutputStream out = new FileOutputStream( new File( targetPath, targetFilename ) );
447         FileInputStream input = new FileInputStream( sourceFile );
448
449         try
450         {
451             int i;
452             while ( ( i = input.read() ) != -1 )
453             {
454                 out.write( i );
455             }
456             out.flush();
457         }
458         finally
459         {
460             out.close();
461             input.close();
462         }
463
464         if ( fixChecksums )
465         {
466             fixChecksums( new File( targetPath, targetFilename ) );
467         }
468     }
469
470     private File createPom( File targetPath, String filename )
471         throws IOException
472     {
473         Model projectModel = new Model();
474         projectModel.setModelVersion( "4.0.0" );
475         projectModel.setGroupId( groupId );
476         projectModel.setArtifactId( artifactId );
477         projectModel.setVersion( version );
478         projectModel.setPackaging( packaging );
479
480         File pomFile = new File( targetPath, filename );
481         MavenXpp3Writer writer = new MavenXpp3Writer();
482         FileWriter w = new FileWriter( pomFile );
483         try
484         {
485             writer.write( w, projectModel );
486         }
487         finally
488         {
489             IOUtil.close( w );
490         }
491
492         return pomFile;
493     }
494
495     private File getMetadata( String targetPath )
496     {
497         String artifactPath = targetPath.substring( 0, targetPath.lastIndexOf( File.separatorChar ) );
498
499         return new File( artifactPath, MetadataTools.MAVEN_METADATA );
500     }
501
502     private ArchivaRepositoryMetadata getMetadata( File metadataFile )
503         throws RepositoryMetadataException
504     {
505         ArchivaRepositoryMetadata metadata = new ArchivaRepositoryMetadata();
506         if ( metadataFile.exists() )
507         {
508             metadata = RepositoryMetadataReader.read( metadataFile );
509         }
510         return metadata;
511     }
512
513     /**
514      * Update artifact level metadata. If it does not exist, create the metadata and
515      * fix checksums if necessary.
516      */
517     private void updateMetadata( ArchivaRepositoryMetadata metadata, File metadataFile, Date lastUpdatedTimestamp,
518                                  String timestamp, int buildNumber, boolean fixChecksums )
519         throws RepositoryMetadataException
520     {
521         List<String> availableVersions = new ArrayList<String>();
522         String latestVersion = version;
523
524         if ( metadataFile.exists() )
525         {
526             availableVersions = metadata.getAvailableVersions();
527
528             Collections.sort( availableVersions, VersionComparator.getInstance() );
529
530             if ( !availableVersions.contains( version ) )
531             {
532                 availableVersions.add( version );
533             }
534
535             latestVersion = availableVersions.get( availableVersions.size() - 1 );
536         }
537         else
538         {
539             availableVersions.add( version );
540
541             metadata.setGroupId( groupId );
542             metadata.setArtifactId( artifactId );
543         }
544
545         if ( metadata.getGroupId() == null )
546         {
547             metadata.setGroupId( groupId );
548         }
549         if ( metadata.getArtifactId() == null )
550         {
551             metadata.setArtifactId( artifactId );
552         }
553
554         metadata.setLatestVersion( latestVersion );
555         metadata.setLastUpdatedTimestamp( lastUpdatedTimestamp );
556         metadata.setAvailableVersions( availableVersions );
557
558         if ( !VersionUtil.isSnapshot( version ) )
559         {
560             metadata.setReleasedVersion( latestVersion );
561         }
562         else
563         {
564             metadata.getSnapshotVersion().setBuildNumber( buildNumber );
565
566             metadata.getSnapshotVersion().setTimestamp( timestamp );
567         }
568
569         RepositoryMetadataWriter.write( metadata, metadataFile );
570
571         if ( fixChecksums )
572         {
573             fixChecksums( metadataFile );
574         }
575     }
576
577     public void validate()
578     {
579         try
580         {
581             // is this enough check for the repository permission?
582             if ( !userRepositories.isAuthorizedToUploadArtifacts( getPrincipal(), repositoryId ) )
583             {
584                 addActionError( "User is not authorized to upload in repository " + repositoryId );
585             }
586
587             if ( artifactFile == null || artifactFile.length() == 0 )
588             {
589                 addActionError( "Please add a file to upload." );
590             }
591
592             if ( version == null || !VersionUtil.isVersion( version ) )
593             {
594                 addActionError( "Invalid version." );
595             }
596         }
597         catch ( PrincipalNotFoundException pe )
598         {
599             addActionError( pe.getMessage() );
600         }
601         catch ( ArchivaSecurityException ae )
602         {
603             addActionError( ae.getMessage() );
604         }
605     }
606
607     private List<String> getManagableRepos()
608     {
609         try
610         {
611             return userRepositories.getManagableRepositoryIds( getPrincipal() );
612         }
613         catch ( PrincipalNotFoundException e )
614         {
615             log.warn( e.getMessage(), e );
616         }
617         catch ( AccessDeniedException e )
618         {
619             log.warn( e.getMessage(), e );
620             // TODO: pass this onto the screen.
621         }
622         catch ( ArchivaSecurityException e )
623         {
624             log.warn( e.getMessage(), e );
625         }
626         return Collections.emptyList();
627     }
628
629     private void queueRepositoryTask( String repositoryId, File localFile )
630     {
631         RepositoryTask task = new RepositoryTask();
632         task.setRepositoryId( repositoryId );
633         task.setResourceFile( localFile );
634         task.setUpdateRelatedArtifacts( true );
635         task.setScanAll( true );
636
637         try
638         {
639             scheduler.queueTask( task );
640         }
641         catch ( TaskQueueException e )
642         {
643             log.error(
644                 "Unable to queue repository task to execute consumers on resource file ['" + localFile.getName() +
645                     "']." );
646         }
647     }
648
649     public void setScheduler( ArchivaTaskScheduler scheduler )
650     {
651         this.scheduler = scheduler;
652     }
653
654     public void setRepositoryFactory( RepositoryContentFactory repositoryFactory )
655     {
656         this.repositoryFactory = repositoryFactory;
657     }
658
659     public void setConfiguration( ArchivaConfiguration configuration )
660     {
661         this.configuration = configuration;
662     }
663 }