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.IOException;
26 import java.text.DateFormat;
27 import java.text.SimpleDateFormat;
28 import java.util.ArrayList;
29 import java.util.Calendar;
30 import java.util.Date;
31 import java.util.Collections;
32 import java.util.List;
33 import java.util.TimeZone;
35 import org.apache.archiva.checksum.ChecksumAlgorithm;
36 import org.apache.archiva.checksum.ChecksummedFile;
37 import org.apache.maven.archiva.common.utils.VersionComparator;
38 import org.apache.maven.archiva.common.utils.VersionUtil;
39 import org.apache.maven.archiva.configuration.ArchivaConfiguration;
40 import org.apache.maven.archiva.configuration.ManagedRepositoryConfiguration;
41 import org.apache.maven.archiva.model.ArchivaProjectModel;
42 import org.apache.maven.archiva.model.ArchivaRepositoryMetadata;
43 import org.apache.maven.archiva.model.ArtifactReference;
44 import org.apache.maven.archiva.model.SnapshotVersion;
45 import org.apache.maven.archiva.repository.ManagedRepositoryContent;
46 import org.apache.maven.archiva.repository.RepositoryContentFactory;
47 import org.apache.maven.archiva.repository.RepositoryException;
48 import org.apache.maven.archiva.repository.RepositoryNotFoundException;
49 import org.apache.maven.archiva.repository.scanner.RepositoryContentConsumers;
50 import org.apache.maven.archiva.repository.audit.AuditEvent;
51 import org.apache.maven.archiva.repository.audit.AuditListener;
52 import org.apache.maven.archiva.repository.audit.Auditable;
53 import org.apache.maven.archiva.repository.metadata.MetadataTools;
54 import org.apache.maven.archiva.repository.metadata.RepositoryMetadataException;
55 import org.apache.maven.archiva.repository.metadata.RepositoryMetadataReader;
56 import org.apache.maven.archiva.repository.metadata.RepositoryMetadataWriter;
57 import org.apache.maven.archiva.repository.project.ProjectModelException;
58 import org.apache.maven.archiva.repository.project.ProjectModelWriter;
59 import org.apache.maven.archiva.repository.project.writers.ProjectModel400Writer;
60 import org.apache.maven.archiva.security.AccessDeniedException;
61 import org.apache.maven.archiva.security.ArchivaSecurityException;
62 import org.apache.maven.archiva.security.PrincipalNotFoundException;
63 import org.apache.maven.archiva.security.UserRepositories;
64 import org.apache.maven.archiva.security.ArchivaXworkUser;
66 import org.apache.struts2.ServletActionContext;
67 import com.opensymphony.xwork2.ActionContext;
68 import com.opensymphony.xwork2.Preparable;
69 import com.opensymphony.xwork2.Validateable;
70 import org.apache.commons.io.FilenameUtils;
71 import org.apache.commons.lang.StringUtils;
74 * Upload an artifact using Jakarta file upload in webwork. If set by the user a pom will also be generated. Metadata
75 * will also be updated if one exists, otherwise it would be created.
77 * @plexus.component role="com.opensymphony.xwork2.Action" role-hint="uploadAction" instantiation-strategy="per-lookup"
79 public class UploadAction
80 extends PlexusActionSupport
81 implements Validateable, Preparable, Auditable
86 private RepositoryContentConsumers consumers;
91 private ArchivaXworkUser archivaXworkUser;
94 * The groupId of the artifact to be deployed.
96 private String groupId;
99 * The artifactId of the artifact to be deployed.
101 private String artifactId;
104 * The version of the artifact to be deployed.
106 private String version;
109 * The packaging of the artifact to be deployed.
111 private String packaging;
114 * The classifier of the artifact to be deployed.
116 private String classifier;
119 * The temporary file representing the artifact to be deployed.
121 private File artifactFile;
124 * The temporary file representing the pom to be deployed alongside the artifact.
126 private File pomFile;
129 * The repository where the artifact is to be deployed.
131 private String repositoryId;
134 * Flag whether to generate a pom for the artifact or not.
136 private boolean generatePom;
139 * List of managed repositories to deploy to.
141 private List<String> managedRepoIdList;
144 * @plexus.requirement
146 private UserRepositories userRepositories;
149 * @plexus.requirement role-hint="default"
151 private ArchivaConfiguration configuration;
154 * @plexus.requirement
156 private RepositoryContentFactory repositoryFactory;
159 * @plexus.requirement role="org.apache.maven.archiva.repository.audit.AuditListener"
161 private List<AuditListener> auditListeners = new ArrayList<AuditListener>();
163 private ChecksumAlgorithm[] algorithms = new ChecksumAlgorithm[] { ChecksumAlgorithm.SHA1, ChecksumAlgorithm.MD5 };
165 private ProjectModelWriter pomWriter = new ProjectModel400Writer();
167 public void setArtifact( File file )
169 this.artifactFile = file;
172 public void setArtifactContentType( String contentType )
174 StringUtils.trim( contentType );
177 public void setArtifactFileName( String filename )
179 StringUtils.trim( filename );
182 public void setPom( File file )
187 public void setPomContentType( String contentType )
189 StringUtils.trim( contentType );
192 public void setPomFileName( String filename )
194 StringUtils.trim( filename );
197 public String getGroupId()
202 public void setGroupId( String groupId )
204 this.groupId = StringUtils.trim( groupId );
207 public String getArtifactId()
212 public void setArtifactId( String artifactId )
214 this.artifactId = StringUtils.trim( artifactId );
217 public String getVersion()
222 public void setVersion( String version )
224 this.version = StringUtils.trim( version );
227 public String getPackaging()
232 public void setPackaging( String packaging )
234 this.packaging = StringUtils.trim( packaging );
237 public String getClassifier()
242 public void setClassifier( String classifier )
244 this.classifier = StringUtils.trim( classifier );
247 public String getRepositoryId()
252 public void setRepositoryId( String repositoryId )
254 this.repositoryId = repositoryId;
257 public boolean isGeneratePom()
262 public void setGeneratePom( boolean generatePom )
264 this.generatePom = generatePom;
267 public List<String> getManagedRepoIdList()
269 return managedRepoIdList;
272 public void setManagedRepoIdList( List<String> managedRepoIdList )
274 this.managedRepoIdList = managedRepoIdList;
277 public void prepare()
279 managedRepoIdList = getManagableRepos();
282 public String input()
289 // reset the fields so the form is clear when
290 // the action returns to the jsp page
302 public String doUpload()
306 ManagedRepositoryConfiguration repoConfig =
307 configuration.getConfiguration().findManagedRepositoryById( repositoryId );
309 ArtifactReference artifactReference = new ArtifactReference();
310 artifactReference.setArtifactId( artifactId );
311 artifactReference.setGroupId( groupId );
312 artifactReference.setVersion( version );
313 artifactReference.setClassifier( classifier );
314 artifactReference.setType( packaging );
316 ManagedRepositoryContent repository = repositoryFactory.getManagedRepositoryContent( repositoryId );
318 String artifactPath = repository.toPath( artifactReference );
320 int lastIndex = artifactPath.lastIndexOf( '/' );
322 File targetPath = new File( repoConfig.getLocation(), artifactPath.substring( 0, lastIndex ) );
324 Date lastUpdatedTimestamp = Calendar.getInstance().getTime();
325 int newBuildNumber = -1;
326 String timestamp = null;
328 File metadataFile = getMetadata( targetPath.getAbsolutePath() );
329 ArchivaRepositoryMetadata metadata = getMetadata( metadataFile );
331 if (VersionUtil.isSnapshot(version))
333 TimeZone timezone = TimeZone.getTimeZone( "UTC" );
334 DateFormat fmt = new SimpleDateFormat( "yyyyMMdd.HHmmss" );
335 fmt.setTimeZone( timezone );
336 timestamp = fmt.format( lastUpdatedTimestamp );
337 if ( metadata.getSnapshotVersion() != null )
339 newBuildNumber = metadata.getSnapshotVersion().getBuildNumber() + 1;
343 metadata.setSnapshotVersion( new SnapshotVersion() );
348 if ( !targetPath.exists() )
353 String filename = artifactPath.substring( lastIndex + 1 );
354 if ( VersionUtil.isSnapshot( version ) )
356 filename = filename.replaceAll( "SNAPSHOT", timestamp + "-" + newBuildNumber );
361 copyFile( artifactFile, targetPath, filename );
362 consumers.executeConsumers( repoConfig, repository.toFile( artifactReference ) );
364 catch ( IOException ie )
366 addActionError( "Error encountered while uploading file: " + ie.getMessage() );
370 String pomFilename = filename;
371 if( classifier != null && !"".equals( classifier ) )
373 pomFilename = StringUtils.remove( pomFilename, "-" + classifier );
375 pomFilename = FilenameUtils.removeExtension( pomFilename ) + ".pom";
381 File generatedPomFile = createPom( targetPath, pomFilename );
382 consumers.executeConsumers( repoConfig, generatedPomFile );
384 catch ( IOException ie )
386 addActionError( "Error encountered while writing pom file: " + ie.getMessage() );
389 catch ( ProjectModelException pe )
391 addActionError( "Error encountered while generating pom file: " + pe.getMessage() );
396 if ( pomFile != null && pomFile.length() > 0 )
400 copyFile( pomFile, targetPath, pomFilename );
401 consumers.executeConsumers( repoConfig, new File( targetPath, pomFilename ) );
403 catch ( IOException ie )
405 addActionError( "Error encountered while uploading pom file: " + ie.getMessage() );
411 updateMetadata( metadata, metadataFile, lastUpdatedTimestamp, timestamp, newBuildNumber );
413 String msg = "Artifact \'" + groupId + ":" + artifactId + ":" + version +
414 "\' was successfully deployed to repository \'" + repositoryId + "\'";
416 triggerAuditEvent( getPrincipal(), repositoryId, groupId + ":" + artifactId + ":" + version, AuditEvent.UPLOAD_FILE );
418 addActionMessage( msg );
423 catch ( RepositoryNotFoundException re )
425 addActionError( "Target repository cannot be found: " + re.getMessage() );
428 catch ( RepositoryException rep )
430 addActionError( "Repository exception: " + rep.getMessage() );
435 @SuppressWarnings("unchecked")
436 private String getPrincipal()
438 return archivaXworkUser.getActivePrincipal( ActionContext.getContext().getSession() );
441 private void copyFile( File sourceFile, File targetPath, String targetFilename )
444 FileOutputStream out = new FileOutputStream( new File( targetPath, targetFilename ) );
448 FileInputStream input = new FileInputStream( sourceFile );
450 while ( ( i = input.read() ) != -1 )
462 private File createPom( File targetPath, String filename )
463 throws IOException, ProjectModelException
465 ArchivaProjectModel projectModel = new ArchivaProjectModel();
466 projectModel.setGroupId( groupId );
467 projectModel.setArtifactId( artifactId );
468 projectModel.setVersion( version );
469 projectModel.setPackaging( packaging );
471 File pomFile = new File( targetPath, filename);
472 pomWriter.write( projectModel, pomFile );
477 private File getMetadata( String targetPath )
479 String artifactPath = targetPath.substring( 0, targetPath.lastIndexOf( File.separatorChar ) );
481 return new File( artifactPath, MetadataTools.MAVEN_METADATA );
484 private ArchivaRepositoryMetadata getMetadata( File metadataFile )
485 throws RepositoryMetadataException
487 ArchivaRepositoryMetadata metadata = new ArchivaRepositoryMetadata();
488 if ( metadataFile.exists() )
490 metadata = RepositoryMetadataReader.read( metadataFile );
496 * Update artifact level metadata. If it does not exist, create the metadata.
500 private void updateMetadata( ArchivaRepositoryMetadata metadata, File metadataFile, Date lastUpdatedTimestamp,
501 String timestamp, int buildNumber )
502 throws RepositoryMetadataException
504 List<String> availableVersions = new ArrayList<String>();
505 String latestVersion = version;
507 if ( metadataFile.exists() )
509 availableVersions = metadata.getAvailableVersions();
511 Collections.sort( availableVersions, VersionComparator.getInstance() );
513 if ( !availableVersions.contains( version ) )
515 availableVersions.add( version );
518 latestVersion = availableVersions.get( availableVersions.size() - 1 );
522 availableVersions.add( version );
524 metadata.setGroupId( groupId );
525 metadata.setArtifactId( artifactId );
528 if ( metadata.getGroupId() == null )
530 metadata.setGroupId( groupId );
532 if ( metadata.getArtifactId() == null )
534 metadata.setArtifactId( artifactId );
537 metadata.setLatestVersion( latestVersion );
538 metadata.setLastUpdatedTimestamp( lastUpdatedTimestamp );
539 metadata.setAvailableVersions( availableVersions );
541 if ( !VersionUtil.isSnapshot( version ) )
543 metadata.setReleasedVersion( latestVersion );
547 metadata.getSnapshotVersion().setBuildNumber( buildNumber );
549 metadata.getSnapshotVersion().setTimestamp( timestamp );
552 RepositoryMetadataWriter.write( metadata, metadataFile );
553 ChecksummedFile checksum = new ChecksummedFile( metadataFile );
554 checksum.fixChecksums( algorithms );
557 public void validate()
561 // is this enough check for the repository permission?
562 if ( !userRepositories.isAuthorizedToUploadArtifacts( getPrincipal(), repositoryId ) )
564 addActionError( "User is not authorized to upload in repository " + repositoryId );
567 if ( artifactFile == null || artifactFile.length() == 0 )
569 addActionError( "Please add a file to upload." );
572 if ( !VersionUtil.isVersion( version ) )
574 addActionError( "Invalid version." );
577 catch ( PrincipalNotFoundException pe )
579 addActionError( pe.getMessage() );
581 catch ( ArchivaSecurityException ae )
583 addActionError( ae.getMessage() );
587 public void addAuditListener( AuditListener listener )
589 this.auditListeners.add( listener );
592 public void clearAuditListeners()
594 this.auditListeners.clear();
597 public void removeAuditListener( AuditListener listener )
599 this.auditListeners.remove( listener );
602 private List<String> getManagableRepos()
606 return userRepositories.getManagableRepositoryIds( getPrincipal() );
608 catch ( PrincipalNotFoundException e )
610 log.warn( e.getMessage(), e );
612 catch ( AccessDeniedException e )
614 log.warn( e.getMessage(), e );
615 // TODO: pass this onto the screen.
617 catch ( ArchivaSecurityException e )
619 log.warn( e.getMessage(), e );
621 return Collections.emptyList();
624 private void triggerAuditEvent( String user, String repositoryId, String resource, String action )
626 AuditEvent event = new AuditEvent( repositoryId, user, resource, action );
627 event.setRemoteIP( ServletActionContext.getRequest().getRemoteAddr() );
629 for ( AuditListener listener : auditListeners )
631 listener.auditEvent( event );