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.codehaus.plexus.taskqueue.TaskQueueException;
57 import org.codehaus.plexus.util.IOUtil;
58 import org.springframework.context.annotation.Scope;
59 import org.springframework.stereotype.Controller;
61 import javax.inject.Inject;
62 import javax.inject.Named;
64 import java.io.FileInputStream;
65 import java.io.FileOutputStream;
66 import java.io.FileWriter;
67 import java.io.IOException;
68 import java.text.DateFormat;
69 import java.text.SimpleDateFormat;
70 import java.util.ArrayList;
71 import java.util.Calendar;
72 import java.util.Collections;
73 import java.util.Date;
74 import java.util.List;
75 import java.util.TimeZone;
78 * Upload an artifact using Jakarta file upload in webwork. If set by the user a pom will also be generated. Metadata
79 * will also be updated if one exists, otherwise it would be created.
81 @SuppressWarnings( "serial" )
82 @Controller( "uploadAction" )
84 public class UploadAction
85 extends AbstractActionSupport
86 implements Validateable, Preparable, Auditable
89 * The groupId of the artifact to be deployed.
91 private String groupId;
94 * The artifactId of the artifact to be deployed.
96 private String artifactId;
99 * The version of the artifact to be deployed.
101 private String version;
104 * The packaging of the artifact to be deployed.
106 private String packaging;
109 * The classifier of the artifact to be deployed.
111 private String classifier;
114 * The temporary file representing the artifact to be deployed.
116 private File artifactFile;
119 * The temporary file representing the pom to be deployed alongside the artifact.
121 private File pomFile;
124 * The repository where the artifact is to be deployed.
126 private String repositoryId;
129 * Flag whether to generate a pom for the artifact or not.
131 private boolean generatePom;
134 * List of managed repositories to deploy to.
136 private List<String> managedRepoIdList;
139 private ManagedRepositoryAdmin managedRepositoryAdmin;
142 private UserRepositories userRepositories;
145 private ArchivaAdministration archivaAdministration;
148 private RepositoryContentFactory repositoryFactory;
151 @Named( value = "archivaTaskScheduler#repository" )
152 private ArchivaTaskScheduler scheduler;
154 private ChecksumAlgorithm[] algorithms = new ChecksumAlgorithm[]{ ChecksumAlgorithm.SHA1, ChecksumAlgorithm.MD5 };
156 public void setArtifact( File file )
158 this.artifactFile = file;
161 public void setArtifactContentType( String contentType )
163 StringUtils.trim( contentType );
166 public void setArtifactFileName( String filename )
168 StringUtils.trim( filename );
171 public void setPom( File file )
176 public void setPomContentType( String contentType )
178 StringUtils.trim( contentType );
181 public void setPomFileName( String filename )
183 StringUtils.trim( filename );
186 public String getGroupId()
191 public void setGroupId( String groupId )
193 this.groupId = StringUtils.trim( groupId );
196 public String getArtifactId()
201 public void setArtifactId( String artifactId )
203 this.artifactId = StringUtils.trim( artifactId );
206 public String getVersion()
211 public void setVersion( String version )
213 this.version = StringUtils.trim( version );
216 public String getPackaging()
221 public void setPackaging( String packaging )
223 this.packaging = StringUtils.trim( packaging );
226 public String getClassifier()
231 public void setClassifier( String classifier )
233 this.classifier = StringUtils.trim( classifier );
236 public String getRepositoryId()
241 public void setRepositoryId( String repositoryId )
243 this.repositoryId = repositoryId;
246 public boolean isGeneratePom()
251 public void setGeneratePom( boolean generatePom )
253 this.generatePom = generatePom;
256 public List<String> getManagedRepoIdList()
258 return managedRepoIdList;
261 public void setManagedRepoIdList( List<String> managedRepoIdList )
263 this.managedRepoIdList = managedRepoIdList;
266 public void prepare()
268 managedRepoIdList = getManagableRepos();
271 public String input()
278 // reset the fields so the form is clear when
279 // the action returns to the jsp page
291 public String doUpload()
295 ManagedRepository repoConfig = managedRepositoryAdmin.getManagedRepository( repositoryId );
297 ArtifactReference artifactReference = new ArtifactReference();
298 artifactReference.setArtifactId( artifactId );
299 artifactReference.setGroupId( groupId );
300 artifactReference.setVersion( version );
301 artifactReference.setClassifier( classifier );
302 artifactReference.setType( packaging );
304 ManagedRepositoryContent repository = repositoryFactory.getManagedRepositoryContent( repositoryId );
306 String artifactPath = repository.toPath( artifactReference );
308 int lastIndex = artifactPath.lastIndexOf( '/' );
310 String path = artifactPath.substring( 0, lastIndex );
311 File targetPath = new File( repoConfig.getLocation(), path );
313 Date lastUpdatedTimestamp = Calendar.getInstance().getTime();
314 int newBuildNumber = -1;
315 String timestamp = null;
317 File versionMetadataFile = new File( targetPath, MetadataTools.MAVEN_METADATA );
318 ArchivaRepositoryMetadata versionMetadata = getMetadata( versionMetadataFile );
320 if ( VersionUtil.isSnapshot( version ) )
322 TimeZone timezone = TimeZone.getTimeZone( "UTC" );
323 DateFormat fmt = new SimpleDateFormat( "yyyyMMdd.HHmmss" );
324 fmt.setTimeZone( timezone );
325 timestamp = fmt.format( lastUpdatedTimestamp );
326 if ( versionMetadata.getSnapshotVersion() != null )
328 newBuildNumber = versionMetadata.getSnapshotVersion().getBuildNumber() + 1;
336 if ( !targetPath.exists() )
341 String filename = artifactPath.substring( lastIndex + 1 );
342 if ( VersionUtil.isSnapshot( version ) )
344 filename = filename.replaceAll( "SNAPSHOT", timestamp + "-" + newBuildNumber );
347 boolean fixChecksums =
348 !( archivaAdministration.getKnownContentConsumers().contains( "create-missing-checksums" ) );
352 File targetFile = new File( targetPath, filename );
353 if ( targetFile.exists() && !VersionUtil.isSnapshot( version ) && repoConfig.isBlockRedeployments() )
356 "Overwriting released artifacts in repository '" + repoConfig.getId() + "' is not allowed." );
361 copyFile( artifactFile, targetPath, filename, fixChecksums );
362 triggerAuditEvent( repository.getId(), path + "/" + filename, AuditEvent.UPLOAD_FILE );
363 queueRepositoryTask( repository.getId(), targetFile );
366 catch ( IOException ie )
368 addActionError( "Error encountered while uploading file: " + ie.getMessage() );
372 String pomFilename = filename;
373 if ( classifier != null && !"".equals( classifier ) )
375 pomFilename = StringUtils.remove( pomFilename, "-" + classifier );
377 pomFilename = FilenameUtils.removeExtension( pomFilename ) + ".pom";
383 File generatedPomFile = createPom( targetPath, pomFilename );
384 triggerAuditEvent( repoConfig.getId(), path + "/" + pomFilename, AuditEvent.UPLOAD_FILE );
387 fixChecksums( generatedPomFile );
389 queueRepositoryTask( repoConfig.getId(), generatedPomFile );
391 catch ( IOException ie )
393 addActionError( "Error encountered while writing pom file: " + ie.getMessage() );
398 if ( pomFile != null && pomFile.length() > 0 )
402 copyFile( pomFile, targetPath, pomFilename, fixChecksums );
403 triggerAuditEvent( repoConfig.getId(), path + "/" + pomFilename, AuditEvent.UPLOAD_FILE );
404 queueRepositoryTask( repoConfig.getId(), new File( targetPath, pomFilename ) );
406 catch ( IOException ie )
408 addActionError( "Error encountered while uploading pom file: " + ie.getMessage() );
414 // explicitly update only if metadata-updater consumer is not enabled!
415 if ( !archivaAdministration.getKnownContentConsumers().contains( "metadata-updater" ) )
417 updateProjectMetadata( targetPath.getAbsolutePath(), lastUpdatedTimestamp, timestamp, newBuildNumber,
420 if ( VersionUtil.isSnapshot( version ) )
422 updateVersionMetadata( versionMetadata, versionMetadataFile, lastUpdatedTimestamp, timestamp,
423 newBuildNumber, fixChecksums );
427 String msg = "Artifact \'" + groupId + ":" + artifactId + ":" + version
428 + "\' was successfully deployed to repository \'" + repositoryId + "\'";
430 addActionMessage( msg );
435 catch ( RepositoryNotFoundException re )
437 addActionError( "Target repository cannot be found: " + re.getMessage() );
440 catch ( RepositoryException rep )
442 addActionError( "Repository exception: " + rep.getMessage() );
445 catch ( RepositoryAdminException e )
447 addActionError( "RepositoryAdmin exception: " + e.getMessage() );
452 private void fixChecksums( File file )
454 ChecksummedFile checksum = new ChecksummedFile( file );
455 checksum.fixChecksums( algorithms );
458 private void copyFile( File sourceFile, File targetPath, String targetFilename, boolean fixChecksums )
461 FileOutputStream out = new FileOutputStream( new File( targetPath, targetFilename ) );
462 FileInputStream input = new FileInputStream( sourceFile );
466 IOUtils.copy( input, out );
476 fixChecksums( new File( targetPath, targetFilename ) );
480 private File createPom( File targetPath, String filename )
483 Model projectModel = new Model();
484 projectModel.setModelVersion( "4.0.0" );
485 projectModel.setGroupId( groupId );
486 projectModel.setArtifactId( artifactId );
487 projectModel.setVersion( version );
488 projectModel.setPackaging( packaging );
490 File pomFile = new File( targetPath, filename );
491 MavenXpp3Writer writer = new MavenXpp3Writer();
492 FileWriter w = new FileWriter( pomFile );
495 writer.write( w, projectModel );
505 private ArchivaRepositoryMetadata getMetadata( File metadataFile )
506 throws RepositoryMetadataException
508 ArchivaRepositoryMetadata metadata = new ArchivaRepositoryMetadata();
509 if ( metadataFile.exists() )
511 metadata = RepositoryMetadataReader.read( metadataFile );
518 * Update version level metadata for snapshot artifacts. If it does not exist, create the metadata and fix checksums
521 private void updateVersionMetadata( ArchivaRepositoryMetadata metadata, File metadataFile,
522 Date lastUpdatedTimestamp, String timestamp, int buildNumber,
523 boolean fixChecksums )
524 throws RepositoryMetadataException
526 if ( !metadataFile.exists() )
528 metadata.setGroupId( groupId );
529 metadata.setArtifactId( artifactId );
530 metadata.setVersion( version );
533 if ( metadata.getSnapshotVersion() == null )
535 metadata.setSnapshotVersion( new SnapshotVersion() );
538 metadata.getSnapshotVersion().setBuildNumber( buildNumber );
539 metadata.getSnapshotVersion().setTimestamp( timestamp );
540 metadata.setLastUpdatedTimestamp( lastUpdatedTimestamp );
542 RepositoryMetadataWriter.write( metadata, metadataFile );
546 fixChecksums( metadataFile );
551 * Update artifact level metadata. If it does not exist, create the metadata and fix checksums if necessary.
553 private void updateProjectMetadata( String targetPath, Date lastUpdatedTimestamp, String timestamp, int buildNumber,
554 boolean fixChecksums )
555 throws RepositoryMetadataException
557 List<String> availableVersions = new ArrayList<String>();
558 String latestVersion = version;
560 File projectDir = new File( targetPath ).getParentFile();
561 File projectMetadataFile = new File( projectDir, MetadataTools.MAVEN_METADATA );
563 ArchivaRepositoryMetadata projectMetadata = getMetadata( projectMetadataFile );
565 if ( projectMetadataFile.exists() )
567 availableVersions = projectMetadata.getAvailableVersions();
569 Collections.sort( availableVersions, VersionComparator.getInstance() );
571 if ( !availableVersions.contains( version ) )
573 availableVersions.add( version );
576 latestVersion = availableVersions.get( availableVersions.size() - 1 );
580 availableVersions.add( version );
582 projectMetadata.setGroupId( groupId );
583 projectMetadata.setArtifactId( artifactId );
586 if ( projectMetadata.getGroupId() == null )
588 projectMetadata.setGroupId( groupId );
591 if ( projectMetadata.getArtifactId() == null )
593 projectMetadata.setArtifactId( artifactId );
596 projectMetadata.setLatestVersion( latestVersion );
597 projectMetadata.setLastUpdatedTimestamp( lastUpdatedTimestamp );
598 projectMetadata.setAvailableVersions( availableVersions );
600 if ( !VersionUtil.isSnapshot( version ) )
602 projectMetadata.setReleasedVersion( latestVersion );
605 RepositoryMetadataWriter.write( projectMetadata, projectMetadataFile );
609 fixChecksums( projectMetadataFile );
613 public void validate()
617 // is this enough check for the repository permission?
618 if ( !userRepositories.isAuthorizedToUploadArtifacts( getPrincipal(), repositoryId ) )
620 addActionError( "User is not authorized to upload in repository " + repositoryId );
623 if ( artifactFile == null || artifactFile.length() == 0 )
625 addActionError( "Please add a file to upload." );
628 if ( version == null || !VersionUtil.isVersion( version ) )
630 addActionError( "Invalid version." );
633 catch ( PrincipalNotFoundException pe )
635 addActionError( pe.getMessage() );
637 catch ( ArchivaSecurityException ae )
639 addActionError( ae.getMessage() );
643 private List<String> getManagableRepos()
647 return userRepositories.getManagableRepositoryIds( getPrincipal() );
649 catch ( PrincipalNotFoundException e )
651 log.warn( e.getMessage(), e );
653 catch ( AccessDeniedException e )
655 log.warn( e.getMessage(), e );
656 // TODO: pass this onto the screen.
658 catch ( ArchivaSecurityException e )
660 log.warn( e.getMessage(), e );
662 return Collections.emptyList();
665 private void queueRepositoryTask( String repositoryId, File localFile )
667 RepositoryTask task = new RepositoryTask();
668 task.setRepositoryId( repositoryId );
669 task.setResourceFile( localFile );
670 task.setUpdateRelatedArtifacts( true );
671 task.setScanAll( true );
675 scheduler.queueTask( task );
677 catch ( TaskQueueException e )
679 log.error( "Unable to queue repository task to execute consumers on resource file ['" + localFile.getName()
684 public void setScheduler( ArchivaTaskScheduler scheduler )
686 this.scheduler = scheduler;
689 public void setRepositoryFactory( RepositoryContentFactory repositoryFactory )
691 this.repositoryFactory = repositoryFactory;
694 public ManagedRepositoryAdmin getManagedRepositoryAdmin()
696 return managedRepositoryAdmin;
699 public void setManagedRepositoryAdmin( ManagedRepositoryAdmin managedRepositoryAdmin )
701 this.managedRepositoryAdmin = managedRepositoryAdmin;
704 public ArchivaAdministration getArchivaAdministration()
706 return archivaAdministration;
709 public void setArchivaAdministration( ArchivaAdministration archivaAdministration )
711 this.archivaAdministration = archivaAdministration;