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.setClassifier( classifier );
148 fileMetadata.setDeleteUrl( tmpFile.getName() );
149 fileMetadata.setPomFile( pomFile );
151 log.info( "uploading file:{}", fileMetadata );
153 List<FileMetadata> fileMetadatas =
154 (List<FileMetadata>) httpServletRequest.getSession().getAttribute( FILES_SESSION_KEY );
156 if ( fileMetadatas == null )
158 fileMetadatas = new ArrayList<FileMetadata>( 1 );
160 fileMetadatas.add( fileMetadata );
161 httpServletRequest.getSession().setAttribute( FILES_SESSION_KEY, fileMetadatas );
164 catch ( IOException e )
166 throw new ArchivaRestServiceException( e.getMessage(),
167 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode() );
172 public Boolean deleteFile( String fileName )
173 throws ArchivaRestServiceException
175 File file = new File( SystemUtils.getJavaIoTmpDir(), fileName );
176 log.debug( "delete file:{},exists:{}", file.getPath(), file.exists() );
177 boolean removed = getSessionFileMetadatas().remove( new FileMetadata( fileName ) );
180 return file.delete();
182 return Boolean.FALSE;
185 public Boolean clearUploadedFiles()
186 throws ArchivaRestServiceException
188 List<FileMetadata> fileMetadatas = new ArrayList( getSessionFileMetadatas() );
189 for ( FileMetadata fileMetadata : fileMetadatas )
191 deleteFile( fileMetadata.getServerFileName() );
196 public List<FileMetadata> getSessionFileMetadatas()
197 throws ArchivaRestServiceException
199 List<FileMetadata> fileMetadatas =
200 (List<FileMetadata>) httpServletRequest.getSession().getAttribute( FILES_SESSION_KEY );
202 return fileMetadatas == null ? Collections.<FileMetadata>emptyList() : fileMetadatas;
205 public Boolean save( String repositoryId, final String groupId, final String artifactId, String version,
206 String packaging, final boolean generatePom )
207 throws ArchivaRestServiceException
209 List<FileMetadata> fileMetadatas =
210 (List<FileMetadata>) httpServletRequest.getSession().getAttribute( FILES_SESSION_KEY );
211 if ( fileMetadatas == null || fileMetadatas.isEmpty() )
213 return Boolean.FALSE;
215 // get from the session file with groupId/artifactId
217 Iterable<FileMetadata> filesToAdd = Iterables.filter( fileMetadatas, new Predicate<FileMetadata>()
219 public boolean apply( FileMetadata fileMetadata )
221 return fileMetadata != null && !fileMetadata.isPomFile();
224 Iterator<FileMetadata> iterator = filesToAdd.iterator();
225 boolean pomGenerated = false;
226 while ( iterator.hasNext() )
228 FileMetadata fileMetadata = iterator.next();
229 log.debug( "fileToAdd: {}", fileMetadata );
230 saveFile( repositoryId, fileMetadata, generatePom && !pomGenerated, groupId, artifactId, version,
235 filesToAdd = Iterables.filter( fileMetadatas, new Predicate<FileMetadata>()
237 public boolean apply( @Nullable FileMetadata fileMetadata )
239 return fileMetadata != null && fileMetadata.isPomFile();
243 iterator = filesToAdd.iterator();
244 while ( iterator.hasNext() )
246 FileMetadata fileMetadata = iterator.next();
247 log.debug( "fileToAdd: {}", fileMetadata );
248 savePomFile( repositoryId, fileMetadata, groupId, artifactId, version, packaging );
254 protected void savePomFile( String repositoryId, FileMetadata fileMetadata, String groupId, String artifactId,
255 String version, String packaging )
256 throws ArchivaRestServiceException
261 boolean fixChecksums =
262 !( archivaAdministration.getKnownContentConsumers().contains( "create-missing-checksums" ) );
264 ManagedRepository repoConfig = managedRepositoryAdmin.getManagedRepository( repositoryId );
266 ArtifactReference artifactReference = new ArtifactReference();
267 artifactReference.setArtifactId( artifactId );
268 artifactReference.setGroupId( groupId );
269 artifactReference.setVersion( version );
270 artifactReference.setClassifier( fileMetadata.getClassifier() );
271 artifactReference.setType( packaging );
273 ManagedRepositoryContent repository = repositoryFactory.getManagedRepositoryContent( repositoryId );
275 String artifactPath = repository.toPath( artifactReference );
277 int lastIndex = artifactPath.lastIndexOf( '/' );
279 String path = artifactPath.substring( 0, lastIndex );
280 File targetPath = new File( repoConfig.getLocation(), path );
282 String pomFilename = artifactPath.substring( lastIndex + 1 );
283 if ( StringUtils.isNotEmpty( fileMetadata.getClassifier() ) )
285 pomFilename = StringUtils.remove( pomFilename, "-" + fileMetadata.getClassifier() );
287 pomFilename = FilenameUtils.removeExtension( pomFilename ) + ".pom";
289 copyFile( new File( fileMetadata.getServerFileName() ), targetPath, pomFilename, fixChecksums );
290 triggerAuditEvent( repoConfig.getId(), path + "/" + pomFilename, AuditEvent.UPLOAD_FILE );
291 queueRepositoryTask( repoConfig.getId(), new File( targetPath, pomFilename ) );
293 catch ( IOException ie )
295 throw new ArchivaRestServiceException( "Error encountered while uploading pom file: " + ie.getMessage(),
296 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode() );
298 catch ( RepositoryException rep )
300 throw new ArchivaRestServiceException( "Repository exception: " + rep.getMessage(),
301 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode() );
303 catch ( RepositoryAdminException e )
305 throw new ArchivaRestServiceException( "RepositoryAdmin exception: " + e.getMessage(),
306 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode() );
310 protected void saveFile( String repositoryId, FileMetadata fileMetadata, boolean generatePom, String groupId,
311 String artifactId, String version, String packaging )
312 throws ArchivaRestServiceException
317 ManagedRepository repoConfig = managedRepositoryAdmin.getManagedRepository( repositoryId );
319 ArtifactReference artifactReference = new ArtifactReference();
320 artifactReference.setArtifactId( artifactId );
321 artifactReference.setGroupId( groupId );
322 artifactReference.setVersion( version );
323 artifactReference.setClassifier( fileMetadata.getClassifier() );
324 artifactReference.setType( packaging );
326 ManagedRepositoryContent repository = repositoryFactory.getManagedRepositoryContent( repositoryId );
328 String artifactPath = repository.toPath( artifactReference );
330 int lastIndex = artifactPath.lastIndexOf( '/' );
332 String path = artifactPath.substring( 0, lastIndex );
333 File targetPath = new File( repoConfig.getLocation(), path );
335 log.debug( "artifactPath: {} found targetPath: {}", artifactPath, targetPath );
337 Date lastUpdatedTimestamp = Calendar.getInstance().getTime();
338 int newBuildNumber = -1;
339 String timestamp = null;
341 File versionMetadataFile = new File( targetPath, MetadataTools.MAVEN_METADATA );
342 ArchivaRepositoryMetadata versionMetadata = getMetadata( versionMetadataFile );
344 if ( VersionUtil.isSnapshot( version ) )
346 TimeZone timezone = TimeZone.getTimeZone( "UTC" );
347 DateFormat fmt = new SimpleDateFormat( "yyyyMMdd.HHmmss" );
348 fmt.setTimeZone( timezone );
349 timestamp = fmt.format( lastUpdatedTimestamp );
350 if ( versionMetadata.getSnapshotVersion() != null )
352 newBuildNumber = versionMetadata.getSnapshotVersion().getBuildNumber() + 1;
360 if ( !targetPath.exists() )
365 String filename = artifactPath.substring( lastIndex + 1 );
366 if ( VersionUtil.isSnapshot( version ) )
368 filename = filename.replaceAll( "SNAPSHOT", timestamp + "-" + newBuildNumber );
371 boolean fixChecksums =
372 !( archivaAdministration.getKnownContentConsumers().contains( "create-missing-checksums" ) );
376 File targetFile = new File( targetPath, filename );
377 if ( targetFile.exists() && !VersionUtil.isSnapshot( version ) && repoConfig.isBlockRedeployments() )
379 throw new ArchivaRestServiceException(
380 "Overwriting released artifacts in repository '" + repoConfig.getId() + "' is not allowed.",
381 Response.Status.BAD_REQUEST.getStatusCode() );
385 copyFile( new File( fileMetadata.getServerFileName() ), targetPath, filename, fixChecksums );
386 triggerAuditEvent( repository.getId(), path + "/" + filename, AuditEvent.UPLOAD_FILE );
387 queueRepositoryTask( repository.getId(), targetFile );
390 catch ( IOException ie )
392 throw new ArchivaRestServiceException(
393 "Overwriting released artifacts in repository '" + repoConfig.getId() + "' is not allowed.",
394 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode() );
399 String pomFilename = filename;
400 if ( StringUtils.isNotEmpty( fileMetadata.getClassifier() ) )
402 pomFilename = StringUtils.remove( pomFilename, "-" + fileMetadata.getClassifier() );
404 pomFilename = FilenameUtils.removeExtension( pomFilename ) + ".pom";
408 File generatedPomFile =
409 createPom( targetPath, pomFilename, fileMetadata, groupId, artifactId, version, packaging );
410 triggerAuditEvent( repoConfig.getId(), path + "/" + pomFilename, AuditEvent.UPLOAD_FILE );
413 fixChecksums( generatedPomFile );
415 queueRepositoryTask( repoConfig.getId(), generatedPomFile );
417 catch ( IOException ie )
419 throw new ArchivaRestServiceException(
420 "Error encountered while writing pom file: " + ie.getMessage(),
421 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode() );
425 // explicitly update only if metadata-updater consumer is not enabled!
426 if ( !archivaAdministration.getKnownContentConsumers().contains( "metadata-updater" ) )
428 updateProjectMetadata( targetPath.getAbsolutePath(), lastUpdatedTimestamp, timestamp, newBuildNumber,
429 fixChecksums, fileMetadata, groupId, artifactId, version, packaging );
431 if ( VersionUtil.isSnapshot( version ) )
433 updateVersionMetadata( versionMetadata, versionMetadataFile, lastUpdatedTimestamp, timestamp,
434 newBuildNumber, fixChecksums, fileMetadata, groupId, artifactId, version,
439 catch ( RepositoryNotFoundException re )
441 throw new ArchivaRestServiceException( "Target repository cannot be found: " + re.getMessage(),
442 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode() );
444 catch ( RepositoryException rep )
446 throw new ArchivaRestServiceException( "Repository exception: " + rep.getMessage(),
447 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode() );
449 catch ( RepositoryAdminException e )
451 throw new ArchivaRestServiceException( "RepositoryAdmin exception: " + e.getMessage(),
452 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode() );
456 private ArchivaRepositoryMetadata getMetadata( File metadataFile )
457 throws RepositoryMetadataException
459 ArchivaRepositoryMetadata metadata = new ArchivaRepositoryMetadata();
460 if ( metadataFile.exists() )
464 metadata = MavenMetadataReader.read( metadataFile );
466 catch ( XMLException e )
468 throw new RepositoryMetadataException( e.getMessage(), e );
474 private File createPom( File targetPath, String filename, FileMetadata fileMetadata, String groupId,
475 String artifactId, String version, String packaging )
478 Model projectModel = new Model();
479 projectModel.setModelVersion( "4.0.0" );
480 projectModel.setGroupId( groupId );
481 projectModel.setArtifactId( artifactId );
482 projectModel.setVersion( version );
483 projectModel.setPackaging( packaging );
485 File pomFile = new File( targetPath, filename );
486 MavenXpp3Writer writer = new MavenXpp3Writer();
487 FileWriter w = new FileWriter( pomFile );
490 writer.write( w, projectModel );
500 private void fixChecksums( File file )
502 ChecksummedFile checksum = new ChecksummedFile( file );
503 checksum.fixChecksums( algorithms );
506 private void queueRepositoryTask( String repositoryId, File localFile )
508 RepositoryTask task = new RepositoryTask();
509 task.setRepositoryId( repositoryId );
510 task.setResourceFile( localFile );
511 task.setUpdateRelatedArtifacts( true );
512 task.setScanAll( false );
516 scheduler.queueTask( task );
518 catch ( TaskQueueException e )
520 log.error( "Unable to queue repository task to execute consumers on resource file ['" + localFile.getName()
525 private void copyFile( File sourceFile, File targetPath, String targetFilename, boolean fixChecksums )
528 FileOutputStream out = new FileOutputStream( new File( targetPath, targetFilename ) );
529 FileInputStream input = new FileInputStream( sourceFile );
533 IOUtils.copy( input, out );
543 fixChecksums( new File( targetPath, targetFilename ) );
548 * Update artifact level metadata. If it does not exist, create the metadata and fix checksums if necessary.
550 private void updateProjectMetadata( String targetPath, Date lastUpdatedTimestamp, String timestamp, int buildNumber,
551 boolean fixChecksums, FileMetadata fileMetadata, String groupId,
552 String artifactId, String version, String packaging )
553 throws RepositoryMetadataException
555 List<String> availableVersions = new ArrayList<String>();
556 String latestVersion = version;
558 File projectDir = new File( targetPath ).getParentFile();
559 File projectMetadataFile = new File( projectDir, MetadataTools.MAVEN_METADATA );
561 ArchivaRepositoryMetadata projectMetadata = getMetadata( projectMetadataFile );
563 if ( projectMetadataFile.exists() )
565 availableVersions = projectMetadata.getAvailableVersions();
567 Collections.sort( availableVersions, VersionComparator.getInstance() );
569 if ( !availableVersions.contains( version ) )
571 availableVersions.add( version );
574 latestVersion = availableVersions.get( availableVersions.size() - 1 );
578 availableVersions.add( version );
580 projectMetadata.setGroupId( groupId );
581 projectMetadata.setArtifactId( artifactId );
584 if ( projectMetadata.getGroupId() == null )
586 projectMetadata.setGroupId( groupId );
589 if ( projectMetadata.getArtifactId() == null )
591 projectMetadata.setArtifactId( artifactId );
594 projectMetadata.setLatestVersion( latestVersion );
595 projectMetadata.setLastUpdatedTimestamp( lastUpdatedTimestamp );
596 projectMetadata.setAvailableVersions( availableVersions );
598 if ( !VersionUtil.isSnapshot( version ) )
600 projectMetadata.setReleasedVersion( latestVersion );
603 RepositoryMetadataWriter.write( projectMetadata, projectMetadataFile );
607 fixChecksums( projectMetadataFile );
612 * Update version level metadata for snapshot artifacts. If it does not exist, create the metadata and fix checksums
615 private void updateVersionMetadata( ArchivaRepositoryMetadata metadata, File metadataFile,
616 Date lastUpdatedTimestamp, String timestamp, int buildNumber,
617 boolean fixChecksums, FileMetadata fileMetadata, String groupId,
618 String artifactId, String version, String packaging )
619 throws RepositoryMetadataException
621 if ( !metadataFile.exists() )
623 metadata.setGroupId( groupId );
624 metadata.setArtifactId( artifactId );
625 metadata.setVersion( version );
628 if ( metadata.getSnapshotVersion() == null )
630 metadata.setSnapshotVersion( new SnapshotVersion() );
633 metadata.getSnapshotVersion().setBuildNumber( buildNumber );
634 metadata.getSnapshotVersion().setTimestamp( timestamp );
635 metadata.setLastUpdatedTimestamp( lastUpdatedTimestamp );
637 RepositoryMetadataWriter.write( metadata, metadataFile );
641 fixChecksums( metadataFile );