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.common.utils.VersionComparator;
33 import org.apache.archiva.common.utils.VersionUtil;
34 import org.apache.archiva.maven2.metadata.MavenMetadataReader;
35 import org.apache.archiva.model.ArchivaRepositoryMetadata;
36 import org.apache.archiva.model.ArtifactReference;
37 import org.apache.archiva.model.SnapshotVersion;
38 import org.apache.archiva.repository.ManagedRepositoryContent;
39 import org.apache.archiva.repository.RepositoryContentFactory;
40 import org.apache.archiva.repository.RepositoryException;
41 import org.apache.archiva.repository.RepositoryNotFoundException;
42 import org.apache.archiva.repository.metadata.MetadataTools;
43 import org.apache.archiva.repository.metadata.RepositoryMetadataException;
44 import org.apache.archiva.repository.metadata.RepositoryMetadataWriter;
45 import org.apache.archiva.scheduler.ArchivaTaskScheduler;
46 import org.apache.archiva.scheduler.repository.RepositoryTask;
47 import org.apache.archiva.security.AccessDeniedException;
48 import org.apache.archiva.security.ArchivaSecurityException;
49 import org.apache.archiva.security.PrincipalNotFoundException;
50 import org.apache.archiva.security.UserRepositories;
51 import org.apache.archiva.xml.XMLException;
52 import org.apache.commons.io.FilenameUtils;
53 import org.apache.commons.io.IOUtils;
54 import org.apache.commons.lang.StringUtils;
55 import org.apache.maven.model.Model;
56 import org.apache.maven.model.io.xpp3.MavenXpp3Writer;
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 log.debug( "artifactPath: {} found targetPath: {}", artifactPath, targetPath );
316 Date lastUpdatedTimestamp = Calendar.getInstance().getTime();
317 int newBuildNumber = -1;
318 String timestamp = null;
320 File versionMetadataFile = new File( targetPath, MetadataTools.MAVEN_METADATA );
321 ArchivaRepositoryMetadata versionMetadata = getMetadata( versionMetadataFile );
323 if ( VersionUtil.isSnapshot( version ) )
325 TimeZone timezone = TimeZone.getTimeZone( "UTC" );
326 DateFormat fmt = new SimpleDateFormat( "yyyyMMdd.HHmmss" );
327 fmt.setTimeZone( timezone );
328 timestamp = fmt.format( lastUpdatedTimestamp );
329 if ( versionMetadata.getSnapshotVersion() != null )
331 newBuildNumber = versionMetadata.getSnapshotVersion().getBuildNumber() + 1;
339 if ( !targetPath.exists() )
344 String filename = artifactPath.substring( lastIndex + 1 );
345 if ( VersionUtil.isSnapshot( version ) )
347 filename = filename.replaceAll( "SNAPSHOT", timestamp + "-" + newBuildNumber );
350 boolean fixChecksums =
351 !( archivaAdministration.getKnownContentConsumers().contains( "create-missing-checksums" ) );
355 File targetFile = new File( targetPath, filename );
356 if ( targetFile.exists() && !VersionUtil.isSnapshot( version ) && repoConfig.isBlockRedeployments() )
359 "Overwriting released artifacts in repository '" + repoConfig.getId() + "' is not allowed." );
364 copyFile( artifactFile, targetPath, filename, fixChecksums );
365 triggerAuditEvent( repository.getId(), path + "/" + filename, AuditEvent.UPLOAD_FILE );
366 queueRepositoryTask( repository.getId(), targetFile );
369 catch ( IOException ie )
371 addActionError( "Error encountered while uploading file: " + ie.getMessage() );
375 String pomFilename = filename;
376 if ( classifier != null && !"".equals( classifier ) )
378 pomFilename = StringUtils.remove( pomFilename, "-" + classifier );
380 pomFilename = FilenameUtils.removeExtension( pomFilename ) + ".pom";
386 File generatedPomFile = createPom( targetPath, pomFilename );
387 triggerAuditEvent( repoConfig.getId(), path + "/" + pomFilename, AuditEvent.UPLOAD_FILE );
390 fixChecksums( generatedPomFile );
392 queueRepositoryTask( repoConfig.getId(), generatedPomFile );
394 catch ( IOException ie )
396 addActionError( "Error encountered while writing pom file: " + ie.getMessage() );
401 if ( pomFile != null && pomFile.length() > 0 )
405 copyFile( pomFile, targetPath, pomFilename, fixChecksums );
406 triggerAuditEvent( repoConfig.getId(), path + "/" + pomFilename, AuditEvent.UPLOAD_FILE );
407 queueRepositoryTask( repoConfig.getId(), new File( targetPath, pomFilename ) );
409 catch ( IOException ie )
411 addActionError( "Error encountered while uploading pom file: " + ie.getMessage() );
417 // explicitly update only if metadata-updater consumer is not enabled!
418 if ( !archivaAdministration.getKnownContentConsumers().contains( "metadata-updater" ) )
420 updateProjectMetadata( targetPath.getAbsolutePath(), lastUpdatedTimestamp, timestamp, newBuildNumber,
423 if ( VersionUtil.isSnapshot( version ) )
425 updateVersionMetadata( versionMetadata, versionMetadataFile, lastUpdatedTimestamp, timestamp,
426 newBuildNumber, fixChecksums );
430 String msg = "Artifact \'" + groupId + ":" + artifactId + ":" + version
431 + "\' was successfully deployed to repository \'" + repositoryId + "\'";
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() );
448 catch ( RepositoryAdminException e )
450 addActionError( "RepositoryAdmin exception: " + e.getMessage() );
455 private void fixChecksums( File file )
457 ChecksummedFile checksum = new ChecksummedFile( file );
458 checksum.fixChecksums( algorithms );
461 private void copyFile( File sourceFile, File targetPath, String targetFilename, boolean fixChecksums )
464 FileOutputStream out = new FileOutputStream( new File( targetPath, targetFilename ) );
465 FileInputStream input = new FileInputStream( sourceFile );
469 IOUtils.copy( input, out );
479 fixChecksums( new File( targetPath, targetFilename ) );
483 private File createPom( File targetPath, String filename )
486 Model projectModel = new Model();
487 projectModel.setModelVersion( "4.0.0" );
488 projectModel.setGroupId( groupId );
489 projectModel.setArtifactId( artifactId );
490 projectModel.setVersion( version );
491 projectModel.setPackaging( packaging );
493 File pomFile = new File( targetPath, filename );
494 MavenXpp3Writer writer = new MavenXpp3Writer();
495 FileWriter w = new FileWriter( pomFile );
498 writer.write( w, projectModel );
508 private ArchivaRepositoryMetadata getMetadata( File metadataFile )
509 throws RepositoryMetadataException
511 ArchivaRepositoryMetadata metadata = new ArchivaRepositoryMetadata();
512 if ( metadataFile.exists() )
516 metadata = MavenMetadataReader.read( metadataFile );
518 catch ( XMLException e )
520 throw new RepositoryMetadataException( e.getMessage(), e );
528 * Update version level metadata for snapshot artifacts. If it does not exist, create the metadata and fix checksums
531 private void updateVersionMetadata( ArchivaRepositoryMetadata metadata, File metadataFile,
532 Date lastUpdatedTimestamp, String timestamp, int buildNumber,
533 boolean fixChecksums )
534 throws RepositoryMetadataException
536 if ( !metadataFile.exists() )
538 metadata.setGroupId( groupId );
539 metadata.setArtifactId( artifactId );
540 metadata.setVersion( version );
543 if ( metadata.getSnapshotVersion() == null )
545 metadata.setSnapshotVersion( new SnapshotVersion() );
548 metadata.getSnapshotVersion().setBuildNumber( buildNumber );
549 metadata.getSnapshotVersion().setTimestamp( timestamp );
550 metadata.setLastUpdatedTimestamp( lastUpdatedTimestamp );
552 RepositoryMetadataWriter.write( metadata, metadataFile );
556 fixChecksums( metadataFile );
561 * Update artifact level metadata. If it does not exist, create the metadata and fix checksums if necessary.
563 private void updateProjectMetadata( String targetPath, Date lastUpdatedTimestamp, String timestamp, int buildNumber,
564 boolean fixChecksums )
565 throws RepositoryMetadataException
567 List<String> availableVersions = new ArrayList<String>();
568 String latestVersion = version;
570 File projectDir = new File( targetPath ).getParentFile();
571 File projectMetadataFile = new File( projectDir, MetadataTools.MAVEN_METADATA );
573 ArchivaRepositoryMetadata projectMetadata = getMetadata( projectMetadataFile );
575 if ( projectMetadataFile.exists() )
577 availableVersions = projectMetadata.getAvailableVersions();
579 Collections.sort( availableVersions, VersionComparator.getInstance() );
581 if ( !availableVersions.contains( version ) )
583 availableVersions.add( version );
586 latestVersion = availableVersions.get( availableVersions.size() - 1 );
590 availableVersions.add( version );
592 projectMetadata.setGroupId( groupId );
593 projectMetadata.setArtifactId( artifactId );
596 if ( projectMetadata.getGroupId() == null )
598 projectMetadata.setGroupId( groupId );
601 if ( projectMetadata.getArtifactId() == null )
603 projectMetadata.setArtifactId( artifactId );
606 projectMetadata.setLatestVersion( latestVersion );
607 projectMetadata.setLastUpdatedTimestamp( lastUpdatedTimestamp );
608 projectMetadata.setAvailableVersions( availableVersions );
610 if ( !VersionUtil.isSnapshot( version ) )
612 projectMetadata.setReleasedVersion( latestVersion );
615 RepositoryMetadataWriter.write( projectMetadata, projectMetadataFile );
619 fixChecksums( projectMetadataFile );
623 public void validate()
627 // is this enough check for the repository permission?
628 if ( !userRepositories.isAuthorizedToUploadArtifacts( getPrincipal(), repositoryId ) )
630 addActionError( "User is not authorized to upload in repository " + repositoryId );
633 if ( artifactFile == null || artifactFile.length() == 0 )
635 addActionError( "Please add a file to upload." );
638 if ( version == null || !VersionUtil.isVersion( version ) )
640 addActionError( "Invalid version." );
643 catch ( PrincipalNotFoundException pe )
645 addActionError( pe.getMessage() );
647 catch ( ArchivaSecurityException ae )
649 addActionError( ae.getMessage() );
653 private List<String> getManagableRepos()
657 return userRepositories.getManagableRepositoryIds( getPrincipal() );
659 catch ( PrincipalNotFoundException e )
661 log.warn( e.getMessage(), e );
663 catch ( AccessDeniedException e )
665 log.warn( e.getMessage(), e );
666 // TODO: pass this onto the screen.
668 catch ( ArchivaSecurityException e )
670 log.warn( e.getMessage(), e );
672 return Collections.emptyList();
675 private void queueRepositoryTask( String repositoryId, File localFile )
677 RepositoryTask task = new RepositoryTask();
678 task.setRepositoryId( repositoryId );
679 task.setResourceFile( localFile );
680 task.setUpdateRelatedArtifacts( true );
681 task.setScanAll( false );
685 scheduler.queueTask( task );
687 catch ( TaskQueueException e )
689 log.error( "Unable to queue repository task to execute consumers on resource file ['" + localFile.getName()
694 public void setScheduler( ArchivaTaskScheduler scheduler )
696 this.scheduler = scheduler;
699 public void setRepositoryFactory( RepositoryContentFactory repositoryFactory )
701 this.repositoryFactory = repositoryFactory;
704 public ManagedRepositoryAdmin getManagedRepositoryAdmin()
706 return managedRepositoryAdmin;
709 public void setManagedRepositoryAdmin( ManagedRepositoryAdmin managedRepositoryAdmin )
711 this.managedRepositoryAdmin = managedRepositoryAdmin;
714 public ArchivaAdministration getArchivaAdministration()
716 return archivaAdministration;
719 public void setArchivaAdministration( ArchivaAdministration archivaAdministration )
721 this.archivaAdministration = archivaAdministration;