1 package org.apache.archiva.web.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.redback.components.taskqueue.TaskQueueException;
37 import org.apache.archiva.repository.ManagedRepositoryContent;
38 import org.apache.archiva.repository.RepositoryContentFactory;
39 import org.apache.archiva.repository.RepositoryException;
40 import org.apache.archiva.repository.RepositoryNotFoundException;
41 import org.apache.archiva.repository.metadata.MetadataTools;
42 import org.apache.archiva.repository.metadata.RepositoryMetadataException;
43 import org.apache.archiva.repository.metadata.RepositoryMetadataWriter;
44 import org.apache.archiva.rest.api.services.ArchivaRestServiceException;
45 import org.apache.archiva.rest.services.AbstractRestService;
46 import org.apache.archiva.scheduler.ArchivaTaskScheduler;
47 import org.apache.archiva.scheduler.repository.model.RepositoryTask;
48 import org.apache.archiva.web.model.FileMetadata;
49 import org.apache.archiva.xml.XMLException;
50 import org.apache.commons.io.FilenameUtils;
51 import org.apache.commons.io.IOUtils;
52 import org.apache.commons.lang.BooleanUtils;
53 import org.apache.commons.lang.StringUtils;
54 import org.apache.commons.lang.SystemUtils;
55 import org.apache.cxf.jaxrs.ext.multipart.Attachment;
56 import org.apache.cxf.jaxrs.ext.multipart.MultipartBody;
57 import org.apache.maven.model.Model;
58 import org.apache.maven.model.io.xpp3.MavenXpp3Writer;
59 import org.slf4j.Logger;
60 import org.slf4j.LoggerFactory;
61 import org.springframework.stereotype.Service;
64 import javax.inject.Inject;
65 import javax.inject.Named;
66 import javax.servlet.http.HttpServletRequest;
67 import javax.ws.rs.core.Context;
68 import javax.ws.rs.core.Response;
70 import java.io.FileInputStream;
71 import java.io.FileOutputStream;
72 import java.io.FileWriter;
73 import java.io.IOException;
74 import java.text.DateFormat;
75 import java.text.SimpleDateFormat;
76 import java.util.ArrayList;
77 import java.util.Calendar;
78 import java.util.Collections;
79 import java.util.Date;
80 import java.util.Iterator;
81 import java.util.List;
82 import java.util.TimeZone;
83 import java.util.concurrent.CopyOnWriteArrayList;
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
127 String classifier = getStringValue( multipartBody, "classifier" );
128 // skygo: http header form pomFile was once sending 1 for true and void for false
129 // leading to permanent false value for pomFile if using toBoolean(); use , "1", ""
130 boolean pomFile = BooleanUtils.toBoolean( getStringValue( multipartBody, "pomFile" ) );
132 Attachment file = multipartBody.getAttachment( "files[]" );
134 //Content-Disposition: form-data; name="files[]"; filename="org.apache.karaf.features.command-2.2.2.jar"
135 String fileName = file.getContentDisposition().getParameter( "filename" );
137 File tmpFile = File.createTempFile( "upload-artifact", "tmp" );
138 tmpFile.deleteOnExit();
139 IOUtils.copy( file.getDataHandler().getInputStream(), new FileOutputStream( tmpFile ) );
140 FileMetadata fileMetadata = new FileMetadata( fileName, tmpFile.length(), "theurl" );
141 fileMetadata.setServerFileName( tmpFile.getPath() );
142 fileMetadata.setClassifier( classifier );
143 fileMetadata.setDeleteUrl( tmpFile.getName() );
144 fileMetadata.setPomFile( pomFile );
146 log.info( "uploading file: {}", fileMetadata );
148 List<FileMetadata> fileMetadatas = getSessionFilesList();
150 fileMetadatas.add( fileMetadata );
154 catch ( IOException e )
156 throw new ArchivaRestServiceException( e.getMessage(),
157 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
163 * FIXME must be per session synchronized not globally
167 protected synchronized List<FileMetadata> getSessionFilesList()
169 List<FileMetadata> fileMetadatas =
170 (List<FileMetadata>) httpServletRequest.getSession().getAttribute( FILES_SESSION_KEY );
171 if ( fileMetadatas == null )
173 fileMetadatas = new CopyOnWriteArrayList<FileMetadata>();
174 httpServletRequest.getSession().setAttribute( FILES_SESSION_KEY, fileMetadatas );
176 return fileMetadatas;
179 public Boolean deleteFile( String fileName )
180 throws ArchivaRestServiceException
182 File file = new File( SystemUtils.getJavaIoTmpDir(), fileName );
183 log.debug( "delete file:{},exists:{}", file.getPath(), file.exists() );
184 boolean removed = getSessionFileMetadatas().remove(
185 new FileMetadata( SystemUtils.getJavaIoTmpDir().getPath() + "/" + fileName ) );
188 return file.delete();
190 return Boolean.FALSE;
193 public Boolean clearUploadedFiles()
194 throws ArchivaRestServiceException
196 List<FileMetadata> fileMetadatas = new ArrayList( getSessionFileMetadatas() );
197 for ( FileMetadata fileMetadata : fileMetadatas )
199 deleteFile( new File( fileMetadata.getServerFileName() ).getName() );
204 public List<FileMetadata> getSessionFileMetadatas()
205 throws ArchivaRestServiceException
207 List<FileMetadata> fileMetadatas =
208 (List<FileMetadata>) httpServletRequest.getSession().getAttribute( FILES_SESSION_KEY );
210 return fileMetadatas == null ? Collections.<FileMetadata>emptyList() : fileMetadatas;
213 public Boolean save( String repositoryId, final String groupId, final String artifactId, String version,
214 String packaging, final boolean generatePom )
215 throws ArchivaRestServiceException
217 List<FileMetadata> fileMetadatas = getSessionFilesList();
218 if ( fileMetadatas == null || fileMetadatas.isEmpty() )
220 return Boolean.FALSE;
222 // get from the session file with groupId/artifactId
224 Iterable<FileMetadata> filesToAdd = Iterables.filter( fileMetadatas, new Predicate<FileMetadata>()
226 public boolean apply( FileMetadata fileMetadata )
228 return fileMetadata != null && !fileMetadata.isPomFile();
231 Iterator<FileMetadata> iterator = filesToAdd.iterator();
232 boolean pomGenerated = false;
233 while ( iterator.hasNext() )
235 FileMetadata fileMetadata = iterator.next();
236 log.debug( "fileToAdd: {}", fileMetadata );
237 saveFile( repositoryId, fileMetadata, generatePom && !pomGenerated, groupId, artifactId, version,
240 deleteFile( fileMetadata.getServerFileName() );
243 filesToAdd = Iterables.filter( fileMetadatas, new Predicate<FileMetadata>()
245 public boolean apply( FileMetadata fileMetadata )
247 return fileMetadata != null && fileMetadata.isPomFile();
251 iterator = filesToAdd.iterator();
252 while ( iterator.hasNext() )
254 FileMetadata fileMetadata = iterator.next();
255 log.debug( "fileToAdd: {}", fileMetadata );
256 savePomFile( repositoryId, fileMetadata, groupId, artifactId, version, packaging );
257 deleteFile( fileMetadata.getServerFileName() );
263 protected void savePomFile( String repositoryId, FileMetadata fileMetadata, String groupId, String artifactId,
264 String version, String packaging )
265 throws ArchivaRestServiceException
270 boolean fixChecksums =
271 !( archivaAdministration.getKnownContentConsumers().contains( "create-missing-checksums" ) );
273 ManagedRepository repoConfig = managedRepositoryAdmin.getManagedRepository( repositoryId );
275 ArtifactReference artifactReference = new ArtifactReference();
276 artifactReference.setArtifactId( artifactId );
277 artifactReference.setGroupId( groupId );
278 artifactReference.setVersion( version );
279 artifactReference.setClassifier( fileMetadata.getClassifier() );
280 artifactReference.setType( packaging );
282 ManagedRepositoryContent repository = repositoryFactory.getManagedRepositoryContent( repositoryId );
284 String artifactPath = repository.toPath( artifactReference );
286 int lastIndex = artifactPath.lastIndexOf( '/' );
288 String path = artifactPath.substring( 0, lastIndex );
289 File targetPath = new File( repoConfig.getLocation(), path );
291 String pomFilename = artifactPath.substring( lastIndex + 1 );
292 if ( StringUtils.isNotEmpty( fileMetadata.getClassifier() ) )
294 pomFilename = StringUtils.remove( pomFilename, "-" + fileMetadata.getClassifier() );
296 pomFilename = FilenameUtils.removeExtension( pomFilename ) + ".pom";
298 copyFile( new File( fileMetadata.getServerFileName() ), targetPath, pomFilename, fixChecksums );
299 triggerAuditEvent( repoConfig.getId(), path + "/" + pomFilename, AuditEvent.UPLOAD_FILE );
300 queueRepositoryTask( repoConfig.getId(), new File( targetPath, pomFilename ) );
302 catch ( IOException ie )
304 throw new ArchivaRestServiceException( "Error encountered while uploading pom file: " + ie.getMessage(),
305 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), ie );
307 catch ( RepositoryException rep )
309 throw new ArchivaRestServiceException( "Repository exception: " + rep.getMessage(),
310 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), rep );
312 catch ( RepositoryAdminException e )
314 throw new ArchivaRestServiceException( "RepositoryAdmin exception: " + e.getMessage(),
315 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
319 protected void saveFile( String repositoryId, FileMetadata fileMetadata, boolean generatePom, String groupId,
320 String artifactId, String version, String packaging )
321 throws ArchivaRestServiceException
326 ManagedRepository repoConfig = managedRepositoryAdmin.getManagedRepository( repositoryId );
328 ArtifactReference artifactReference = new ArtifactReference();
329 artifactReference.setArtifactId( artifactId );
330 artifactReference.setGroupId( groupId );
331 artifactReference.setVersion( version );
332 artifactReference.setClassifier( fileMetadata.getClassifier() );
333 artifactReference.setType( packaging );
335 ManagedRepositoryContent repository = repositoryFactory.getManagedRepositoryContent( repositoryId );
337 String artifactPath = repository.toPath( artifactReference );
339 int lastIndex = artifactPath.lastIndexOf( '/' );
341 String path = artifactPath.substring( 0, lastIndex );
342 File targetPath = new File( repoConfig.getLocation(), path );
344 log.debug( "artifactPath: {} found targetPath: {}", artifactPath, targetPath );
346 Date lastUpdatedTimestamp = Calendar.getInstance().getTime();
347 int newBuildNumber = -1;
348 String timestamp = null;
350 File versionMetadataFile = new File( targetPath, MetadataTools.MAVEN_METADATA );
351 ArchivaRepositoryMetadata versionMetadata = getMetadata( versionMetadataFile );
353 if ( VersionUtil.isSnapshot( version ) )
355 TimeZone timezone = TimeZone.getTimeZone( "UTC" );
356 DateFormat fmt = new SimpleDateFormat( "yyyyMMdd.HHmmss" );
357 fmt.setTimeZone( timezone );
358 timestamp = fmt.format( lastUpdatedTimestamp );
359 if ( versionMetadata.getSnapshotVersion() != null )
361 newBuildNumber = versionMetadata.getSnapshotVersion().getBuildNumber() + 1;
369 if ( !targetPath.exists() )
374 String filename = artifactPath.substring( lastIndex + 1 );
375 if ( VersionUtil.isSnapshot( version ) )
377 filename = filename.replaceAll( VersionUtil.SNAPSHOT, timestamp + "-" + newBuildNumber );
380 boolean fixChecksums =
381 !( archivaAdministration.getKnownContentConsumers().contains( "create-missing-checksums" ) );
385 File targetFile = new File( targetPath, filename );
386 if ( targetFile.exists() && !VersionUtil.isSnapshot( version ) && repoConfig.isBlockRedeployments() )
388 throw new ArchivaRestServiceException(
389 "Overwriting released artifacts in repository '" + repoConfig.getId() + "' is not allowed.",
390 Response.Status.BAD_REQUEST.getStatusCode(), null );
394 copyFile( new File( fileMetadata.getServerFileName() ), targetPath, filename, fixChecksums );
395 triggerAuditEvent( repository.getId(), path + "/" + filename, AuditEvent.UPLOAD_FILE );
396 queueRepositoryTask( repository.getId(), targetFile );
399 catch ( IOException ie )
401 throw new ArchivaRestServiceException(
402 "Overwriting released artifacts in repository '" + repoConfig.getId() + "' is not allowed.",
403 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), ie );
408 String pomFilename = filename;
409 if ( StringUtils.isNotEmpty( fileMetadata.getClassifier() ) )
411 pomFilename = StringUtils.remove( pomFilename, "-" + fileMetadata.getClassifier() );
413 pomFilename = FilenameUtils.removeExtension( pomFilename ) + ".pom";
417 File generatedPomFile =
418 createPom( targetPath, pomFilename, fileMetadata, groupId, artifactId, version, packaging );
419 triggerAuditEvent( repoConfig.getId(), path + "/" + pomFilename, AuditEvent.UPLOAD_FILE );
422 fixChecksums( generatedPomFile );
424 queueRepositoryTask( repoConfig.getId(), generatedPomFile );
426 catch ( IOException ie )
428 throw new ArchivaRestServiceException(
429 "Error encountered while writing pom file: " + ie.getMessage(),
430 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), ie );
434 // explicitly update only if metadata-updater consumer is not enabled!
435 if ( !archivaAdministration.getKnownContentConsumers().contains( "metadata-updater" ) )
437 updateProjectMetadata( targetPath.getAbsolutePath(), lastUpdatedTimestamp, timestamp, newBuildNumber,
438 fixChecksums, fileMetadata, groupId, artifactId, version, packaging );
440 if ( VersionUtil.isSnapshot( version ) )
442 updateVersionMetadata( versionMetadata, versionMetadataFile, lastUpdatedTimestamp, timestamp,
443 newBuildNumber, fixChecksums, fileMetadata, groupId, artifactId, version,
448 catch ( RepositoryNotFoundException re )
450 throw new ArchivaRestServiceException( "Target repository cannot be found: " + re.getMessage(),
451 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), re );
453 catch ( RepositoryException rep )
455 throw new ArchivaRestServiceException( "Repository exception: " + rep.getMessage(),
456 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), rep );
458 catch ( RepositoryAdminException e )
460 throw new ArchivaRestServiceException( "RepositoryAdmin exception: " + e.getMessage(),
461 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
465 private ArchivaRepositoryMetadata getMetadata( File metadataFile )
466 throws RepositoryMetadataException
468 ArchivaRepositoryMetadata metadata = new ArchivaRepositoryMetadata();
469 if ( metadataFile.exists() )
473 metadata = MavenMetadataReader.read( metadataFile );
475 catch ( XMLException e )
477 throw new RepositoryMetadataException( e.getMessage(), e );
483 private File createPom( File targetPath, String filename, FileMetadata fileMetadata, String groupId,
484 String artifactId, String version, String packaging )
487 Model projectModel = new Model();
488 projectModel.setModelVersion( "4.0.0" );
489 projectModel.setGroupId( groupId );
490 projectModel.setArtifactId( artifactId );
491 projectModel.setVersion( version );
492 projectModel.setPackaging( packaging );
494 File pomFile = new File( targetPath, filename );
495 MavenXpp3Writer writer = new MavenXpp3Writer();
496 FileWriter w = new FileWriter( pomFile );
499 writer.write( w, projectModel );
503 IOUtils.closeQuietly( w );
509 private void fixChecksums( File file )
511 ChecksummedFile checksum = new ChecksummedFile( file );
512 checksum.fixChecksums( algorithms );
515 private void queueRepositoryTask( String repositoryId, File localFile )
517 RepositoryTask task = new RepositoryTask();
518 task.setRepositoryId( repositoryId );
519 task.setResourceFile( localFile );
520 task.setUpdateRelatedArtifacts( true );
521 task.setScanAll( false );
525 scheduler.queueTask( task );
527 catch ( TaskQueueException e )
529 log.error( "Unable to queue repository task to execute consumers on resource file ['" + localFile.getName()
534 private void copyFile( File sourceFile, File targetPath, String targetFilename, boolean fixChecksums )
537 FileOutputStream out = new FileOutputStream( new File( targetPath, targetFilename ) );
538 FileInputStream input = new FileInputStream( sourceFile );
542 IOUtils.copy( input, out );
552 fixChecksums( new File( targetPath, targetFilename ) );
557 * Update artifact level metadata. If it does not exist, create the metadata and fix checksums if necessary.
559 private void updateProjectMetadata( String targetPath, Date lastUpdatedTimestamp, String timestamp, int buildNumber,
560 boolean fixChecksums, FileMetadata fileMetadata, String groupId,
561 String artifactId, String version, String packaging )
562 throws RepositoryMetadataException
564 List<String> availableVersions = new ArrayList<String>();
565 String latestVersion = version;
567 File projectDir = new File( targetPath ).getParentFile();
568 File projectMetadataFile = new File( projectDir, MetadataTools.MAVEN_METADATA );
570 ArchivaRepositoryMetadata projectMetadata = getMetadata( projectMetadataFile );
572 if ( projectMetadataFile.exists() )
574 availableVersions = projectMetadata.getAvailableVersions();
576 Collections.sort( availableVersions, VersionComparator.getInstance() );
578 if ( !availableVersions.contains( version ) )
580 availableVersions.add( version );
583 latestVersion = availableVersions.get( availableVersions.size() - 1 );
587 availableVersions.add( version );
589 projectMetadata.setGroupId( groupId );
590 projectMetadata.setArtifactId( artifactId );
593 if ( projectMetadata.getGroupId() == null )
595 projectMetadata.setGroupId( groupId );
598 if ( projectMetadata.getArtifactId() == null )
600 projectMetadata.setArtifactId( artifactId );
603 projectMetadata.setLatestVersion( latestVersion );
604 projectMetadata.setLastUpdatedTimestamp( lastUpdatedTimestamp );
605 projectMetadata.setAvailableVersions( availableVersions );
607 if ( !VersionUtil.isSnapshot( version ) )
609 projectMetadata.setReleasedVersion( latestVersion );
612 RepositoryMetadataWriter.write( projectMetadata, projectMetadataFile );
616 fixChecksums( projectMetadataFile );
621 * Update version level metadata for snapshot artifacts. If it does not exist, create the metadata and fix checksums
624 private void updateVersionMetadata( ArchivaRepositoryMetadata metadata, File metadataFile,
625 Date lastUpdatedTimestamp, String timestamp, int buildNumber,
626 boolean fixChecksums, FileMetadata fileMetadata, String groupId,
627 String artifactId, String version, String packaging )
628 throws RepositoryMetadataException
630 if ( !metadataFile.exists() )
632 metadata.setGroupId( groupId );
633 metadata.setArtifactId( artifactId );
634 metadata.setVersion( version );
637 if ( metadata.getSnapshotVersion() == null )
639 metadata.setSnapshotVersion( new SnapshotVersion() );
642 metadata.getSnapshotVersion().setBuildNumber( buildNumber );
643 metadata.getSnapshotVersion().setTimestamp( timestamp );
644 metadata.setLastUpdatedTimestamp( lastUpdatedTimestamp );
646 RepositoryMetadataWriter.write( metadata, metadataFile );
650 fixChecksums( metadataFile );