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 = 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, final boolean generatePom )
211 throws ArchivaRestServiceException
213 List<FileMetadata> fileMetadatas =
214 (List<FileMetadata>) httpServletRequest.getSession().getAttribute( FILES_SESSION_KEY );
215 if ( fileMetadatas == null || fileMetadatas.isEmpty() )
217 return Boolean.FALSE;
219 // get from the session file with groupId/artifactId
221 Iterable<FileMetadata> filesToAdd = Iterables.filter( fileMetadatas, new Predicate<FileMetadata>()
223 public boolean apply( FileMetadata fileMetadata )
225 if ( fileMetadata == null )
229 return StringUtils.equals( groupId, fileMetadata.getGroupId() ) && StringUtils.equals( artifactId,
230 fileMetadata.getArtifactId() )
231 && !fileMetadata.isPomFile();
234 Iterator<FileMetadata> iterator = filesToAdd.iterator();
235 boolean pomGenerated = false;
236 while ( iterator.hasNext() )
238 FileMetadata fileMetadata = iterator.next();
239 log.debug( "fileToAdd: {}", fileMetadata );
240 saveFile( repositoryId, fileMetadata, generatePom && !pomGenerated );
244 filesToAdd = Iterables.filter( fileMetadatas, new Predicate<FileMetadata>()
246 public boolean apply( @Nullable FileMetadata fileMetadata )
248 return fileMetadata.isPomFile();
252 iterator = filesToAdd.iterator();
253 while ( iterator.hasNext() )
255 FileMetadata fileMetadata = iterator.next();
256 log.debug( "fileToAdd: {}", fileMetadata );
257 savePomFile( repositoryId, fileMetadata );
263 protected void savePomFile( String repositoryId, FileMetadata fileMetadata )
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( fileMetadata.getArtifactId() );
276 artifactReference.setGroupId( fileMetadata.getGroupId() );
277 artifactReference.setVersion( fileMetadata.getVersion() );
278 artifactReference.setClassifier( fileMetadata.getClassifier() );
279 artifactReference.setType( fileMetadata.getPackaging() );
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 )
319 throws ArchivaRestServiceException
324 ManagedRepository repoConfig = managedRepositoryAdmin.getManagedRepository( repositoryId );
326 ArtifactReference artifactReference = new ArtifactReference();
327 artifactReference.setArtifactId( fileMetadata.getArtifactId() );
328 artifactReference.setGroupId( fileMetadata.getGroupId() );
329 artifactReference.setVersion( fileMetadata.getVersion() );
330 artifactReference.setClassifier( fileMetadata.getClassifier() );
331 artifactReference.setType( fileMetadata.getPackaging() );
333 ManagedRepositoryContent repository = repositoryFactory.getManagedRepositoryContent( repositoryId );
335 String artifactPath = repository.toPath( artifactReference );
337 int lastIndex = artifactPath.lastIndexOf( '/' );
339 String path = artifactPath.substring( 0, lastIndex );
340 File targetPath = new File( repoConfig.getLocation(), path );
342 log.debug( "artifactPath: {} found targetPath: {}", artifactPath, targetPath );
344 Date lastUpdatedTimestamp = Calendar.getInstance().getTime();
345 int newBuildNumber = -1;
346 String timestamp = null;
348 File versionMetadataFile = new File( targetPath, MetadataTools.MAVEN_METADATA );
349 ArchivaRepositoryMetadata versionMetadata = getMetadata( versionMetadataFile );
351 if ( VersionUtil.isSnapshot( fileMetadata.getVersion() ) )
353 TimeZone timezone = TimeZone.getTimeZone( "UTC" );
354 DateFormat fmt = new SimpleDateFormat( "yyyyMMdd.HHmmss" );
355 fmt.setTimeZone( timezone );
356 timestamp = fmt.format( lastUpdatedTimestamp );
357 if ( versionMetadata.getSnapshotVersion() != null )
359 newBuildNumber = versionMetadata.getSnapshotVersion().getBuildNumber() + 1;
367 if ( !targetPath.exists() )
372 String filename = artifactPath.substring( lastIndex + 1 );
373 if ( VersionUtil.isSnapshot( fileMetadata.getVersion() ) )
375 filename = filename.replaceAll( "SNAPSHOT", timestamp + "-" + newBuildNumber );
378 boolean fixChecksums =
379 !( archivaAdministration.getKnownContentConsumers().contains( "create-missing-checksums" ) );
383 File targetFile = new File( targetPath, filename );
384 if ( targetFile.exists() && !VersionUtil.isSnapshot( fileMetadata.getVersion() )
385 && 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 = createPom( targetPath, pomFilename, fileMetadata );
417 triggerAuditEvent( repoConfig.getId(), path + "/" + pomFilename, AuditEvent.UPLOAD_FILE );
420 fixChecksums( generatedPomFile );
422 queueRepositoryTask( repoConfig.getId(), generatedPomFile );
424 catch ( IOException ie )
426 throw new ArchivaRestServiceException(
427 "Error encountered while writing pom file: " + ie.getMessage(),
428 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode() );
432 // explicitly update only if metadata-updater consumer is not enabled!
433 if ( !archivaAdministration.getKnownContentConsumers().contains( "metadata-updater" ) )
435 updateProjectMetadata( targetPath.getAbsolutePath(), lastUpdatedTimestamp, timestamp, newBuildNumber,
436 fixChecksums, fileMetadata );
438 if ( VersionUtil.isSnapshot( fileMetadata.getVersion() ) )
440 updateVersionMetadata( versionMetadata, versionMetadataFile, lastUpdatedTimestamp, timestamp,
441 newBuildNumber, fixChecksums, fileMetadata );
445 catch ( RepositoryNotFoundException re )
447 throw new ArchivaRestServiceException( "Target repository cannot be found: " + re.getMessage(),
448 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode() );
450 catch ( RepositoryException rep )
452 throw new ArchivaRestServiceException( "Repository exception: " + rep.getMessage(),
453 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode() );
455 catch ( RepositoryAdminException e )
457 throw new ArchivaRestServiceException( "RepositoryAdmin exception: " + e.getMessage(),
458 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode() );
462 private ArchivaRepositoryMetadata getMetadata( File metadataFile )
463 throws RepositoryMetadataException
465 ArchivaRepositoryMetadata metadata = new ArchivaRepositoryMetadata();
466 if ( metadataFile.exists() )
470 metadata = MavenMetadataReader.read( metadataFile );
472 catch ( XMLException e )
474 throw new RepositoryMetadataException( e.getMessage(), e );
480 private File createPom( File targetPath, String filename, FileMetadata fileMetadata )
483 Model projectModel = new Model();
484 projectModel.setModelVersion( "4.0.0" );
485 projectModel.setGroupId( fileMetadata.getGroupId() );
486 projectModel.setArtifactId( fileMetadata.getArtifactId() );
487 projectModel.setVersion( fileMetadata.getVersion() );
488 projectModel.setPackaging( fileMetadata.getPackaging() );
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 )
557 throws RepositoryMetadataException
559 List<String> availableVersions = new ArrayList<String>();
560 String latestVersion = fileMetadata.getVersion();
562 File projectDir = new File( targetPath ).getParentFile();
563 File projectMetadataFile = new File( projectDir, MetadataTools.MAVEN_METADATA );
565 ArchivaRepositoryMetadata projectMetadata = getMetadata( projectMetadataFile );
567 if ( projectMetadataFile.exists() )
569 availableVersions = projectMetadata.getAvailableVersions();
571 Collections.sort( availableVersions, VersionComparator.getInstance() );
573 if ( !availableVersions.contains( fileMetadata.getVersion() ) )
575 availableVersions.add( fileMetadata.getVersion() );
578 latestVersion = availableVersions.get( availableVersions.size() - 1 );
582 availableVersions.add( fileMetadata.getVersion() );
584 projectMetadata.setGroupId( fileMetadata.getGroupId() );
585 projectMetadata.setArtifactId( fileMetadata.getArtifactId() );
588 if ( projectMetadata.getGroupId() == null )
590 projectMetadata.setGroupId( fileMetadata.getGroupId() );
593 if ( projectMetadata.getArtifactId() == null )
595 projectMetadata.setArtifactId( fileMetadata.getArtifactId() );
598 projectMetadata.setLatestVersion( latestVersion );
599 projectMetadata.setLastUpdatedTimestamp( lastUpdatedTimestamp );
600 projectMetadata.setAvailableVersions( availableVersions );
602 if ( !VersionUtil.isSnapshot( fileMetadata.getVersion() ) )
604 projectMetadata.setReleasedVersion( latestVersion );
607 RepositoryMetadataWriter.write( projectMetadata, projectMetadataFile );
611 fixChecksums( projectMetadataFile );
616 * Update version level metadata for snapshot artifacts. If it does not exist, create the metadata and fix checksums
619 private void updateVersionMetadata( ArchivaRepositoryMetadata metadata, File metadataFile,
620 Date lastUpdatedTimestamp, String timestamp, int buildNumber,
621 boolean fixChecksums, FileMetadata fileMetadata )
622 throws RepositoryMetadataException
624 if ( !metadataFile.exists() )
626 metadata.setGroupId( fileMetadata.getGroupId() );
627 metadata.setArtifactId( fileMetadata.getArtifactId() );
628 metadata.setVersion( fileMetadata.getVersion() );
631 if ( metadata.getSnapshotVersion() == null )
633 metadata.setSnapshotVersion( new SnapshotVersion() );
636 metadata.getSnapshotVersion().setBuildNumber( buildNumber );
637 metadata.getSnapshotVersion().setTimestamp( timestamp );
638 metadata.setLastUpdatedTimestamp( lastUpdatedTimestamp );
640 RepositoryMetadataWriter.write( metadata, metadataFile );
644 fixChecksums( metadataFile );