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
22 import com.opensymphony.xwork2.Preparable;
23 import com.opensymphony.xwork2.Validateable;
24 import org.apache.archiva.audit.AuditEvent;
25 import org.apache.archiva.audit.Auditable;
26 import org.apache.archiva.checksum.ChecksumAlgorithm;
27 import org.apache.archiva.checksum.ChecksummedFile;
28 import org.apache.archiva.scheduler.ArchivaTaskScheduler;
29 import org.apache.archiva.scheduler.repository.RepositoryTask;
30 import org.apache.commons.io.FilenameUtils;
31 import org.apache.commons.lang.StringUtils;
32 import org.apache.maven.archiva.common.utils.VersionComparator;
33 import org.apache.maven.archiva.common.utils.VersionUtil;
34 import org.apache.maven.archiva.configuration.ArchivaConfiguration;
35 import org.apache.maven.archiva.configuration.Configuration;
36 import org.apache.maven.archiva.configuration.ManagedRepositoryConfiguration;
37 import org.apache.maven.archiva.model.ArchivaRepositoryMetadata;
38 import org.apache.maven.archiva.model.ArtifactReference;
39 import org.apache.maven.archiva.model.SnapshotVersion;
40 import org.apache.maven.archiva.repository.ManagedRepositoryContent;
41 import org.apache.maven.archiva.repository.RepositoryContentFactory;
42 import org.apache.maven.archiva.repository.RepositoryException;
43 import org.apache.maven.archiva.repository.RepositoryNotFoundException;
44 import org.apache.maven.archiva.repository.metadata.MetadataTools;
45 import org.apache.maven.archiva.repository.metadata.RepositoryMetadataException;
46 import org.apache.maven.archiva.repository.metadata.RepositoryMetadataReader;
47 import org.apache.maven.archiva.repository.metadata.RepositoryMetadataWriter;
48 import org.apache.maven.archiva.security.AccessDeniedException;
49 import org.apache.maven.archiva.security.ArchivaSecurityException;
50 import org.apache.maven.archiva.security.PrincipalNotFoundException;
51 import org.apache.maven.archiva.security.UserRepositories;
52 import org.apache.maven.model.Model;
53 import org.apache.maven.model.io.xpp3.MavenXpp3Writer;
54 import org.codehaus.plexus.taskqueue.TaskQueueException;
55 import org.codehaus.plexus.util.IOUtil;
58 import java.io.FileInputStream;
59 import java.io.FileOutputStream;
60 import java.io.FileWriter;
61 import java.io.IOException;
62 import java.text.DateFormat;
63 import java.text.SimpleDateFormat;
64 import java.util.ArrayList;
65 import java.util.Calendar;
66 import java.util.Collections;
67 import java.util.Date;
68 import java.util.List;
69 import java.util.TimeZone;
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.
75 * @plexus.component role="com.opensymphony.xwork2.Action" role-hint="uploadAction" instantiation-strategy="per-lookup"
77 @SuppressWarnings( "serial" )
78 public class UploadAction
79 extends PlexusActionSupport
80 implements Validateable, Preparable, Auditable
83 * The groupId of the artifact to be deployed.
85 private String groupId;
88 * The artifactId of the artifact to be deployed.
90 private String artifactId;
93 * The version of the artifact to be deployed.
95 private String version;
98 * The packaging of the artifact to be deployed.
100 private String packaging;
103 * The classifier of the artifact to be deployed.
105 private String classifier;
108 * The temporary file representing the artifact to be deployed.
110 private File artifactFile;
113 * The temporary file representing the pom to be deployed alongside the artifact.
115 private File pomFile;
118 * The repository where the artifact is to be deployed.
120 private String repositoryId;
123 * Flag whether to generate a pom for the artifact or not.
125 private boolean generatePom;
128 * List of managed repositories to deploy to.
130 private List<String> managedRepoIdList;
133 * @plexus.requirement
135 private UserRepositories userRepositories;
138 * @plexus.requirement role-hint="default"
140 private ArchivaConfiguration configuration;
143 * @plexus.requirement
145 private RepositoryContentFactory repositoryFactory;
148 * @plexus.requirement role="org.apache.archiva.scheduler.ArchivaTaskScheduler" role-hint="repository"
150 private ArchivaTaskScheduler scheduler;
152 private ChecksumAlgorithm[] algorithms = new ChecksumAlgorithm[]{ChecksumAlgorithm.SHA1, ChecksumAlgorithm.MD5};
154 public void setArtifact( File file )
156 this.artifactFile = file;
159 public void setArtifactContentType( String contentType )
161 StringUtils.trim( contentType );
164 public void setArtifactFileName( String filename )
166 StringUtils.trim( filename );
169 public void setPom( File file )
174 public void setPomContentType( String contentType )
176 StringUtils.trim( contentType );
179 public void setPomFileName( String filename )
181 StringUtils.trim( filename );
184 public String getGroupId()
189 public void setGroupId( String groupId )
191 this.groupId = StringUtils.trim( groupId );
194 public String getArtifactId()
199 public void setArtifactId( String artifactId )
201 this.artifactId = StringUtils.trim( artifactId );
204 public String getVersion()
209 public void setVersion( String version )
211 this.version = StringUtils.trim( version );
214 public String getPackaging()
219 public void setPackaging( String packaging )
221 this.packaging = StringUtils.trim( packaging );
224 public String getClassifier()
229 public void setClassifier( String classifier )
231 this.classifier = StringUtils.trim( classifier );
234 public String getRepositoryId()
239 public void setRepositoryId( String repositoryId )
241 this.repositoryId = repositoryId;
244 public boolean isGeneratePom()
249 public void setGeneratePom( boolean generatePom )
251 this.generatePom = generatePom;
254 public List<String> getManagedRepoIdList()
256 return managedRepoIdList;
259 public void setManagedRepoIdList( List<String> managedRepoIdList )
261 this.managedRepoIdList = managedRepoIdList;
264 public void prepare()
266 managedRepoIdList = getManagableRepos();
269 public String input()
276 // reset the fields so the form is clear when
277 // the action returns to the jsp page
289 public String doUpload()
293 Configuration config = configuration.getConfiguration();
294 ManagedRepositoryConfiguration repoConfig = config.findManagedRepositoryById( repositoryId );
296 ArtifactReference artifactReference = new ArtifactReference();
297 artifactReference.setArtifactId( artifactId );
298 artifactReference.setGroupId( groupId );
299 artifactReference.setVersion( version );
300 artifactReference.setClassifier( classifier );
301 artifactReference.setType( packaging );
303 ManagedRepositoryContent repository = repositoryFactory.getManagedRepositoryContent( repositoryId );
305 String artifactPath = repository.toPath( artifactReference );
307 int lastIndex = artifactPath.lastIndexOf( '/' );
309 String path = artifactPath.substring( 0, lastIndex );
310 File targetPath = new File( repoConfig.getLocation(), path );
312 Date lastUpdatedTimestamp = Calendar.getInstance().getTime();
313 int newBuildNumber = -1;
314 String timestamp = null;
316 File versionMetadataFile = getMetadata( targetPath.getAbsolutePath() );
317 ArchivaRepositoryMetadata versionMetadata = getMetadata( versionMetadataFile );
319 if ( VersionUtil.isSnapshot( version ) )
321 TimeZone timezone = TimeZone.getTimeZone( "UTC" );
322 DateFormat fmt = new SimpleDateFormat( "yyyyMMdd.HHmmss" );
323 fmt.setTimeZone( timezone );
324 timestamp = fmt.format( lastUpdatedTimestamp );
325 if ( versionMetadata.getSnapshotVersion() != null )
327 newBuildNumber = versionMetadata.getSnapshotVersion().getBuildNumber() + 1;
335 if ( !targetPath.exists() )
340 String filename = artifactPath.substring( lastIndex + 1 );
341 if ( VersionUtil.isSnapshot( version ) )
343 filename = filename.replaceAll( "SNAPSHOT", timestamp + "-" + newBuildNumber );
346 boolean fixChecksums =
347 !( config.getRepositoryScanning().getKnownContentConsumers().contains( "create-missing-checksums" ) );
351 File targetFile = new File( targetPath, filename );
352 if ( targetFile.exists() && !VersionUtil.isSnapshot( version ) && repoConfig.isBlockRedeployments() )
355 "Overwriting released artifacts in repository '" + repoConfig.getId() + "' is not allowed." );
360 copyFile( artifactFile, targetPath, filename, fixChecksums );
361 triggerAuditEvent( repository.getId(), path + "/" + filename, AuditEvent.UPLOAD_FILE );
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 );
383 triggerAuditEvent( repoConfig.getId(), path + "/" + pomFilename, AuditEvent.UPLOAD_FILE );
386 fixChecksums( generatedPomFile );
388 queueRepositoryTask( repoConfig.getId(), generatedPomFile );
390 catch ( IOException ie )
392 addActionError( "Error encountered while writing pom file: " + ie.getMessage() );
397 if ( pomFile != null && pomFile.length() > 0 )
401 copyFile( pomFile, targetPath, pomFilename, fixChecksums );
402 triggerAuditEvent( repoConfig.getId(), path + "/" + pomFilename, AuditEvent.UPLOAD_FILE );
403 queueRepositoryTask( repoConfig.getId(), new File( targetPath, pomFilename ) );
405 catch ( IOException ie )
407 addActionError( "Error encountered while uploading pom file: " + ie.getMessage() );
413 // explicitly update only if metadata-updater consumer is not enabled!
414 if ( !config.getRepositoryScanning().getKnownContentConsumers().contains( "metadata-updater" ) )
416 updateProjectMetadata( targetPath.getAbsolutePath(), lastUpdatedTimestamp, timestamp, newBuildNumber,
419 if ( VersionUtil.isSnapshot( version ) )
421 updateVersionMetadata( versionMetadata, versionMetadataFile, lastUpdatedTimestamp, timestamp,
422 newBuildNumber, fixChecksums );
426 String msg = "Artifact \'" + groupId + ":" + artifactId + ":" + version +
427 "\' was successfully deployed to repository \'" + repositoryId + "\'";
429 addActionMessage( msg );
434 catch ( RepositoryNotFoundException re )
436 addActionError( "Target repository cannot be found: " + re.getMessage() );
439 catch ( RepositoryException rep )
441 addActionError( "Repository exception: " + rep.getMessage() );
446 private void fixChecksums( File file )
448 ChecksummedFile checksum = new ChecksummedFile( file );
449 checksum.fixChecksums( algorithms );
452 private void copyFile( File sourceFile, File targetPath, String targetFilename, boolean fixChecksums )
455 FileOutputStream out = new FileOutputStream( new File( targetPath, targetFilename ) );
456 FileInputStream input = new FileInputStream( sourceFile );
461 while ( ( i = input.read() ) != -1 )
475 fixChecksums( new File( targetPath, targetFilename ) );
479 private File createPom( File targetPath, String filename )
482 Model projectModel = new Model();
483 projectModel.setModelVersion( "4.0.0" );
484 projectModel.setGroupId( groupId );
485 projectModel.setArtifactId( artifactId );
486 projectModel.setVersion( version );
487 projectModel.setPackaging( packaging );
489 File pomFile = new File( targetPath, filename );
490 MavenXpp3Writer writer = new MavenXpp3Writer();
491 FileWriter w = new FileWriter( pomFile );
494 writer.write( w, projectModel );
504 private File getMetadata( String targetPath )
506 return new File( targetPath, MetadataTools.MAVEN_METADATA );
509 private ArchivaRepositoryMetadata getMetadata( File metadataFile )
510 throws RepositoryMetadataException
512 ArchivaRepositoryMetadata metadata = new ArchivaRepositoryMetadata();
513 if ( metadataFile.exists() )
515 metadata = RepositoryMetadataReader.read( metadataFile );
522 * Update version level metadata for snapshot artifacts. If it does not exist, create the metadata and fix checksums
525 private void updateVersionMetadata( ArchivaRepositoryMetadata metadata, File metadataFile,
526 Date lastUpdatedTimestamp, String timestamp, int buildNumber,
527 boolean fixChecksums )
528 throws RepositoryMetadataException
530 if ( !metadataFile.exists() )
532 metadata.setGroupId( groupId );
533 metadata.setArtifactId( artifactId );
534 metadata.setVersion( version );
537 if ( metadata.getSnapshotVersion() == null )
539 metadata.setSnapshotVersion( new SnapshotVersion() );
542 metadata.getSnapshotVersion().setBuildNumber( buildNumber );
543 metadata.getSnapshotVersion().setTimestamp( timestamp );
544 metadata.setLastUpdatedTimestamp( lastUpdatedTimestamp );
546 RepositoryMetadataWriter.write( metadata, metadataFile );
550 fixChecksums( metadataFile );
555 * Update artifact level metadata. If it does not exist, create the metadata and fix checksums if necessary.
557 private void updateProjectMetadata( String targetPath, Date lastUpdatedTimestamp, String timestamp,
558 int buildNumber, boolean fixChecksums )
559 throws RepositoryMetadataException
561 List<String> availableVersions = new ArrayList<String>();
562 String latestVersion = version;
564 String projectPath = targetPath.substring( 0, targetPath.lastIndexOf( File.separatorChar ) );
565 File projectMetadataFile = getMetadata( projectPath );
566 ArchivaRepositoryMetadata projectMetadata = getMetadata( projectMetadataFile );
568 if ( projectMetadataFile.exists() )
570 availableVersions = projectMetadata.getAvailableVersions();
572 Collections.sort( availableVersions, VersionComparator.getInstance() );
574 if ( !availableVersions.contains( version ) )
576 availableVersions.add( version );
579 latestVersion = availableVersions.get( availableVersions.size() - 1 );
583 availableVersions.add( version );
585 projectMetadata.setGroupId( groupId );
586 projectMetadata.setArtifactId( artifactId );
589 if ( projectMetadata.getGroupId() == null )
591 projectMetadata.setGroupId( groupId );
594 if ( projectMetadata.getArtifactId() == null )
596 projectMetadata.setArtifactId( artifactId );
599 projectMetadata.setLatestVersion( latestVersion );
600 projectMetadata.setLastUpdatedTimestamp( lastUpdatedTimestamp );
601 projectMetadata.setAvailableVersions( availableVersions );
603 if ( !VersionUtil.isSnapshot( version ) )
605 projectMetadata.setReleasedVersion( latestVersion );
608 RepositoryMetadataWriter.write( projectMetadata, projectMetadataFile );
612 fixChecksums( projectMetadataFile );
616 public void validate()
620 // is this enough check for the repository permission?
621 if ( !userRepositories.isAuthorizedToUploadArtifacts( getPrincipal(), repositoryId ) )
623 addActionError( "User is not authorized to upload in repository " + repositoryId );
626 if ( artifactFile == null || artifactFile.length() == 0 )
628 addActionError( "Please add a file to upload." );
631 if ( version == null || !VersionUtil.isVersion( version ) )
633 addActionError( "Invalid version." );
636 catch ( PrincipalNotFoundException pe )
638 addActionError( pe.getMessage() );
640 catch ( ArchivaSecurityException ae )
642 addActionError( ae.getMessage() );
646 private List<String> getManagableRepos()
650 return userRepositories.getManagableRepositoryIds( getPrincipal() );
652 catch ( PrincipalNotFoundException e )
654 log.warn( e.getMessage(), e );
656 catch ( AccessDeniedException e )
658 log.warn( e.getMessage(), e );
659 // TODO: pass this onto the screen.
661 catch ( ArchivaSecurityException e )
663 log.warn( e.getMessage(), e );
665 return Collections.emptyList();
668 private void queueRepositoryTask( String repositoryId, File localFile )
670 RepositoryTask task = new RepositoryTask();
671 task.setRepositoryId( repositoryId );
672 task.setResourceFile( localFile );
673 task.setUpdateRelatedArtifacts( true );
674 task.setScanAll( true );
678 scheduler.queueTask( task );
680 catch ( TaskQueueException e )
683 "Unable to queue repository task to execute consumers on resource file ['" + localFile.getName() +
688 public void setScheduler( ArchivaTaskScheduler scheduler )
690 this.scheduler = scheduler;
693 public void setRepositoryFactory( RepositoryContentFactory repositoryFactory )
695 this.repositoryFactory = repositoryFactory;
698 public void setConfiguration( ArchivaConfiguration configuration )
700 this.configuration = configuration;