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
23 import java.io.FileInputStream;
24 import java.io.FileOutputStream;
25 import java.io.FileWriter;
26 import java.io.IOException;
27 import java.text.DateFormat;
28 import java.text.SimpleDateFormat;
29 import java.util.ArrayList;
30 import java.util.Calendar;
31 import java.util.Collections;
32 import java.util.Date;
33 import java.util.List;
34 import java.util.TimeZone;
36 import com.opensymphony.xwork2.Preparable;
37 import com.opensymphony.xwork2.Validateable;
38 import org.apache.archiva.checksum.ChecksumAlgorithm;
39 import org.apache.archiva.checksum.ChecksummedFile;
40 import org.apache.archiva.scheduler.ArchivaTaskScheduler;
41 import org.apache.archiva.scheduler.repository.RepositoryTask;
42 import org.apache.commons.io.FilenameUtils;
43 import org.apache.commons.lang.StringUtils;
44 import org.apache.maven.archiva.common.utils.VersionComparator;
45 import org.apache.maven.archiva.common.utils.VersionUtil;
46 import org.apache.maven.archiva.configuration.ArchivaConfiguration;
47 import org.apache.maven.archiva.configuration.Configuration;
48 import org.apache.maven.archiva.configuration.ManagedRepositoryConfiguration;
49 import org.apache.maven.archiva.model.ArchivaRepositoryMetadata;
50 import org.apache.maven.archiva.model.ArtifactReference;
51 import org.apache.maven.archiva.model.SnapshotVersion;
52 import org.apache.maven.archiva.repository.ManagedRepositoryContent;
53 import org.apache.maven.archiva.repository.RepositoryContentFactory;
54 import org.apache.maven.archiva.repository.RepositoryException;
55 import org.apache.maven.archiva.repository.RepositoryNotFoundException;
56 import org.apache.maven.archiva.repository.audit.AuditEvent;
57 import org.apache.maven.archiva.repository.audit.Auditable;
58 import org.apache.maven.archiva.repository.metadata.MetadataTools;
59 import org.apache.maven.archiva.repository.metadata.RepositoryMetadataException;
60 import org.apache.maven.archiva.repository.metadata.RepositoryMetadataReader;
61 import org.apache.maven.archiva.repository.metadata.RepositoryMetadataWriter;
62 import org.apache.maven.archiva.security.AccessDeniedException;
63 import org.apache.maven.archiva.security.ArchivaSecurityException;
64 import org.apache.maven.archiva.security.PrincipalNotFoundException;
65 import org.apache.maven.archiva.security.UserRepositories;
66 import org.apache.maven.model.Model;
67 import org.apache.maven.model.io.xpp3.MavenXpp3Writer;
68 import org.codehaus.plexus.taskqueue.TaskQueueException;
69 import org.codehaus.plexus.util.IOUtil;
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 public class UploadAction
78 extends PlexusActionSupport
79 implements Validateable, Preparable, Auditable
82 * The groupId of the artifact to be deployed.
84 private String groupId;
87 * The artifactId of the artifact to be deployed.
89 private String artifactId;
92 * The version of the artifact to be deployed.
94 private String version;
97 * The packaging of the artifact to be deployed.
99 private String packaging;
102 * The classifier of the artifact to be deployed.
104 private String classifier;
107 * The temporary file representing the artifact to be deployed.
109 private File artifactFile;
112 * The temporary file representing the pom to be deployed alongside the artifact.
114 private File pomFile;
117 * The repository where the artifact is to be deployed.
119 private String repositoryId;
122 * Flag whether to generate a pom for the artifact or not.
124 private boolean generatePom;
127 * List of managed repositories to deploy to.
129 private List<String> managedRepoIdList;
132 * @plexus.requirement
134 private UserRepositories userRepositories;
137 * @plexus.requirement role-hint="default"
139 private ArchivaConfiguration configuration;
142 * @plexus.requirement
144 private RepositoryContentFactory repositoryFactory;
147 * @plexus.requirement role="org.apache.archiva.scheduler.ArchivaTaskScheduler" role-hint="repository"
149 private ArchivaTaskScheduler scheduler;
151 private ChecksumAlgorithm[] algorithms = new ChecksumAlgorithm[]{ChecksumAlgorithm.SHA1, ChecksumAlgorithm.MD5};
153 public void setArtifact( File file )
155 this.artifactFile = file;
158 public void setArtifactContentType( String contentType )
160 StringUtils.trim( contentType );
163 public void setArtifactFileName( String filename )
165 StringUtils.trim( filename );
168 public void setPom( File file )
173 public void setPomContentType( String contentType )
175 StringUtils.trim( contentType );
178 public void setPomFileName( String filename )
180 StringUtils.trim( filename );
183 public String getGroupId()
188 public void setGroupId( String groupId )
190 this.groupId = StringUtils.trim( groupId );
193 public String getArtifactId()
198 public void setArtifactId( String artifactId )
200 this.artifactId = StringUtils.trim( artifactId );
203 public String getVersion()
208 public void setVersion( String version )
210 this.version = StringUtils.trim( version );
213 public String getPackaging()
218 public void setPackaging( String packaging )
220 this.packaging = StringUtils.trim( packaging );
223 public String getClassifier()
228 public void setClassifier( String classifier )
230 this.classifier = StringUtils.trim( classifier );
233 public String getRepositoryId()
238 public void setRepositoryId( String repositoryId )
240 this.repositoryId = repositoryId;
243 public boolean isGeneratePom()
248 public void setGeneratePom( boolean generatePom )
250 this.generatePom = generatePom;
253 public List<String> getManagedRepoIdList()
255 return managedRepoIdList;
258 public void setManagedRepoIdList( List<String> managedRepoIdList )
260 this.managedRepoIdList = managedRepoIdList;
263 public void prepare()
265 managedRepoIdList = getManagableRepos();
268 public String input()
275 // reset the fields so the form is clear when
276 // the action returns to the jsp page
288 public String doUpload()
292 Configuration config = configuration.getConfiguration();
293 ManagedRepositoryConfiguration repoConfig = config.findManagedRepositoryById( repositoryId );
295 ArtifactReference artifactReference = new ArtifactReference();
296 artifactReference.setArtifactId( artifactId );
297 artifactReference.setGroupId( groupId );
298 artifactReference.setVersion( version );
299 artifactReference.setClassifier( classifier );
300 artifactReference.setType( packaging );
302 ManagedRepositoryContent repository = repositoryFactory.getManagedRepositoryContent( repositoryId );
304 String artifactPath = repository.toPath( artifactReference );
306 int lastIndex = artifactPath.lastIndexOf( '/' );
308 String path = artifactPath.substring( 0, lastIndex );
309 File targetPath = new File( repoConfig.getLocation(), path );
311 Date lastUpdatedTimestamp = Calendar.getInstance().getTime();
312 int newBuildNumber = -1;
313 String timestamp = null;
315 File metadataFile = getMetadata( targetPath.getAbsolutePath() );
316 ArchivaRepositoryMetadata metadata = getMetadata( metadataFile );
318 if ( VersionUtil.isSnapshot( version ) )
320 TimeZone timezone = TimeZone.getTimeZone( "UTC" );
321 DateFormat fmt = new SimpleDateFormat( "yyyyMMdd.HHmmss" );
322 fmt.setTimeZone( timezone );
323 timestamp = fmt.format( lastUpdatedTimestamp );
324 if ( metadata.getSnapshotVersion() != null )
326 newBuildNumber = metadata.getSnapshotVersion().getBuildNumber() + 1;
330 metadata.setSnapshotVersion( new SnapshotVersion() );
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 updateMetadata( metadata, metadataFile, lastUpdatedTimestamp, timestamp, newBuildNumber, fixChecksums );
419 String msg = "Artifact \'" + groupId + ":" + artifactId + ":" + version +
420 "\' was successfully deployed to repository \'" + repositoryId + "\'";
422 addActionMessage( msg );
427 catch ( RepositoryNotFoundException re )
429 addActionError( "Target repository cannot be found: " + re.getMessage() );
432 catch ( RepositoryException rep )
434 addActionError( "Repository exception: " + rep.getMessage() );
439 private void fixChecksums( File file )
441 ChecksummedFile checksum = new ChecksummedFile( file );
442 checksum.fixChecksums( algorithms );
445 private void copyFile( File sourceFile, File targetPath, String targetFilename, boolean fixChecksums )
448 FileOutputStream out = new FileOutputStream( new File( targetPath, targetFilename ) );
449 FileInputStream input = new FileInputStream( sourceFile );
454 while ( ( i = input.read() ) != -1 )
468 fixChecksums( new File( targetPath, targetFilename ) );
472 private File createPom( File targetPath, String filename )
475 Model projectModel = new Model();
476 projectModel.setModelVersion( "4.0.0" );
477 projectModel.setGroupId( groupId );
478 projectModel.setArtifactId( artifactId );
479 projectModel.setVersion( version );
480 projectModel.setPackaging( packaging );
482 File pomFile = new File( targetPath, filename );
483 MavenXpp3Writer writer = new MavenXpp3Writer();
484 FileWriter w = new FileWriter( pomFile );
487 writer.write( w, projectModel );
497 private File getMetadata( String targetPath )
499 String artifactPath = targetPath.substring( 0, targetPath.lastIndexOf( File.separatorChar ) );
501 return new File( artifactPath, MetadataTools.MAVEN_METADATA );
504 private ArchivaRepositoryMetadata getMetadata( File metadataFile )
505 throws RepositoryMetadataException
507 ArchivaRepositoryMetadata metadata = new ArchivaRepositoryMetadata();
508 if ( metadataFile.exists() )
510 metadata = RepositoryMetadataReader.read( metadataFile );
516 * Update artifact level metadata. If it does not exist, create the metadata and
517 * fix checksums if necessary.
519 private void updateMetadata( ArchivaRepositoryMetadata metadata, File metadataFile, Date lastUpdatedTimestamp,
520 String timestamp, int buildNumber, boolean fixChecksums )
521 throws RepositoryMetadataException
523 List<String> availableVersions = new ArrayList<String>();
524 String latestVersion = version;
526 if ( metadataFile.exists() )
528 availableVersions = metadata.getAvailableVersions();
530 Collections.sort( availableVersions, VersionComparator.getInstance() );
532 if ( !availableVersions.contains( version ) )
534 availableVersions.add( version );
537 latestVersion = availableVersions.get( availableVersions.size() - 1 );
541 availableVersions.add( version );
543 metadata.setGroupId( groupId );
544 metadata.setArtifactId( artifactId );
547 if ( metadata.getGroupId() == null )
549 metadata.setGroupId( groupId );
551 if ( metadata.getArtifactId() == null )
553 metadata.setArtifactId( artifactId );
556 metadata.setLatestVersion( latestVersion );
557 metadata.setLastUpdatedTimestamp( lastUpdatedTimestamp );
558 metadata.setAvailableVersions( availableVersions );
560 if ( !VersionUtil.isSnapshot( version ) )
562 metadata.setReleasedVersion( latestVersion );
566 metadata.getSnapshotVersion().setBuildNumber( buildNumber );
568 metadata.getSnapshotVersion().setTimestamp( timestamp );
571 RepositoryMetadataWriter.write( metadata, metadataFile );
575 fixChecksums( metadataFile );
579 public void validate()
583 // is this enough check for the repository permission?
584 if ( !userRepositories.isAuthorizedToUploadArtifacts( getPrincipal(), repositoryId ) )
586 addActionError( "User is not authorized to upload in repository " + repositoryId );
589 if ( artifactFile == null || artifactFile.length() == 0 )
591 addActionError( "Please add a file to upload." );
594 if ( version == null || !VersionUtil.isVersion( version ) )
596 addActionError( "Invalid version." );
599 catch ( PrincipalNotFoundException pe )
601 addActionError( pe.getMessage() );
603 catch ( ArchivaSecurityException ae )
605 addActionError( ae.getMessage() );
609 private List<String> getManagableRepos()
613 return userRepositories.getManagableRepositoryIds( getPrincipal() );
615 catch ( PrincipalNotFoundException e )
617 log.warn( e.getMessage(), e );
619 catch ( AccessDeniedException e )
621 log.warn( e.getMessage(), e );
622 // TODO: pass this onto the screen.
624 catch ( ArchivaSecurityException e )
626 log.warn( e.getMessage(), e );
628 return Collections.emptyList();
631 private void queueRepositoryTask( String repositoryId, File localFile )
633 RepositoryTask task = new RepositoryTask();
634 task.setRepositoryId( repositoryId );
635 task.setResourceFile( localFile );
636 task.setUpdateRelatedArtifacts( true );
637 task.setScanAll( true );
641 scheduler.queueTask( task );
643 catch ( TaskQueueException e )
646 "Unable to queue repository task to execute consumers on resource file ['" + localFile.getName() +
651 public void setScheduler( ArchivaTaskScheduler scheduler )
653 this.scheduler = scheduler;
656 public void setRepositoryFactory( RepositoryContentFactory repositoryFactory )
658 this.repositoryFactory = repositoryFactory;
661 public void setConfiguration( ArchivaConfiguration configuration )
663 this.configuration = configuration;