1 package org.apache.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.admin.model.RepositoryAdminException;
25 import org.apache.archiva.admin.model.admin.ArchivaAdministration;
26 import org.apache.archiva.admin.model.beans.ManagedRepository;
27 import org.apache.archiva.admin.model.managed.ManagedRepositoryAdmin;
28 import org.apache.archiva.audit.AuditEvent;
29 import org.apache.archiva.audit.Auditable;
30 import org.apache.archiva.checksum.ChecksumAlgorithm;
31 import org.apache.archiva.checksum.ChecksummedFile;
32 import org.apache.archiva.scheduler.ArchivaTaskScheduler;
33 import org.apache.archiva.scheduler.repository.RepositoryTask;
34 import org.apache.archiva.security.AccessDeniedException;
35 import org.apache.archiva.security.ArchivaSecurityException;
36 import org.apache.archiva.security.PrincipalNotFoundException;
37 import org.apache.archiva.security.UserRepositories;
38 import org.apache.commons.io.FilenameUtils;
39 import org.apache.commons.io.IOUtils;
40 import org.apache.commons.lang.StringUtils;
41 import org.apache.archiva.common.utils.VersionComparator;
42 import org.apache.archiva.common.utils.VersionUtil;
43 import org.apache.archiva.model.ArchivaRepositoryMetadata;
44 import org.apache.archiva.model.ArtifactReference;
45 import org.apache.archiva.model.SnapshotVersion;
46 import org.apache.archiva.repository.ManagedRepositoryContent;
47 import org.apache.archiva.repository.RepositoryContentFactory;
48 import org.apache.archiva.repository.RepositoryException;
49 import org.apache.archiva.repository.RepositoryNotFoundException;
50 import org.apache.archiva.repository.metadata.MetadataTools;
51 import org.apache.archiva.repository.metadata.RepositoryMetadataException;
52 import org.apache.archiva.repository.metadata.RepositoryMetadataReader;
53 import org.apache.archiva.repository.metadata.RepositoryMetadataWriter;
54 import org.apache.maven.model.Model;
55 import org.apache.maven.model.io.xpp3.MavenXpp3Writer;
56 import org.apache.taglibs.standard.tlv.JstlBaseTLV;
57 import org.codehaus.plexus.taskqueue.TaskQueueException;
58 import org.codehaus.plexus.util.IOUtil;
59 import org.springframework.context.annotation.Scope;
60 import org.springframework.stereotype.Controller;
62 import javax.inject.Inject;
63 import javax.inject.Named;
65 import java.io.FileInputStream;
66 import java.io.FileOutputStream;
67 import java.io.FileWriter;
68 import java.io.IOException;
69 import java.text.DateFormat;
70 import java.text.SimpleDateFormat;
71 import java.util.ArrayList;
72 import java.util.Calendar;
73 import java.util.Collections;
74 import java.util.Date;
75 import java.util.List;
76 import java.util.TimeZone;
79 * Upload an artifact using Jakarta file upload in webwork. If set by the user a pom will also be generated. Metadata
80 * will also be updated if one exists, otherwise it would be created.
82 @SuppressWarnings( "serial" )
83 @Controller( "uploadAction" )
85 public class UploadAction
86 extends AbstractActionSupport
87 implements Validateable, Preparable, Auditable
90 * The groupId of the artifact to be deployed.
92 private String groupId;
95 * The artifactId of the artifact to be deployed.
97 private String artifactId;
100 * The version of the artifact to be deployed.
102 private String version;
105 * The packaging of the artifact to be deployed.
107 private String packaging;
110 * The classifier of the artifact to be deployed.
112 private String classifier;
115 * The temporary file representing the artifact to be deployed.
117 private File artifactFile;
120 * The temporary file representing the pom to be deployed alongside the artifact.
122 private File pomFile;
125 * The repository where the artifact is to be deployed.
127 private String repositoryId;
130 * Flag whether to generate a pom for the artifact or not.
132 private boolean generatePom;
135 * List of managed repositories to deploy to.
137 private List<String> managedRepoIdList;
140 private ManagedRepositoryAdmin managedRepositoryAdmin;
143 private UserRepositories userRepositories;
146 private ArchivaAdministration archivaAdministration;
149 private RepositoryContentFactory repositoryFactory;
152 @Named( value = "archivaTaskScheduler#repository" )
153 private ArchivaTaskScheduler scheduler;
155 private ChecksumAlgorithm[] algorithms = new ChecksumAlgorithm[]{ ChecksumAlgorithm.SHA1, ChecksumAlgorithm.MD5 };
157 public void setArtifact( File file )
159 this.artifactFile = file;
162 public void setArtifactContentType( String contentType )
164 StringUtils.trim( contentType );
167 public void setArtifactFileName( String filename )
169 StringUtils.trim( filename );
172 public void setPom( File file )
177 public void setPomContentType( String contentType )
179 StringUtils.trim( contentType );
182 public void setPomFileName( String filename )
184 StringUtils.trim( filename );
187 public String getGroupId()
192 public void setGroupId( String groupId )
194 this.groupId = StringUtils.trim( groupId );
197 public String getArtifactId()
202 public void setArtifactId( String artifactId )
204 this.artifactId = StringUtils.trim( artifactId );
207 public String getVersion()
212 public void setVersion( String version )
214 this.version = StringUtils.trim( version );
217 public String getPackaging()
222 public void setPackaging( String packaging )
224 this.packaging = StringUtils.trim( packaging );
227 public String getClassifier()
232 public void setClassifier( String classifier )
234 this.classifier = StringUtils.trim( classifier );
237 public String getRepositoryId()
242 public void setRepositoryId( String repositoryId )
244 this.repositoryId = repositoryId;
247 public boolean isGeneratePom()
252 public void setGeneratePom( boolean generatePom )
254 this.generatePom = generatePom;
257 public List<String> getManagedRepoIdList()
259 return managedRepoIdList;
262 public void setManagedRepoIdList( List<String> managedRepoIdList )
264 this.managedRepoIdList = managedRepoIdList;
267 public void prepare()
269 managedRepoIdList = getManagableRepos();
272 public String input()
279 // reset the fields so the form is clear when
280 // the action returns to the jsp page
292 public String doUpload()
296 ManagedRepository repoConfig = managedRepositoryAdmin.getManagedRepository( repositoryId );
298 ArtifactReference artifactReference = new ArtifactReference();
299 artifactReference.setArtifactId( artifactId );
300 artifactReference.setGroupId( groupId );
301 artifactReference.setVersion( version );
302 artifactReference.setClassifier( classifier );
303 artifactReference.setType( packaging );
305 ManagedRepositoryContent repository = repositoryFactory.getManagedRepositoryContent( repositoryId );
307 String artifactPath = repository.toPath( artifactReference );
309 int lastIndex = artifactPath.lastIndexOf( '/' );
311 String path = artifactPath.substring( 0, lastIndex );
312 File targetPath = new File( repoConfig.getLocation(), path );
314 Date lastUpdatedTimestamp = Calendar.getInstance().getTime();
315 int newBuildNumber = -1;
316 String timestamp = null;
318 File versionMetadataFile = new File( targetPath, MetadataTools.MAVEN_METADATA );
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 );
327 if ( versionMetadata.getSnapshotVersion() != null )
329 newBuildNumber = versionMetadata.getSnapshotVersion().getBuildNumber() + 1;
337 if ( !targetPath.exists() )
342 String filename = artifactPath.substring( lastIndex + 1 );
343 if ( VersionUtil.isSnapshot( version ) )
345 filename = filename.replaceAll( "SNAPSHOT", timestamp + "-" + newBuildNumber );
348 boolean fixChecksums =
349 !( archivaAdministration.getKnownContentConsumers().contains( "create-missing-checksums" ) );
353 File targetFile = new File( targetPath, filename );
354 if ( targetFile.exists() && !VersionUtil.isSnapshot( version ) && repoConfig.isBlockRedeployments() )
357 "Overwriting released artifacts in repository '" + repoConfig.getId() + "' is not allowed." );
362 copyFile( artifactFile, targetPath, filename, fixChecksums );
363 triggerAuditEvent( repository.getId(), path + "/" + filename, AuditEvent.UPLOAD_FILE );
364 queueRepositoryTask( repository.getId(), targetFile );
367 catch ( IOException ie )
369 addActionError( "Error encountered while uploading file: " + ie.getMessage() );
373 String pomFilename = filename;
374 if ( classifier != null && !"".equals( classifier ) )
376 pomFilename = StringUtils.remove( pomFilename, "-" + classifier );
378 pomFilename = FilenameUtils.removeExtension( pomFilename ) + ".pom";
384 File generatedPomFile = createPom( targetPath, pomFilename );
385 triggerAuditEvent( repoConfig.getId(), path + "/" + pomFilename, AuditEvent.UPLOAD_FILE );
388 fixChecksums( generatedPomFile );
390 queueRepositoryTask( repoConfig.getId(), generatedPomFile );
392 catch ( IOException ie )
394 addActionError( "Error encountered while writing pom file: " + ie.getMessage() );
399 if ( pomFile != null && pomFile.length() > 0 )
403 copyFile( pomFile, targetPath, pomFilename, fixChecksums );
404 triggerAuditEvent( repoConfig.getId(), path + "/" + pomFilename, AuditEvent.UPLOAD_FILE );
405 queueRepositoryTask( repoConfig.getId(), new File( targetPath, pomFilename ) );
407 catch ( IOException ie )
409 addActionError( "Error encountered while uploading pom file: " + ie.getMessage() );
415 // explicitly update only if metadata-updater consumer is not enabled!
416 if ( !archivaAdministration.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 addActionMessage( msg );
436 catch ( RepositoryNotFoundException re )
438 addActionError( "Target repository cannot be found: " + re.getMessage() );
441 catch ( RepositoryException rep )
443 addActionError( "Repository exception: " + rep.getMessage() );
446 catch ( RepositoryAdminException e )
448 addActionError( "RepositoryAdmin exception: " + e.getMessage() );
453 private void fixChecksums( File file )
455 ChecksummedFile checksum = new ChecksummedFile( file );
456 checksum.fixChecksums( algorithms );
459 private void copyFile( File sourceFile, File targetPath, String targetFilename, boolean fixChecksums )
462 FileOutputStream out = new FileOutputStream( new File( targetPath, targetFilename ) );
463 FileInputStream input = new FileInputStream( sourceFile );
467 IOUtils.copy( input, out );
477 fixChecksums( new File( targetPath, targetFilename ) );
481 private File createPom( File targetPath, String filename )
484 Model projectModel = new Model();
485 projectModel.setModelVersion( "4.0.0" );
486 projectModel.setGroupId( groupId );
487 projectModel.setArtifactId( artifactId );
488 projectModel.setVersion( version );
489 projectModel.setPackaging( packaging );
491 File pomFile = new File( targetPath, filename );
492 MavenXpp3Writer writer = new MavenXpp3Writer();
493 FileWriter w = new FileWriter( pomFile );
496 writer.write( w, projectModel );
506 private ArchivaRepositoryMetadata getMetadata( File metadataFile )
507 throws RepositoryMetadataException
509 ArchivaRepositoryMetadata metadata = new ArchivaRepositoryMetadata();
510 if ( metadataFile.exists() )
512 metadata = RepositoryMetadataReader.read( metadataFile );
519 * Update version level metadata for snapshot artifacts. If it does not exist, create the metadata and fix checksums
522 private void updateVersionMetadata( ArchivaRepositoryMetadata metadata, File metadataFile,
523 Date lastUpdatedTimestamp, String timestamp, int buildNumber,
524 boolean fixChecksums )
525 throws RepositoryMetadataException
527 if ( !metadataFile.exists() )
529 metadata.setGroupId( groupId );
530 metadata.setArtifactId( artifactId );
531 metadata.setVersion( version );
534 if ( metadata.getSnapshotVersion() == null )
536 metadata.setSnapshotVersion( new SnapshotVersion() );
539 metadata.getSnapshotVersion().setBuildNumber( buildNumber );
540 metadata.getSnapshotVersion().setTimestamp( timestamp );
541 metadata.setLastUpdatedTimestamp( lastUpdatedTimestamp );
543 RepositoryMetadataWriter.write( metadata, metadataFile );
547 fixChecksums( metadataFile );
552 * Update artifact level metadata. If it does not exist, create the metadata and fix checksums if necessary.
554 private void updateProjectMetadata( String targetPath, Date lastUpdatedTimestamp, String timestamp, int buildNumber,
555 boolean fixChecksums )
556 throws RepositoryMetadataException
558 List<String> availableVersions = new ArrayList<String>();
559 String latestVersion = version;
561 File projectDir = new File( targetPath ).getParentFile();
562 File projectMetadataFile = new File( projectDir, MetadataTools.MAVEN_METADATA );
564 ArchivaRepositoryMetadata projectMetadata = getMetadata( projectMetadataFile );
566 if ( projectMetadataFile.exists() )
568 availableVersions = projectMetadata.getAvailableVersions();
570 Collections.sort( availableVersions, VersionComparator.getInstance() );
572 if ( !availableVersions.contains( version ) )
574 availableVersions.add( version );
577 latestVersion = availableVersions.get( availableVersions.size() - 1 );
581 availableVersions.add( version );
583 projectMetadata.setGroupId( groupId );
584 projectMetadata.setArtifactId( artifactId );
587 if ( projectMetadata.getGroupId() == null )
589 projectMetadata.setGroupId( groupId );
592 if ( projectMetadata.getArtifactId() == null )
594 projectMetadata.setArtifactId( artifactId );
597 projectMetadata.setLatestVersion( latestVersion );
598 projectMetadata.setLastUpdatedTimestamp( lastUpdatedTimestamp );
599 projectMetadata.setAvailableVersions( availableVersions );
601 if ( !VersionUtil.isSnapshot( version ) )
603 projectMetadata.setReleasedVersion( latestVersion );
606 RepositoryMetadataWriter.write( projectMetadata, projectMetadataFile );
610 fixChecksums( projectMetadataFile );
614 public void validate()
618 // is this enough check for the repository permission?
619 if ( !userRepositories.isAuthorizedToUploadArtifacts( getPrincipal(), repositoryId ) )
621 addActionError( "User is not authorized to upload in repository " + repositoryId );
624 if ( artifactFile == null || artifactFile.length() == 0 )
626 addActionError( "Please add a file to upload." );
629 if ( version == null || !VersionUtil.isVersion( version ) )
631 addActionError( "Invalid version." );
634 catch ( PrincipalNotFoundException pe )
636 addActionError( pe.getMessage() );
638 catch ( ArchivaSecurityException ae )
640 addActionError( ae.getMessage() );
644 private List<String> getManagableRepos()
648 return userRepositories.getManagableRepositoryIds( getPrincipal() );
650 catch ( PrincipalNotFoundException e )
652 log.warn( e.getMessage(), e );
654 catch ( AccessDeniedException e )
656 log.warn( e.getMessage(), e );
657 // TODO: pass this onto the screen.
659 catch ( ArchivaSecurityException e )
661 log.warn( e.getMessage(), e );
663 return Collections.emptyList();
666 private void queueRepositoryTask( String repositoryId, File localFile )
668 RepositoryTask task = new RepositoryTask();
669 task.setRepositoryId( repositoryId );
670 task.setResourceFile( localFile );
671 task.setUpdateRelatedArtifacts( true );
672 task.setScanAll( false );
676 scheduler.queueTask( task );
678 catch ( TaskQueueException e )
680 log.error( "Unable to queue repository task to execute consumers on resource file ['" + localFile.getName()
685 public void setScheduler( ArchivaTaskScheduler scheduler )
687 this.scheduler = scheduler;
690 public void setRepositoryFactory( RepositoryContentFactory repositoryFactory )
692 this.repositoryFactory = repositoryFactory;
695 public ManagedRepositoryAdmin getManagedRepositoryAdmin()
697 return managedRepositoryAdmin;
700 public void setManagedRepositoryAdmin( ManagedRepositoryAdmin managedRepositoryAdmin )
702 this.managedRepositoryAdmin = managedRepositoryAdmin;
705 public ArchivaAdministration getArchivaAdministration()
707 return archivaAdministration;
710 public void setArchivaAdministration( ArchivaAdministration archivaAdministration )
712 this.archivaAdministration = archivaAdministration;