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;
84 import java.util.concurrent.CopyOnWriteArrayList;
87 * @author Olivier Lamy
89 @Service( "fileUploadService#rest" )
90 public class DefaultFileUploadService
91 extends AbstractRestService
92 implements FileUploadService
94 private Logger log = LoggerFactory.getLogger( getClass() );
97 private HttpServletRequest httpServletRequest;
100 private ManagedRepositoryAdmin managedRepositoryAdmin;
103 private RepositoryContentFactory repositoryFactory;
106 private ArchivaAdministration archivaAdministration;
108 private ChecksumAlgorithm[] algorithms = new ChecksumAlgorithm[]{ ChecksumAlgorithm.SHA1, ChecksumAlgorithm.MD5 };
111 @Named( value = "archivaTaskScheduler#repository" )
112 private ArchivaTaskScheduler scheduler;
114 private String getStringValue( MultipartBody multipartBody, String attachmentId )
117 Attachment attachment = multipartBody.getAttachment( attachmentId );
118 return attachment == null ? "" : IOUtils.toString( attachment.getDataHandler().getInputStream() );
121 public FileMetadata post( MultipartBody multipartBody )
122 throws ArchivaRestServiceException
128 String classifier = getStringValue( multipartBody, "classifier" );
129 boolean pomFile = BooleanUtils.toBoolean( getStringValue( multipartBody, "pomFile" ) );
131 Attachment file = multipartBody.getAttachment( "files[]" );
133 //Content-Disposition: form-data; name="files[]"; filename="org.apache.karaf.features.command-2.2.2.jar"
134 String fileName = file.getContentDisposition().getParameter( "filename" );
136 File tmpFile = File.createTempFile( "upload-artifact", "tmp" );
137 tmpFile.deleteOnExit();
138 IOUtils.copy( file.getDataHandler().getInputStream(), new FileOutputStream( tmpFile ) );
139 FileMetadata fileMetadata = new FileMetadata( fileName, tmpFile.length(), "theurl" );
140 fileMetadata.setServerFileName( tmpFile.getPath() );
141 fileMetadata.setClassifier( classifier );
142 fileMetadata.setDeleteUrl( tmpFile.getName() );
143 fileMetadata.setPomFile( pomFile );
145 log.info( "uploading file: {}", fileMetadata );
147 List<FileMetadata> fileMetadatas = getSessionFilesList();
149 fileMetadatas.add( fileMetadata );
153 catch ( IOException e )
155 throw new ArchivaRestServiceException( e.getMessage(),
156 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode() );
162 * FIXME must be per session synchronized not globally
166 protected synchronized List<FileMetadata> getSessionFilesList()
168 List<FileMetadata> fileMetadatas =
169 (List<FileMetadata>) httpServletRequest.getSession().getAttribute( FILES_SESSION_KEY );
170 if ( fileMetadatas == null )
172 fileMetadatas = new CopyOnWriteArrayList<FileMetadata>();
173 httpServletRequest.getSession().setAttribute( FILES_SESSION_KEY, fileMetadatas );
175 return fileMetadatas;
178 public Boolean deleteFile( String fileName )
179 throws ArchivaRestServiceException
181 File file = new File( SystemUtils.getJavaIoTmpDir(), fileName );
182 log.debug( "delete file:{},exists:{}", file.getPath(), file.exists() );
183 boolean removed = getSessionFileMetadatas().remove(
184 new FileMetadata( SystemUtils.getJavaIoTmpDir().getPath() + "/" + fileName ) );
187 return file.delete();
189 return Boolean.FALSE;
192 public Boolean clearUploadedFiles()
193 throws ArchivaRestServiceException
195 List<FileMetadata> fileMetadatas = new ArrayList( getSessionFileMetadatas() );
196 for ( FileMetadata fileMetadata : fileMetadatas )
198 deleteFile( new File( fileMetadata.getServerFileName() ).getName() );
203 public List<FileMetadata> getSessionFileMetadatas()
204 throws ArchivaRestServiceException
206 List<FileMetadata> fileMetadatas =
207 (List<FileMetadata>) httpServletRequest.getSession().getAttribute( FILES_SESSION_KEY );
209 return fileMetadatas == null ? Collections.<FileMetadata>emptyList() : fileMetadatas;
212 public Boolean save( String repositoryId, final String groupId, final String artifactId, String version,
213 String packaging, final boolean generatePom )
214 throws ArchivaRestServiceException
216 List<FileMetadata> fileMetadatas = getSessionFilesList();
217 if ( fileMetadatas == null || fileMetadatas.isEmpty() )
219 return Boolean.FALSE;
221 // get from the session file with groupId/artifactId
223 Iterable<FileMetadata> filesToAdd = Iterables.filter( fileMetadatas, new Predicate<FileMetadata>()
225 public boolean apply( FileMetadata fileMetadata )
227 return fileMetadata != null && !fileMetadata.isPomFile();
230 Iterator<FileMetadata> iterator = filesToAdd.iterator();
231 boolean pomGenerated = false;
232 while ( iterator.hasNext() )
234 FileMetadata fileMetadata = iterator.next();
235 log.debug( "fileToAdd: {}", fileMetadata );
236 saveFile( repositoryId, fileMetadata, generatePom && !pomGenerated, groupId, artifactId, version,
239 deleteFile( fileMetadata.getServerFileName() );
242 filesToAdd = Iterables.filter( fileMetadatas, new Predicate<FileMetadata>()
244 public boolean apply( @Nullable FileMetadata fileMetadata )
246 return fileMetadata != null && fileMetadata.isPomFile();
250 iterator = filesToAdd.iterator();
251 while ( iterator.hasNext() )
253 FileMetadata fileMetadata = iterator.next();
254 log.debug( "fileToAdd: {}", fileMetadata );
255 savePomFile( repositoryId, fileMetadata, groupId, artifactId, version, packaging );
256 deleteFile( fileMetadata.getServerFileName() );
262 protected void savePomFile( String repositoryId, FileMetadata fileMetadata, String groupId, String artifactId,
263 String version, String packaging )
264 throws ArchivaRestServiceException
269 boolean fixChecksums =
270 !( archivaAdministration.getKnownContentConsumers().contains( "create-missing-checksums" ) );
272 ManagedRepository repoConfig = managedRepositoryAdmin.getManagedRepository( repositoryId );
274 ArtifactReference artifactReference = new ArtifactReference();
275 artifactReference.setArtifactId( artifactId );
276 artifactReference.setGroupId( groupId );
277 artifactReference.setVersion( version );
278 artifactReference.setClassifier( fileMetadata.getClassifier() );
279 artifactReference.setType( packaging );
281 ManagedRepositoryContent repository = repositoryFactory.getManagedRepositoryContent( repositoryId );
283 String artifactPath = repository.toPath( artifactReference );
285 int lastIndex = artifactPath.lastIndexOf( '/' );
287 String path = artifactPath.substring( 0, lastIndex );
288 File targetPath = new File( repoConfig.getLocation(), path );
290 String pomFilename = artifactPath.substring( lastIndex + 1 );
291 if ( StringUtils.isNotEmpty( fileMetadata.getClassifier() ) )
293 pomFilename = StringUtils.remove( pomFilename, "-" + fileMetadata.getClassifier() );
295 pomFilename = FilenameUtils.removeExtension( pomFilename ) + ".pom";
297 copyFile( new File( fileMetadata.getServerFileName() ), targetPath, pomFilename, fixChecksums );
298 triggerAuditEvent( repoConfig.getId(), path + "/" + pomFilename, AuditEvent.UPLOAD_FILE );
299 queueRepositoryTask( repoConfig.getId(), new File( targetPath, pomFilename ) );
301 catch ( IOException ie )
303 throw new ArchivaRestServiceException( "Error encountered while uploading pom file: " + ie.getMessage(),
304 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode() );
306 catch ( RepositoryException rep )
308 throw new ArchivaRestServiceException( "Repository exception: " + rep.getMessage(),
309 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode() );
311 catch ( RepositoryAdminException e )
313 throw new ArchivaRestServiceException( "RepositoryAdmin exception: " + e.getMessage(),
314 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode() );
318 protected void saveFile( String repositoryId, FileMetadata fileMetadata, boolean generatePom, String groupId,
319 String artifactId, String version, String packaging )
320 throws ArchivaRestServiceException
325 ManagedRepository repoConfig = managedRepositoryAdmin.getManagedRepository( repositoryId );
327 ArtifactReference artifactReference = new ArtifactReference();
328 artifactReference.setArtifactId( artifactId );
329 artifactReference.setGroupId( groupId );
330 artifactReference.setVersion( version );
331 artifactReference.setClassifier( fileMetadata.getClassifier() );
332 artifactReference.setType( packaging );
334 ManagedRepositoryContent repository = repositoryFactory.getManagedRepositoryContent( repositoryId );
336 String artifactPath = repository.toPath( artifactReference );
338 int lastIndex = artifactPath.lastIndexOf( '/' );
340 String path = artifactPath.substring( 0, lastIndex );
341 File targetPath = new File( repoConfig.getLocation(), path );
343 log.debug( "artifactPath: {} found targetPath: {}", artifactPath, targetPath );
345 Date lastUpdatedTimestamp = Calendar.getInstance().getTime();
346 int newBuildNumber = -1;
347 String timestamp = null;
349 File versionMetadataFile = new File( targetPath, MetadataTools.MAVEN_METADATA );
350 ArchivaRepositoryMetadata versionMetadata = getMetadata( versionMetadataFile );
352 if ( VersionUtil.isSnapshot( version ) )
354 TimeZone timezone = TimeZone.getTimeZone( "UTC" );
355 DateFormat fmt = new SimpleDateFormat( "yyyyMMdd.HHmmss" );
356 fmt.setTimeZone( timezone );
357 timestamp = fmt.format( lastUpdatedTimestamp );
358 if ( versionMetadata.getSnapshotVersion() != null )
360 newBuildNumber = versionMetadata.getSnapshotVersion().getBuildNumber() + 1;
368 if ( !targetPath.exists() )
373 String filename = artifactPath.substring( lastIndex + 1 );
374 if ( VersionUtil.isSnapshot( version ) )
376 filename = filename.replaceAll( "SNAPSHOT", timestamp + "-" + newBuildNumber );
379 boolean fixChecksums =
380 !( archivaAdministration.getKnownContentConsumers().contains( "create-missing-checksums" ) );
384 File targetFile = new File( targetPath, filename );
385 if ( targetFile.exists() && !VersionUtil.isSnapshot( version ) && repoConfig.isBlockRedeployments() )
387 throw new ArchivaRestServiceException(
388 "Overwriting released artifacts in repository '" + repoConfig.getId() + "' is not allowed.",
389 Response.Status.BAD_REQUEST.getStatusCode() );
393 copyFile( new File( fileMetadata.getServerFileName() ), targetPath, filename, fixChecksums );
394 triggerAuditEvent( repository.getId(), path + "/" + filename, AuditEvent.UPLOAD_FILE );
395 queueRepositoryTask( repository.getId(), targetFile );
398 catch ( IOException ie )
400 throw new ArchivaRestServiceException(
401 "Overwriting released artifacts in repository '" + repoConfig.getId() + "' is not allowed.",
402 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode() );
407 String pomFilename = filename;
408 if ( StringUtils.isNotEmpty( fileMetadata.getClassifier() ) )
410 pomFilename = StringUtils.remove( pomFilename, "-" + fileMetadata.getClassifier() );
412 pomFilename = FilenameUtils.removeExtension( pomFilename ) + ".pom";
416 File generatedPomFile =
417 createPom( targetPath, pomFilename, fileMetadata, groupId, artifactId, version, packaging );
418 triggerAuditEvent( repoConfig.getId(), path + "/" + pomFilename, AuditEvent.UPLOAD_FILE );
421 fixChecksums( generatedPomFile );
423 queueRepositoryTask( repoConfig.getId(), generatedPomFile );
425 catch ( IOException ie )
427 throw new ArchivaRestServiceException(
428 "Error encountered while writing pom file: " + ie.getMessage(),
429 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode() );
433 // explicitly update only if metadata-updater consumer is not enabled!
434 if ( !archivaAdministration.getKnownContentConsumers().contains( "metadata-updater" ) )
436 updateProjectMetadata( targetPath.getAbsolutePath(), lastUpdatedTimestamp, timestamp, newBuildNumber,
437 fixChecksums, fileMetadata, groupId, artifactId, version, packaging );
439 if ( VersionUtil.isSnapshot( version ) )
441 updateVersionMetadata( versionMetadata, versionMetadataFile, lastUpdatedTimestamp, timestamp,
442 newBuildNumber, fixChecksums, fileMetadata, groupId, artifactId, version,
447 catch ( RepositoryNotFoundException re )
449 throw new ArchivaRestServiceException( "Target repository cannot be found: " + re.getMessage(),
450 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode() );
452 catch ( RepositoryException rep )
454 throw new ArchivaRestServiceException( "Repository exception: " + rep.getMessage(),
455 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode() );
457 catch ( RepositoryAdminException e )
459 throw new ArchivaRestServiceException( "RepositoryAdmin exception: " + e.getMessage(),
460 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode() );
464 private ArchivaRepositoryMetadata getMetadata( File metadataFile )
465 throws RepositoryMetadataException
467 ArchivaRepositoryMetadata metadata = new ArchivaRepositoryMetadata();
468 if ( metadataFile.exists() )
472 metadata = MavenMetadataReader.read( metadataFile );
474 catch ( XMLException e )
476 throw new RepositoryMetadataException( e.getMessage(), e );
482 private File createPom( File targetPath, String filename, FileMetadata fileMetadata, String groupId,
483 String artifactId, String version, String packaging )
486 Model projectModel = new Model();
487 projectModel.setModelVersion( "4.0.0" );
488 projectModel.setGroupId( groupId );
489 projectModel.setArtifactId( artifactId );
490 projectModel.setVersion( version );
491 projectModel.setPackaging( packaging );
493 File pomFile = new File( targetPath, filename );
494 MavenXpp3Writer writer = new MavenXpp3Writer();
495 FileWriter w = new FileWriter( pomFile );
498 writer.write( w, projectModel );
508 private void fixChecksums( File file )
510 ChecksummedFile checksum = new ChecksummedFile( file );
511 checksum.fixChecksums( algorithms );
514 private void queueRepositoryTask( String repositoryId, File localFile )
516 RepositoryTask task = new RepositoryTask();
517 task.setRepositoryId( repositoryId );
518 task.setResourceFile( localFile );
519 task.setUpdateRelatedArtifacts( true );
520 task.setScanAll( false );
524 scheduler.queueTask( task );
526 catch ( TaskQueueException e )
528 log.error( "Unable to queue repository task to execute consumers on resource file ['" + localFile.getName()
533 private void copyFile( File sourceFile, File targetPath, String targetFilename, boolean fixChecksums )
536 FileOutputStream out = new FileOutputStream( new File( targetPath, targetFilename ) );
537 FileInputStream input = new FileInputStream( sourceFile );
541 IOUtils.copy( input, out );
551 fixChecksums( new File( targetPath, targetFilename ) );
556 * Update artifact level metadata. If it does not exist, create the metadata and fix checksums if necessary.
558 private void updateProjectMetadata( String targetPath, Date lastUpdatedTimestamp, String timestamp, int buildNumber,
559 boolean fixChecksums, FileMetadata fileMetadata, String groupId,
560 String artifactId, String version, String packaging )
561 throws RepositoryMetadataException
563 List<String> availableVersions = new ArrayList<String>();
564 String latestVersion = version;
566 File projectDir = new File( targetPath ).getParentFile();
567 File projectMetadataFile = new File( projectDir, MetadataTools.MAVEN_METADATA );
569 ArchivaRepositoryMetadata projectMetadata = getMetadata( projectMetadataFile );
571 if ( projectMetadataFile.exists() )
573 availableVersions = projectMetadata.getAvailableVersions();
575 Collections.sort( availableVersions, VersionComparator.getInstance() );
577 if ( !availableVersions.contains( version ) )
579 availableVersions.add( version );
582 latestVersion = availableVersions.get( availableVersions.size() - 1 );
586 availableVersions.add( version );
588 projectMetadata.setGroupId( groupId );
589 projectMetadata.setArtifactId( artifactId );
592 if ( projectMetadata.getGroupId() == null )
594 projectMetadata.setGroupId( groupId );
597 if ( projectMetadata.getArtifactId() == null )
599 projectMetadata.setArtifactId( artifactId );
602 projectMetadata.setLatestVersion( latestVersion );
603 projectMetadata.setLastUpdatedTimestamp( lastUpdatedTimestamp );
604 projectMetadata.setAvailableVersions( availableVersions );
606 if ( !VersionUtil.isSnapshot( version ) )
608 projectMetadata.setReleasedVersion( latestVersion );
611 RepositoryMetadataWriter.write( projectMetadata, projectMetadataFile );
615 fixChecksums( projectMetadataFile );
620 * Update version level metadata for snapshot artifacts. If it does not exist, create the metadata and fix checksums
623 private void updateVersionMetadata( ArchivaRepositoryMetadata metadata, File metadataFile,
624 Date lastUpdatedTimestamp, String timestamp, int buildNumber,
625 boolean fixChecksums, FileMetadata fileMetadata, String groupId,
626 String artifactId, String version, String packaging )
627 throws RepositoryMetadataException
629 if ( !metadataFile.exists() )
631 metadata.setGroupId( groupId );
632 metadata.setArtifactId( artifactId );
633 metadata.setVersion( version );
636 if ( metadata.getSnapshotVersion() == null )
638 metadata.setSnapshotVersion( new SnapshotVersion() );
641 metadata.getSnapshotVersion().setBuildNumber( buildNumber );
642 metadata.getSnapshotVersion().setTimestamp( timestamp );
643 metadata.setLastUpdatedTimestamp( lastUpdatedTimestamp );
645 RepositoryMetadataWriter.write( metadata, metadataFile );
649 fixChecksums( metadataFile );