1 package org.apache.maven.archiva.web.action;
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
12 * http://www.apache.org/licenses/LICENSE-2.0
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
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;
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;
70 import com.opensymphony.xwork2.Preparable;
71 import com.opensymphony.xwork2.Validateable;
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.
77 * @plexus.component role="com.opensymphony.xwork2.Action" role-hint="uploadAction" instantiation-strategy="per-lookup"
79 @SuppressWarnings( "serial" )
80 public class UploadAction
81 extends PlexusActionSupport
82 implements Validateable, Preparable, Auditable
85 * The groupId of the artifact to be deployed.
87 private String groupId;
90 * The artifactId of the artifact to be deployed.
92 private String artifactId;
95 * The version of the artifact to be deployed.
97 private String version;
100 * The packaging of the artifact to be deployed.
102 private String packaging;
105 * The classifier of the artifact to be deployed.
107 private String classifier;
110 * The temporary file representing the artifact to be deployed.
112 private File artifactFile;
115 * The temporary file representing the pom to be deployed alongside the artifact.
117 private File pomFile;
120 * The repository where the artifact is to be deployed.
122 private String repositoryId;
125 * Flag whether to generate a pom for the artifact or not.
127 private boolean generatePom;
130 * List of managed repositories to deploy to.
132 private List<String> managedRepoIdList;
135 * @plexus.requirement
137 private UserRepositories userRepositories;
140 * @plexus.requirement role-hint="default"
142 private ArchivaConfiguration configuration;
145 * @plexus.requirement
147 private RepositoryContentFactory repositoryFactory;
150 * @plexus.requirement
152 private ArchivaTaskScheduler scheduler;
154 private ChecksumAlgorithm[] algorithms = new ChecksumAlgorithm[]{ChecksumAlgorithm.SHA1, ChecksumAlgorithm.MD5};
156 private ProjectModelWriter pomWriter = new ProjectModel400Writer();
158 public void setArtifact( File file )
160 this.artifactFile = file;
163 public void setArtifactContentType( String contentType )
165 StringUtils.trim( contentType );
168 public void setArtifactFileName( String filename )
170 StringUtils.trim( filename );
173 public void setPom( File file )
178 public void setPomContentType( String contentType )
180 StringUtils.trim( contentType );
183 public void setPomFileName( String filename )
185 StringUtils.trim( filename );
188 public String getGroupId()
193 public void setGroupId( String groupId )
195 this.groupId = StringUtils.trim( groupId );
198 public String getArtifactId()
203 public void setArtifactId( String artifactId )
205 this.artifactId = StringUtils.trim( artifactId );
208 public String getVersion()
213 public void setVersion( String version )
215 this.version = StringUtils.trim( version );
218 public String getPackaging()
223 public void setPackaging( String packaging )
225 this.packaging = StringUtils.trim( packaging );
228 public String getClassifier()
233 public void setClassifier( String classifier )
235 this.classifier = StringUtils.trim( classifier );
238 public String getRepositoryId()
243 public void setRepositoryId( String repositoryId )
245 this.repositoryId = repositoryId;
248 public boolean isGeneratePom()
253 public void setGeneratePom( boolean generatePom )
255 this.generatePom = generatePom;
258 public List<String> getManagedRepoIdList()
260 return managedRepoIdList;
263 public void setManagedRepoIdList( List<String> managedRepoIdList )
265 this.managedRepoIdList = managedRepoIdList;
268 public void prepare()
270 managedRepoIdList = getManagableRepos();
273 public String input()
280 // reset the fields so the form is clear when
281 // the action returns to the jsp page
293 public String doUpload()
297 Configuration config = configuration.getConfiguration();
298 ManagedRepositoryConfiguration repoConfig =
299 config.findManagedRepositoryById( repositoryId );
301 ArtifactReference artifactReference = new ArtifactReference();
302 artifactReference.setArtifactId( artifactId );
303 artifactReference.setGroupId( groupId );
304 artifactReference.setVersion( version );
305 artifactReference.setClassifier( classifier );
306 artifactReference.setType( packaging );
308 ManagedRepositoryContent repository = repositoryFactory.getManagedRepositoryContent( repositoryId );
310 String artifactPath = repository.toPath( artifactReference );
311 int lastIndex = artifactPath.lastIndexOf( '/' );
312 File targetPath = new File( repoConfig.getLocation(), artifactPath.substring( 0, lastIndex ) );
314 Date lastUpdatedTimestamp = Calendar.getInstance().getTime();
315 int newBuildNumber = -1;
316 String timestamp = null;
318 File versionMetadataFile = getMetadata( targetPath.getAbsolutePath() );
319 ArchivaRepositoryMetadata versionMetadata = getMetadata( versionMetadataFile );
321 if ( VersionUtil.isSnapshot( version ) )
323 TimeZone timezone = TimeZone.getTimeZone( "UTC" );
324 DateFormat fmt = new SimpleDateFormat( "yyyyMMdd.HHmmss" );
325 fmt.setTimeZone( timezone );
326 timestamp = fmt.format( lastUpdatedTimestamp );
328 if ( versionMetadata.getSnapshotVersion() != null )
330 newBuildNumber = versionMetadata.getSnapshotVersion().getBuildNumber() + 1;
338 if ( !targetPath.exists() )
343 String filename = artifactPath.substring( lastIndex + 1 );
344 if ( VersionUtil.isSnapshot( version ) )
346 filename = filename.replaceAll( "SNAPSHOT", timestamp + "-" + newBuildNumber );
349 boolean fixChecksums = !( config.getRepositoryScanning().getKnownContentConsumers().contains( "create-missing-checksums" ) );
353 File targetFile = new File( targetPath, filename );
354 if( targetFile.exists() && !VersionUtil.isSnapshot( version ) && repoConfig.isBlockRedeployments() )
356 addActionError( "Overwriting released artifacts in repository '" + repoConfig.getId() + "' is not allowed." );
361 copyFile( artifactFile, targetPath, filename, fixChecksums );
362 queueRepositoryTask( repository.getId(), targetFile );
365 catch ( IOException ie )
367 addActionError( "Error encountered while uploading file: " + ie.getMessage() );
371 String pomFilename = filename;
372 if ( classifier != null && !"".equals( classifier ) )
374 pomFilename = StringUtils.remove( pomFilename, "-" + classifier );
376 pomFilename = FilenameUtils.removeExtension( pomFilename ) + ".pom";
382 File generatedPomFile = createPom( targetPath, pomFilename );
385 fixChecksums( generatedPomFile );
387 queueRepositoryTask( repoConfig.getId(), generatedPomFile );
389 catch ( IOException ie )
391 addActionError( "Error encountered while writing pom file: " + ie.getMessage() );
394 catch ( ProjectModelException pe )
396 addActionError( "Error encountered while generating pom file: " + pe.getMessage() );
401 if ( pomFile != null && pomFile.length() > 0 )
405 copyFile( pomFile, targetPath, pomFilename, fixChecksums );
406 queueRepositoryTask( repoConfig.getId(), new File( targetPath, pomFilename ) );
408 catch ( IOException ie )
410 addActionError( "Error encountered while uploading pom file: " + ie.getMessage() );
415 // explicitly update only if metadata-updater consumer is not enabled!
416 if ( !config.getRepositoryScanning().getKnownContentConsumers().contains( "metadata-updater" ) )
418 updateProjectMetadata( targetPath.getAbsolutePath(), lastUpdatedTimestamp, timestamp, newBuildNumber,
421 if ( VersionUtil.isSnapshot( version ) )
423 updateVersionMetadata( versionMetadata, versionMetadataFile, lastUpdatedTimestamp, timestamp,
424 newBuildNumber, fixChecksums );
428 String msg = "Artifact \'" + groupId + ":" + artifactId + ":" + version +
429 "\' was successfully deployed to repository \'" + repositoryId + "\'";
431 triggerAuditEvent( repositoryId, artifactPath, AuditEvent.UPLOAD_FILE );
433 addActionMessage( msg );
438 catch ( RepositoryNotFoundException re )
440 addActionError( "Target repository cannot be found: " + re.getMessage() );
443 catch ( RepositoryException rep )
445 addActionError( "Repository exception: " + rep.getMessage() );
450 private void fixChecksums( File file )
452 ChecksummedFile checksum = new ChecksummedFile( file );
453 checksum.fixChecksums( algorithms );
456 private void copyFile( File sourceFile, File targetPath, String targetFilename, boolean fixChecksums )
459 FileOutputStream out = new FileOutputStream( new File( targetPath, targetFilename ) );
460 FileInputStream input = new FileInputStream( sourceFile );
465 while ( ( i = input.read() ) != -1 )
479 fixChecksums( new File( targetPath, targetFilename ) );
483 private File createPom( File targetPath, String filename )
484 throws IOException, ProjectModelException
486 ArchivaProjectModel projectModel = new ArchivaProjectModel();
487 projectModel.setGroupId( groupId );
488 projectModel.setArtifactId( artifactId );
489 projectModel.setVersion( version );
490 projectModel.setPackaging( packaging );
492 File pomFile = new File( targetPath, filename );
493 pomWriter.write( projectModel, pomFile );
498 private File getMetadata( String targetPath )
500 return new File( targetPath, MetadataTools.MAVEN_METADATA );
503 private ArchivaRepositoryMetadata getMetadata( File metadataFile )
504 throws RepositoryMetadataException
506 ArchivaRepositoryMetadata metadata = new ArchivaRepositoryMetadata();
507 if ( metadataFile.exists() )
509 metadata = RepositoryMetadataReader.read( metadataFile );
515 * Update version level metadata for snapshot artifacts. If it does not exist, create the metadata and fix checksums
518 private void updateVersionMetadata( ArchivaRepositoryMetadata metadata, File metadataFile,
519 Date lastUpdatedTimestamp, String timestamp, int buildNumber,
520 boolean fixChecksums )
521 throws RepositoryMetadataException
523 if ( !metadataFile.exists() )
525 metadata.setGroupId( groupId );
526 metadata.setArtifactId( artifactId );
527 metadata.setVersion( version );
530 if ( metadata.getSnapshotVersion() == null )
532 metadata.setSnapshotVersion( new SnapshotVersion() );
535 metadata.getSnapshotVersion().setBuildNumber( buildNumber );
536 metadata.getSnapshotVersion().setTimestamp( timestamp );
537 metadata.setLastUpdatedTimestamp( lastUpdatedTimestamp );
539 RepositoryMetadataWriter.write( metadata, metadataFile );
543 fixChecksums( metadataFile );
548 * Update artifact level metadata. If it does not exist, create the metadata and fix checksums if necessary.
550 private void updateProjectMetadata( String targetPath, Date lastUpdatedTimestamp, String timestamp,
551 int buildNumber, boolean fixChecksums )
552 throws RepositoryMetadataException
554 List<String> availableVersions = new ArrayList<String>();
555 String latestVersion = version;
557 String projectPath = targetPath.substring( 0, targetPath.lastIndexOf( File.separatorChar ) );
558 File projectMetadataFile = getMetadata( projectPath );
559 ArchivaRepositoryMetadata projectMetadata = getMetadata( projectMetadataFile );
561 if ( projectMetadataFile.exists() )
563 availableVersions = projectMetadata.getAvailableVersions();
565 Collections.sort( availableVersions, VersionComparator.getInstance() );
567 if ( !availableVersions.contains( version ) )
569 availableVersions.add( version );
572 latestVersion = availableVersions.get( availableVersions.size() - 1 );
576 availableVersions.add( version );
578 projectMetadata.setGroupId( groupId );
579 projectMetadata.setArtifactId( artifactId );
582 if ( projectMetadata.getGroupId() == null )
584 projectMetadata.setGroupId( groupId );
587 if ( projectMetadata.getArtifactId() == null )
589 projectMetadata.setArtifactId( artifactId );
592 projectMetadata.setLatestVersion( latestVersion );
593 projectMetadata.setLastUpdatedTimestamp( lastUpdatedTimestamp );
594 projectMetadata.setAvailableVersions( availableVersions );
596 if ( !VersionUtil.isSnapshot( version ) )
598 projectMetadata.setReleasedVersion( latestVersion );
601 RepositoryMetadataWriter.write( projectMetadata, projectMetadataFile );
605 fixChecksums( projectMetadataFile );
609 public void validate()
613 // is this enough check for the repository permission?
614 if ( !userRepositories.isAuthorizedToUploadArtifacts( getPrincipal(), repositoryId ) )
616 addActionError( "User is not authorized to upload in repository " + repositoryId );
619 if ( artifactFile == null || artifactFile.length() == 0 )
621 addActionError( "Please add a file to upload." );
624 if ( version == null || !VersionUtil.isVersion( version ) )
626 addActionError( "Invalid version." );
629 catch ( PrincipalNotFoundException pe )
631 addActionError( pe.getMessage() );
633 catch ( ArchivaSecurityException ae )
635 addActionError( ae.getMessage() );
639 private List<String> getManagableRepos()
643 return userRepositories.getManagableRepositoryIds( getPrincipal() );
645 catch ( PrincipalNotFoundException e )
647 log.warn( e.getMessage(), e );
649 catch ( AccessDeniedException e )
651 log.warn( e.getMessage(), e );
652 // TODO: pass this onto the screen.
654 catch ( ArchivaSecurityException e )
656 log.warn( e.getMessage(), e );
658 return Collections.emptyList();
661 private void queueRepositoryTask( String repositoryId, File localFile )
663 RepositoryTask task = TaskCreator.createRepositoryTask( repositoryId, localFile, true, true );
667 scheduler.queueRepositoryTask( task );
669 catch ( TaskQueueException e )
672 "Unable to queue repository task to execute consumers on resource file ['" + localFile.getName() +
677 public void setScheduler( ArchivaTaskScheduler scheduler )
679 this.scheduler = scheduler;
682 public void setRepositoryFactory( RepositoryContentFactory repositoryFactory )
684 this.repositoryFactory = repositoryFactory;
687 public void setConfiguration( ArchivaConfiguration configuration )
689 this.configuration = configuration;