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.redback.components.taskqueue.TaskQueueException;
39 import org.apache.archiva.repository.ManagedRepositoryContent;
40 import org.apache.archiva.repository.RepositoryContentFactory;
41 import org.apache.archiva.repository.RepositoryException;
42 import org.apache.archiva.repository.RepositoryNotFoundException;
43 import org.apache.archiva.repository.metadata.MetadataTools;
44 import org.apache.archiva.repository.metadata.RepositoryMetadataException;
45 import org.apache.archiva.repository.metadata.RepositoryMetadataWriter;
46 import org.apache.archiva.scheduler.ArchivaTaskScheduler;
47 import org.apache.archiva.scheduler.repository.RepositoryTask;
48 import org.apache.archiva.security.AccessDeniedException;
49 import org.apache.archiva.security.ArchivaSecurityException;
50 import org.apache.archiva.security.PrincipalNotFoundException;
51 import org.apache.archiva.security.UserRepositories;
52 import org.apache.archiva.xml.XMLException;
53 import org.apache.commons.io.FilenameUtils;
54 import org.apache.commons.io.IOUtils;
55 import org.apache.commons.lang.StringUtils;
56 import org.apache.maven.model.Model;
57 import org.apache.maven.model.io.xpp3.MavenXpp3Writer;
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 log.debug( "artifactPath: {} found targetPath: {}", artifactPath, targetPath );
315 Date lastUpdatedTimestamp = Calendar.getInstance().getTime();
316 int newBuildNumber = -1;
317 String timestamp = null;
319 File versionMetadataFile = new File( targetPath, MetadataTools.MAVEN_METADATA );
320 ArchivaRepositoryMetadata versionMetadata = getMetadata( versionMetadataFile );
322 if ( VersionUtil.isSnapshot( version ) )
324 TimeZone timezone = TimeZone.getTimeZone( "UTC" );
325 DateFormat fmt = new SimpleDateFormat( "yyyyMMdd.HHmmss" );
326 fmt.setTimeZone( timezone );
327 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( VersionUtil.SNAPSHOT, timestamp + "-" + newBuildNumber );
349 boolean fixChecksums =
350 !( archivaAdministration.getKnownContentConsumers().contains( "create-missing-checksums" ) );
354 File targetFile = new File( targetPath, filename );
355 if ( targetFile.exists() && !VersionUtil.isSnapshot( version ) && repoConfig.isBlockRedeployments() )
358 "Overwriting released artifacts in repository '" + repoConfig.getId() + "' is not allowed." );
363 copyFile( artifactFile, targetPath, filename, fixChecksums );
364 triggerAuditEvent( repository.getId(), path + "/" + filename, AuditEvent.UPLOAD_FILE );
365 queueRepositoryTask( repository.getId(), targetFile );
368 catch ( IOException ie )
370 addActionError( "Error encountered while uploading file: " + ie.getMessage() );
374 String pomFilename = filename;
375 if ( classifier != null && !"".equals( classifier ) )
377 pomFilename = StringUtils.remove( pomFilename, "-" + classifier );
379 pomFilename = FilenameUtils.removeExtension( pomFilename ) + ".pom";
385 File generatedPomFile = createPom( targetPath, pomFilename );
386 triggerAuditEvent( repoConfig.getId(), path + "/" + pomFilename, AuditEvent.UPLOAD_FILE );
389 fixChecksums( generatedPomFile );
391 queueRepositoryTask( repoConfig.getId(), generatedPomFile );
393 catch ( IOException ie )
395 addActionError( "Error encountered while writing pom file: " + ie.getMessage() );
400 if ( pomFile != null && pomFile.length() > 0 )
404 copyFile( pomFile, targetPath, pomFilename, fixChecksums );
405 triggerAuditEvent( repoConfig.getId(), path + "/" + pomFilename, AuditEvent.UPLOAD_FILE );
406 queueRepositoryTask( repoConfig.getId(), new File( targetPath, pomFilename ) );
408 catch ( IOException ie )
410 addActionError( "Error encountered while uploading pom file: " + ie.getMessage() );
416 // explicitly update only if metadata-updater consumer is not enabled!
417 if ( !archivaAdministration.getKnownContentConsumers().contains( "metadata-updater" ) )
419 updateProjectMetadata( targetPath.getAbsolutePath(), lastUpdatedTimestamp, timestamp, newBuildNumber,
422 if ( VersionUtil.isSnapshot( version ) )
424 updateVersionMetadata( versionMetadata, versionMetadataFile, lastUpdatedTimestamp, timestamp,
425 newBuildNumber, fixChecksums );
429 String msg = "Artifact \'" + groupId + ":" + artifactId + ":" + version
430 + "\' was successfully deployed to repository \'" + repositoryId + "\'";
432 addActionMessage( msg );
437 catch ( RepositoryNotFoundException re )
439 addActionError( "Target repository cannot be found: " + re.getMessage() );
442 catch ( RepositoryException rep )
444 addActionError( "Repository exception: " + rep.getMessage() );
447 catch ( RepositoryAdminException e )
449 addActionError( "RepositoryAdmin exception: " + e.getMessage() );
454 private void fixChecksums( File file )
456 ChecksummedFile checksum = new ChecksummedFile( file );
457 checksum.fixChecksums( algorithms );
460 private void copyFile( File sourceFile, File targetPath, String targetFilename, boolean fixChecksums )
463 FileOutputStream out = new FileOutputStream( new File( targetPath, targetFilename ) );
464 FileInputStream input = new FileInputStream( sourceFile );
468 IOUtils.copy( input, out );
472 IOUtils.closeQuietly( out );
473 IOUtils.closeQuietly( input );
478 fixChecksums( new File( targetPath, targetFilename ) );
482 private File createPom( File targetPath, String filename )
485 Model projectModel = new Model();
486 projectModel.setModelVersion( "4.0.0" );
487 projectModel.setGroupId( groupId );
488 projectModel.setArtifactId( artifactId );
489 projectModel.setVersion( version );
490 projectModel.setPackaging( packaging );
492 File pomFile = new File( targetPath, filename );
493 MavenXpp3Writer writer = new MavenXpp3Writer();
494 FileWriter w = new FileWriter( pomFile );
497 writer.write( w, projectModel );
501 IOUtils.closeQuietly( w );
507 private ArchivaRepositoryMetadata getMetadata( File metadataFile )
508 throws RepositoryMetadataException
510 ArchivaRepositoryMetadata metadata = new ArchivaRepositoryMetadata();
511 if ( metadataFile.exists() )
515 metadata = MavenMetadataReader.read( metadataFile );
517 catch ( XMLException e )
519 throw new RepositoryMetadataException( e.getMessage(), e );
527 * Update version level metadata for snapshot artifacts. If it does not exist, create the metadata and fix checksums
530 private void updateVersionMetadata( ArchivaRepositoryMetadata metadata, File metadataFile,
531 Date lastUpdatedTimestamp, String timestamp, int buildNumber,
532 boolean fixChecksums )
533 throws RepositoryMetadataException
535 if ( !metadataFile.exists() )
537 metadata.setGroupId( groupId );
538 metadata.setArtifactId( artifactId );
539 metadata.setVersion( version );
542 if ( metadata.getSnapshotVersion() == null )
544 metadata.setSnapshotVersion( new SnapshotVersion() );
547 metadata.getSnapshotVersion().setBuildNumber( buildNumber );
548 metadata.getSnapshotVersion().setTimestamp( timestamp );
549 metadata.setLastUpdatedTimestamp( lastUpdatedTimestamp );
551 RepositoryMetadataWriter.write( metadata, metadataFile );
555 fixChecksums( metadataFile );
560 * Update artifact level metadata. If it does not exist, create the metadata and fix checksums if necessary.
562 private void updateProjectMetadata( String targetPath, Date lastUpdatedTimestamp, String timestamp, int buildNumber,
563 boolean fixChecksums )
564 throws RepositoryMetadataException
566 List<String> availableVersions = new ArrayList<String>();
567 String latestVersion = version;
569 File projectDir = new File( targetPath ).getParentFile();
570 File projectMetadataFile = new File( projectDir, MetadataTools.MAVEN_METADATA );
572 ArchivaRepositoryMetadata projectMetadata = getMetadata( projectMetadataFile );
574 if ( projectMetadataFile.exists() )
576 availableVersions = projectMetadata.getAvailableVersions();
578 Collections.sort( availableVersions, VersionComparator.getInstance() );
580 if ( !availableVersions.contains( version ) )
582 availableVersions.add( version );
585 latestVersion = availableVersions.get( availableVersions.size() - 1 );
589 availableVersions.add( version );
591 projectMetadata.setGroupId( groupId );
592 projectMetadata.setArtifactId( artifactId );
595 if ( projectMetadata.getGroupId() == null )
597 projectMetadata.setGroupId( groupId );
600 if ( projectMetadata.getArtifactId() == null )
602 projectMetadata.setArtifactId( artifactId );
605 projectMetadata.setLatestVersion( latestVersion );
606 projectMetadata.setLastUpdatedTimestamp( lastUpdatedTimestamp );
607 projectMetadata.setAvailableVersions( availableVersions );
609 if ( !VersionUtil.isSnapshot( version ) )
611 projectMetadata.setReleasedVersion( latestVersion );
614 RepositoryMetadataWriter.write( projectMetadata, projectMetadataFile );
618 fixChecksums( projectMetadataFile );
622 public void validate()
626 // is this enough check for the repository permission?
627 if ( !userRepositories.isAuthorizedToUploadArtifacts( getPrincipal(), repositoryId ) )
629 addActionError( "User is not authorized to upload in repository " + repositoryId );
632 if ( artifactFile == null || artifactFile.length() == 0 )
634 addActionError( "Please add a file to upload." );
637 if ( version == null || !VersionUtil.isVersion( version ) )
639 addActionError( "Invalid version." );
642 catch ( PrincipalNotFoundException pe )
644 addActionError( pe.getMessage() );
646 catch ( ArchivaSecurityException ae )
648 addActionError( ae.getMessage() );
652 private List<String> getManagableRepos()
656 return userRepositories.getManagableRepositoryIds( getPrincipal() );
658 catch ( PrincipalNotFoundException e )
660 log.warn( e.getMessage(), e );
662 catch ( AccessDeniedException e )
664 log.warn( e.getMessage(), e );
665 // TODO: pass this onto the screen.
667 catch ( ArchivaSecurityException e )
669 log.warn( e.getMessage(), e );
671 return Collections.emptyList();
674 private void queueRepositoryTask( String repositoryId, File localFile )
676 RepositoryTask task = new RepositoryTask();
677 task.setRepositoryId( repositoryId );
678 task.setResourceFile( localFile );
679 task.setUpdateRelatedArtifacts( true );
680 task.setScanAll( false );
684 scheduler.queueTask( task );
686 catch ( TaskQueueException e )
688 log.error( "Unable to queue repository task to execute consumers on resource file ['" + localFile.getName()
693 public void setScheduler( ArchivaTaskScheduler scheduler )
695 this.scheduler = scheduler;
698 public void setRepositoryFactory( RepositoryContentFactory repositoryFactory )
700 this.repositoryFactory = repositoryFactory;
703 public ManagedRepositoryAdmin getManagedRepositoryAdmin()
705 return managedRepositoryAdmin;
708 public void setManagedRepositoryAdmin( ManagedRepositoryAdmin managedRepositoryAdmin )
710 this.managedRepositoryAdmin = managedRepositoryAdmin;
713 public ArchivaAdministration getArchivaAdministration()
715 return archivaAdministration;
718 public void setArchivaAdministration( ArchivaAdministration archivaAdministration )
720 this.archivaAdministration = archivaAdministration;