1 package org.apache.archiva.webapp.ui.services.api;
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
21 import com.google.common.base.Predicate;
22 import com.google.common.collect.Iterables;
23 import org.apache.archiva.admin.model.RepositoryAdminException;
24 import org.apache.archiva.admin.model.admin.ArchivaAdministration;
25 import org.apache.archiva.admin.model.beans.ManagedRepository;
26 import org.apache.archiva.admin.model.managed.ManagedRepositoryAdmin;
27 import org.apache.archiva.audit.AuditEvent;
28 import org.apache.archiva.checksum.ChecksumAlgorithm;
29 import org.apache.archiva.checksum.ChecksummedFile;
30 import org.apache.archiva.common.utils.VersionComparator;
31 import org.apache.archiva.common.utils.VersionUtil;
32 import org.apache.archiva.maven2.metadata.MavenMetadataReader;
33 import org.apache.archiva.model.ArchivaRepositoryMetadata;
34 import org.apache.archiva.model.ArtifactReference;
35 import org.apache.archiva.model.SnapshotVersion;
36 import org.apache.archiva.repository.ManagedRepositoryContent;
37 import org.apache.archiva.repository.RepositoryContentFactory;
38 import org.apache.archiva.repository.RepositoryException;
39 import org.apache.archiva.repository.RepositoryNotFoundException;
40 import org.apache.archiva.repository.metadata.MetadataTools;
41 import org.apache.archiva.repository.metadata.RepositoryMetadataException;
42 import org.apache.archiva.repository.metadata.RepositoryMetadataWriter;
43 import org.apache.archiva.rest.api.services.ArchivaRestServiceException;
44 import org.apache.archiva.rest.services.AbstractRestService;
45 import org.apache.archiva.scheduler.ArchivaTaskScheduler;
46 import org.apache.archiva.scheduler.repository.RepositoryTask;
47 import org.apache.archiva.webapp.ui.services.model.FileMetadata;
48 import org.apache.archiva.xml.XMLException;
49 import org.apache.commons.io.FilenameUtils;
50 import org.apache.commons.io.IOUtils;
51 import org.apache.commons.lang.BooleanUtils;
52 import org.apache.commons.lang.StringUtils;
53 import org.apache.commons.lang.SystemUtils;
54 import org.apache.cxf.jaxrs.ext.multipart.Attachment;
55 import org.apache.cxf.jaxrs.ext.multipart.MultipartBody;
56 import org.apache.maven.model.Model;
57 import org.apache.maven.model.io.xpp3.MavenXpp3Writer;
58 import org.codehaus.plexus.taskqueue.TaskQueueException;
59 import org.codehaus.plexus.util.IOUtil;
60 import org.slf4j.Logger;
61 import org.slf4j.LoggerFactory;
62 import org.springframework.stereotype.Service;
64 import javax.annotation.Nullable;
65 import javax.inject.Inject;
66 import javax.inject.Named;
67 import javax.servlet.http.HttpServletRequest;
68 import javax.ws.rs.core.Context;
69 import javax.ws.rs.core.Response;
71 import java.io.FileInputStream;
72 import java.io.FileOutputStream;
73 import java.io.FileWriter;
74 import java.io.IOException;
75 import java.text.DateFormat;
76 import java.text.SimpleDateFormat;
77 import java.util.ArrayList;
78 import java.util.Calendar;
79 import java.util.Collections;
80 import java.util.Date;
81 import java.util.Iterator;
82 import java.util.List;
83 import java.util.TimeZone;
86 * @author Olivier Lamy
88 @Service( "fileUploadService#rest" )
89 public class DefaultFileUploadService
90 extends AbstractRestService
91 implements FileUploadService
93 private Logger log = LoggerFactory.getLogger( getClass() );
96 private HttpServletRequest httpServletRequest;
99 private ManagedRepositoryAdmin managedRepositoryAdmin;
102 private RepositoryContentFactory repositoryFactory;
105 private ArchivaAdministration archivaAdministration;
107 private ChecksumAlgorithm[] algorithms = new ChecksumAlgorithm[]{ ChecksumAlgorithm.SHA1, ChecksumAlgorithm.MD5 };
110 @Named( value = "archivaTaskScheduler#repository" )
111 private ArchivaTaskScheduler scheduler;
113 private String getStringValue( MultipartBody multipartBody, String attachmentId )
116 Attachment attachment = multipartBody.getAttachment( attachmentId );
117 return attachment == null ? "" : IOUtils.toString( attachment.getDataHandler().getInputStream() );
120 public FileMetadata post( MultipartBody multipartBody )
121 throws ArchivaRestServiceException
126 String groupId = getStringValue( multipartBody, "groupId" );
128 String artifactId = getStringValue( multipartBody, "artifactId" );
130 String version = getStringValue( multipartBody, "version" );
132 String packaging = getStringValue( multipartBody, "packaging" );
134 String classifier = getStringValue( multipartBody, "classifier" );
135 boolean pomFile = BooleanUtils.toBoolean( getStringValue( multipartBody, "pomFile" ) );
137 Attachment file = multipartBody.getAttachment( "files[]" );
139 //Content-Disposition: form-data; name="files[]"; filename="org.apache.karaf.features.command-2.2.2.jar"
140 String fileName = file.getContentDisposition().getParameter( "filename" );
142 File tmpFile = File.createTempFile( "upload-artifact", "tmp" );
143 tmpFile.deleteOnExit();
144 IOUtils.copy( file.getDataHandler().getInputStream(), new FileOutputStream( tmpFile ) );
145 FileMetadata fileMetadata = new FileMetadata( fileName, tmpFile.length(), "theurl" );
146 fileMetadata.setServerFileName( tmpFile.getPath() );
147 fileMetadata.setGroupId( groupId );
148 fileMetadata.setArtifactId( artifactId );
149 fileMetadata.setVersion( version );
150 fileMetadata.setVersion( version );
151 fileMetadata.setPackaging( packaging );
152 fileMetadata.setClassifier( classifier );
153 fileMetadata.setDeleteUrl( tmpFile.getName() );
154 fileMetadata.setPomFile( pomFile );
156 log.info( "uploading file:{}", fileMetadata );
158 List<FileMetadata> fileMetadatas =
159 (List<FileMetadata>) httpServletRequest.getSession().getAttribute( FILES_SESSION_KEY );
161 if ( fileMetadatas == null )
163 fileMetadatas = new ArrayList<FileMetadata>( 1 );
165 fileMetadatas.add( fileMetadata );
166 httpServletRequest.getSession().setAttribute( FILES_SESSION_KEY, fileMetadatas );
169 catch ( IOException e )
171 throw new ArchivaRestServiceException( e.getMessage(),
172 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode() );
177 public Boolean deleteFile( String fileName )
178 throws ArchivaRestServiceException
180 File file = new File( SystemUtils.getJavaIoTmpDir(), fileName );
181 log.debug( "delete file:{},exists:{}", file.getPath(), file.exists() );
182 boolean removed = getSessionFileMetadatas().remove( new FileMetadata( fileName ) );
185 return file.delete();
187 return Boolean.FALSE;
190 public Boolean clearUploadedFiles()
191 throws ArchivaRestServiceException
193 List<FileMetadata> fileMetadatas = new ArrayList( getSessionFileMetadatas() );
194 for ( FileMetadata fileMetadata : fileMetadatas )
196 deleteFile( fileMetadata.getServerFileName() );
201 public List<FileMetadata> getSessionFileMetadatas()
202 throws ArchivaRestServiceException
204 List<FileMetadata> fileMetadatas =
205 (List<FileMetadata>) httpServletRequest.getSession().getAttribute( FILES_SESSION_KEY );
207 return fileMetadatas == null ? Collections.<FileMetadata>emptyList() : fileMetadatas;
210 public Boolean save( String repositoryId, final String groupId, final String artifactId, String version,
211 String packaging, final boolean generatePom )
212 throws ArchivaRestServiceException
214 List<FileMetadata> fileMetadatas =
215 (List<FileMetadata>) httpServletRequest.getSession().getAttribute( FILES_SESSION_KEY );
216 if ( fileMetadatas == null || fileMetadatas.isEmpty() )
218 return Boolean.FALSE;
220 // get from the session file with groupId/artifactId
222 Iterable<FileMetadata> filesToAdd = Iterables.filter( fileMetadatas, new Predicate<FileMetadata>()
224 public boolean apply( FileMetadata fileMetadata )
226 return fileMetadata != null && !fileMetadata.isPomFile();
229 Iterator<FileMetadata> iterator = filesToAdd.iterator();
230 boolean pomGenerated = false;
231 while ( iterator.hasNext() )
233 FileMetadata fileMetadata = iterator.next();
234 log.debug( "fileToAdd: {}", fileMetadata );
235 saveFile( repositoryId, fileMetadata, generatePom && !pomGenerated, groupId, artifactId, version,
240 filesToAdd = Iterables.filter( fileMetadatas, new Predicate<FileMetadata>()
242 public boolean apply( @Nullable FileMetadata fileMetadata )
244 return fileMetadata != null && fileMetadata.isPomFile();
248 iterator = filesToAdd.iterator();
249 while ( iterator.hasNext() )
251 FileMetadata fileMetadata = iterator.next();
252 log.debug( "fileToAdd: {}", fileMetadata );
253 savePomFile( repositoryId, fileMetadata, groupId, artifactId, version, packaging );
259 protected void savePomFile( String repositoryId, FileMetadata fileMetadata, String groupId, String artifactId,
260 String version, String packaging )
261 throws ArchivaRestServiceException
266 boolean fixChecksums =
267 !( archivaAdministration.getKnownContentConsumers().contains( "create-missing-checksums" ) );
269 ManagedRepository repoConfig = managedRepositoryAdmin.getManagedRepository( repositoryId );
271 ArtifactReference artifactReference = new ArtifactReference();
272 artifactReference.setArtifactId( artifactId );
273 artifactReference.setGroupId( groupId );
274 artifactReference.setVersion( version );
275 artifactReference.setClassifier( fileMetadata.getClassifier() );
276 artifactReference.setType( packaging );
278 ManagedRepositoryContent repository = repositoryFactory.getManagedRepositoryContent( repositoryId );
280 String artifactPath = repository.toPath( artifactReference );
282 int lastIndex = artifactPath.lastIndexOf( '/' );
284 String path = artifactPath.substring( 0, lastIndex );
285 File targetPath = new File( repoConfig.getLocation(), path );
287 String pomFilename = artifactPath.substring( lastIndex + 1 );
288 if ( StringUtils.isNotEmpty( fileMetadata.getClassifier() ) )
290 pomFilename = StringUtils.remove( pomFilename, "-" + fileMetadata.getClassifier() );
292 pomFilename = FilenameUtils.removeExtension( pomFilename ) + ".pom";
294 copyFile( new File( fileMetadata.getServerFileName() ), targetPath, pomFilename, fixChecksums );
295 triggerAuditEvent( repoConfig.getId(), path + "/" + pomFilename, AuditEvent.UPLOAD_FILE );
296 queueRepositoryTask( repoConfig.getId(), new File( targetPath, pomFilename ) );
298 catch ( IOException ie )
300 throw new ArchivaRestServiceException( "Error encountered while uploading pom file: " + ie.getMessage(),
301 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode() );
303 catch ( RepositoryException rep )
305 throw new ArchivaRestServiceException( "Repository exception: " + rep.getMessage(),
306 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode() );
308 catch ( RepositoryAdminException e )
310 throw new ArchivaRestServiceException( "RepositoryAdmin exception: " + e.getMessage(),
311 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode() );
315 protected void saveFile( String repositoryId, FileMetadata fileMetadata, boolean generatePom, String groupId,
316 String artifactId, String version, String packaging )
317 throws ArchivaRestServiceException
322 ManagedRepository repoConfig = managedRepositoryAdmin.getManagedRepository( repositoryId );
324 ArtifactReference artifactReference = new ArtifactReference();
325 artifactReference.setArtifactId( artifactId );
326 artifactReference.setGroupId( groupId );
327 artifactReference.setVersion( version );
328 artifactReference.setClassifier( fileMetadata.getClassifier() );
329 artifactReference.setType( packaging );
331 ManagedRepositoryContent repository = repositoryFactory.getManagedRepositoryContent( repositoryId );
333 String artifactPath = repository.toPath( artifactReference );
335 int lastIndex = artifactPath.lastIndexOf( '/' );
337 String path = artifactPath.substring( 0, lastIndex );
338 File targetPath = new File( repoConfig.getLocation(), path );
340 log.debug( "artifactPath: {} found targetPath: {}", artifactPath, targetPath );
342 Date lastUpdatedTimestamp = Calendar.getInstance().getTime();
343 int newBuildNumber = -1;
344 String timestamp = null;
346 File versionMetadataFile = new File( targetPath, MetadataTools.MAVEN_METADATA );
347 ArchivaRepositoryMetadata versionMetadata = getMetadata( versionMetadataFile );
349 if ( VersionUtil.isSnapshot( version ) )
351 TimeZone timezone = TimeZone.getTimeZone( "UTC" );
352 DateFormat fmt = new SimpleDateFormat( "yyyyMMdd.HHmmss" );
353 fmt.setTimeZone( timezone );
354 timestamp = fmt.format( lastUpdatedTimestamp );
355 if ( versionMetadata.getSnapshotVersion() != null )
357 newBuildNumber = versionMetadata.getSnapshotVersion().getBuildNumber() + 1;
365 if ( !targetPath.exists() )
370 String filename = artifactPath.substring( lastIndex + 1 );
371 if ( VersionUtil.isSnapshot( version ) )
373 filename = filename.replaceAll( "SNAPSHOT", timestamp + "-" + newBuildNumber );
376 boolean fixChecksums =
377 !( archivaAdministration.getKnownContentConsumers().contains( "create-missing-checksums" ) );
381 File targetFile = new File( targetPath, filename );
382 if ( targetFile.exists() && !VersionUtil.isSnapshot( version ) && repoConfig.isBlockRedeployments() )
384 throw new ArchivaRestServiceException(
385 "Overwriting released artifacts in repository '" + repoConfig.getId() + "' is not allowed.",
386 Response.Status.BAD_REQUEST.getStatusCode() );
390 copyFile( new File( fileMetadata.getServerFileName() ), targetPath, filename, fixChecksums );
391 triggerAuditEvent( repository.getId(), path + "/" + filename, AuditEvent.UPLOAD_FILE );
392 queueRepositoryTask( repository.getId(), targetFile );
395 catch ( IOException ie )
397 throw new ArchivaRestServiceException(
398 "Overwriting released artifacts in repository '" + repoConfig.getId() + "' is not allowed.",
399 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode() );
404 String pomFilename = filename;
405 if ( StringUtils.isNotEmpty( fileMetadata.getClassifier() ) )
407 pomFilename = StringUtils.remove( pomFilename, "-" + fileMetadata.getClassifier() );
409 pomFilename = FilenameUtils.removeExtension( pomFilename ) + ".pom";
413 File generatedPomFile =
414 createPom( targetPath, pomFilename, fileMetadata, groupId, artifactId, version, packaging );
415 triggerAuditEvent( repoConfig.getId(), path + "/" + pomFilename, AuditEvent.UPLOAD_FILE );
418 fixChecksums( generatedPomFile );
420 queueRepositoryTask( repoConfig.getId(), generatedPomFile );
422 catch ( IOException ie )
424 throw new ArchivaRestServiceException(
425 "Error encountered while writing pom file: " + ie.getMessage(),
426 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode() );
430 // explicitly update only if metadata-updater consumer is not enabled!
431 if ( !archivaAdministration.getKnownContentConsumers().contains( "metadata-updater" ) )
433 updateProjectMetadata( targetPath.getAbsolutePath(), lastUpdatedTimestamp, timestamp, newBuildNumber,
434 fixChecksums, fileMetadata, groupId, artifactId, version, packaging );
436 if ( VersionUtil.isSnapshot( version ) )
438 updateVersionMetadata( versionMetadata, versionMetadataFile, lastUpdatedTimestamp, timestamp,
439 newBuildNumber, fixChecksums, fileMetadata, groupId, artifactId, version,
444 catch ( RepositoryNotFoundException re )
446 throw new ArchivaRestServiceException( "Target repository cannot be found: " + re.getMessage(),
447 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode() );
449 catch ( RepositoryException rep )
451 throw new ArchivaRestServiceException( "Repository exception: " + rep.getMessage(),
452 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode() );
454 catch ( RepositoryAdminException e )
456 throw new ArchivaRestServiceException( "RepositoryAdmin exception: " + e.getMessage(),
457 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode() );
461 private ArchivaRepositoryMetadata getMetadata( File metadataFile )
462 throws RepositoryMetadataException
464 ArchivaRepositoryMetadata metadata = new ArchivaRepositoryMetadata();
465 if ( metadataFile.exists() )
469 metadata = MavenMetadataReader.read( metadataFile );
471 catch ( XMLException e )
473 throw new RepositoryMetadataException( e.getMessage(), e );
479 private File createPom( File targetPath, String filename, FileMetadata fileMetadata, String groupId,
480 String artifactId, String version, String packaging )
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 void fixChecksums( File file )
507 ChecksummedFile checksum = new ChecksummedFile( file );
508 checksum.fixChecksums( algorithms );
511 private void queueRepositoryTask( String repositoryId, File localFile )
513 RepositoryTask task = new RepositoryTask();
514 task.setRepositoryId( repositoryId );
515 task.setResourceFile( localFile );
516 task.setUpdateRelatedArtifacts( true );
517 task.setScanAll( false );
521 scheduler.queueTask( task );
523 catch ( TaskQueueException e )
525 log.error( "Unable to queue repository task to execute consumers on resource file ['" + localFile.getName()
530 private void copyFile( File sourceFile, File targetPath, String targetFilename, boolean fixChecksums )
533 FileOutputStream out = new FileOutputStream( new File( targetPath, targetFilename ) );
534 FileInputStream input = new FileInputStream( sourceFile );
538 IOUtils.copy( input, out );
548 fixChecksums( new File( targetPath, targetFilename ) );
553 * Update artifact level metadata. If it does not exist, create the metadata and fix checksums if necessary.
555 private void updateProjectMetadata( String targetPath, Date lastUpdatedTimestamp, String timestamp, int buildNumber,
556 boolean fixChecksums, FileMetadata fileMetadata, String groupId,
557 String artifactId, String version, String packaging )
558 throws RepositoryMetadataException
560 List<String> availableVersions = new ArrayList<String>();
561 String latestVersion = version;
563 File projectDir = new File( targetPath ).getParentFile();
564 File projectMetadataFile = new File( projectDir, MetadataTools.MAVEN_METADATA );
566 ArchivaRepositoryMetadata projectMetadata = getMetadata( projectMetadataFile );
568 if ( projectMetadataFile.exists() )
570 availableVersions = projectMetadata.getAvailableVersions();
572 Collections.sort( availableVersions, VersionComparator.getInstance() );
574 if ( !availableVersions.contains( version ) )
576 availableVersions.add( version );
579 latestVersion = availableVersions.get( availableVersions.size() - 1 );
583 availableVersions.add( version );
585 projectMetadata.setGroupId( groupId );
586 projectMetadata.setArtifactId( artifactId );
589 if ( projectMetadata.getGroupId() == null )
591 projectMetadata.setGroupId( groupId );
594 if ( projectMetadata.getArtifactId() == null )
596 projectMetadata.setArtifactId( artifactId );
599 projectMetadata.setLatestVersion( latestVersion );
600 projectMetadata.setLastUpdatedTimestamp( lastUpdatedTimestamp );
601 projectMetadata.setAvailableVersions( availableVersions );
603 if ( !VersionUtil.isSnapshot( version ) )
605 projectMetadata.setReleasedVersion( latestVersion );
608 RepositoryMetadataWriter.write( projectMetadata, projectMetadataFile );
612 fixChecksums( projectMetadataFile );
617 * Update version level metadata for snapshot artifacts. If it does not exist, create the metadata and fix checksums
620 private void updateVersionMetadata( ArchivaRepositoryMetadata metadata, File metadataFile,
621 Date lastUpdatedTimestamp, String timestamp, int buildNumber,
622 boolean fixChecksums, FileMetadata fileMetadata, String groupId,
623 String artifactId, String version, String packaging )
624 throws RepositoryMetadataException
626 if ( !metadataFile.exists() )
628 metadata.setGroupId( groupId );
629 metadata.setArtifactId( artifactId );
630 metadata.setVersion( version );
633 if ( metadata.getSnapshotVersion() == null )
635 metadata.setSnapshotVersion( new SnapshotVersion() );
638 metadata.getSnapshotVersion().setBuildNumber( buildNumber );
639 metadata.getSnapshotVersion().setTimestamp( timestamp );
640 metadata.setLastUpdatedTimestamp( lastUpdatedTimestamp );
642 RepositoryMetadataWriter.write( metadata, metadataFile );
646 fixChecksums( metadataFile );