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.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.RepositoryTask;
48 import org.apache.archiva.webapp.ui.services.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 boolean pomFile = BooleanUtils.toBoolean( getStringValue( multipartBody, "pomFile" ) );
130 Attachment file = multipartBody.getAttachment( "files[]" );
132 //Content-Disposition: form-data; name="files[]"; filename="org.apache.karaf.features.command-2.2.2.jar"
133 String fileName = file.getContentDisposition().getParameter( "filename" );
135 File tmpFile = File.createTempFile( "upload-artifact", "tmp" );
136 tmpFile.deleteOnExit();
137 IOUtils.copy( file.getDataHandler().getInputStream(), new FileOutputStream( tmpFile ) );
138 FileMetadata fileMetadata = new FileMetadata( fileName, tmpFile.length(), "theurl" );
139 fileMetadata.setServerFileName( tmpFile.getPath() );
140 fileMetadata.setClassifier( classifier );
141 fileMetadata.setDeleteUrl( tmpFile.getName() );
142 fileMetadata.setPomFile( pomFile );
144 log.info( "uploading file: {}", fileMetadata );
146 List<FileMetadata> fileMetadatas = getSessionFilesList();
148 fileMetadatas.add( fileMetadata );
152 catch ( IOException e )
154 throw new ArchivaRestServiceException( e.getMessage(),
155 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
161 * FIXME must be per session synchronized not globally
165 protected synchronized List<FileMetadata> getSessionFilesList()
167 List<FileMetadata> fileMetadatas =
168 (List<FileMetadata>) httpServletRequest.getSession().getAttribute( FILES_SESSION_KEY );
169 if ( fileMetadatas == null )
171 fileMetadatas = new CopyOnWriteArrayList<FileMetadata>();
172 httpServletRequest.getSession().setAttribute( FILES_SESSION_KEY, fileMetadatas );
174 return fileMetadatas;
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(
183 new FileMetadata( SystemUtils.getJavaIoTmpDir().getPath() + "/" + fileName ) );
186 return file.delete();
188 return Boolean.FALSE;
191 public Boolean clearUploadedFiles()
192 throws ArchivaRestServiceException
194 List<FileMetadata> fileMetadatas = new ArrayList( getSessionFileMetadatas() );
195 for ( FileMetadata fileMetadata : fileMetadatas )
197 deleteFile( new File( fileMetadata.getServerFileName() ).getName() );
202 public List<FileMetadata> getSessionFileMetadatas()
203 throws ArchivaRestServiceException
205 List<FileMetadata> fileMetadatas =
206 (List<FileMetadata>) httpServletRequest.getSession().getAttribute( FILES_SESSION_KEY );
208 return fileMetadatas == null ? Collections.<FileMetadata>emptyList() : fileMetadatas;
211 public Boolean save( String repositoryId, final String groupId, final String artifactId, String version,
212 String packaging, final boolean generatePom )
213 throws ArchivaRestServiceException
215 List<FileMetadata> fileMetadatas = getSessionFilesList();
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,
238 deleteFile( fileMetadata.getServerFileName() );
241 filesToAdd = Iterables.filter( fileMetadatas, new Predicate<FileMetadata>()
243 public boolean apply( FileMetadata fileMetadata )
245 return fileMetadata != null && fileMetadata.isPomFile();
249 iterator = filesToAdd.iterator();
250 while ( iterator.hasNext() )
252 FileMetadata fileMetadata = iterator.next();
253 log.debug( "fileToAdd: {}", fileMetadata );
254 savePomFile( repositoryId, fileMetadata, groupId, artifactId, version, packaging );
255 deleteFile( fileMetadata.getServerFileName() );
261 protected void savePomFile( String repositoryId, FileMetadata fileMetadata, String groupId, String artifactId,
262 String version, String packaging )
263 throws ArchivaRestServiceException
268 boolean fixChecksums =
269 !( archivaAdministration.getKnownContentConsumers().contains( "create-missing-checksums" ) );
271 ManagedRepository repoConfig = managedRepositoryAdmin.getManagedRepository( repositoryId );
273 ArtifactReference artifactReference = new ArtifactReference();
274 artifactReference.setArtifactId( artifactId );
275 artifactReference.setGroupId( groupId );
276 artifactReference.setVersion( version );
277 artifactReference.setClassifier( fileMetadata.getClassifier() );
278 artifactReference.setType( packaging );
280 ManagedRepositoryContent repository = repositoryFactory.getManagedRepositoryContent( repositoryId );
282 String artifactPath = repository.toPath( artifactReference );
284 int lastIndex = artifactPath.lastIndexOf( '/' );
286 String path = artifactPath.substring( 0, lastIndex );
287 File targetPath = new File( repoConfig.getLocation(), path );
289 String pomFilename = artifactPath.substring( lastIndex + 1 );
290 if ( StringUtils.isNotEmpty( fileMetadata.getClassifier() ) )
292 pomFilename = StringUtils.remove( pomFilename, "-" + fileMetadata.getClassifier() );
294 pomFilename = FilenameUtils.removeExtension( pomFilename ) + ".pom";
296 copyFile( new File( fileMetadata.getServerFileName() ), targetPath, pomFilename, fixChecksums );
297 triggerAuditEvent( repoConfig.getId(), path + "/" + pomFilename, AuditEvent.UPLOAD_FILE );
298 queueRepositoryTask( repoConfig.getId(), new File( targetPath, pomFilename ) );
300 catch ( IOException ie )
302 throw new ArchivaRestServiceException( "Error encountered while uploading pom file: " + ie.getMessage(),
303 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), ie );
305 catch ( RepositoryException rep )
307 throw new ArchivaRestServiceException( "Repository exception: " + rep.getMessage(),
308 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), rep );
310 catch ( RepositoryAdminException e )
312 throw new ArchivaRestServiceException( "RepositoryAdmin exception: " + e.getMessage(),
313 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
317 protected void saveFile( String repositoryId, FileMetadata fileMetadata, boolean generatePom, String groupId,
318 String artifactId, String version, String packaging )
319 throws ArchivaRestServiceException
324 ManagedRepository repoConfig = managedRepositoryAdmin.getManagedRepository( repositoryId );
326 ArtifactReference artifactReference = new ArtifactReference();
327 artifactReference.setArtifactId( artifactId );
328 artifactReference.setGroupId( groupId );
329 artifactReference.setVersion( version );
330 artifactReference.setClassifier( fileMetadata.getClassifier() );
331 artifactReference.setType( packaging );
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( version ) )
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( version ) )
375 filename = filename.replaceAll( VersionUtil.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( version ) && repoConfig.isBlockRedeployments() )
386 throw new ArchivaRestServiceException(
387 "Overwriting released artifacts in repository '" + repoConfig.getId() + "' is not allowed.",
388 Response.Status.BAD_REQUEST.getStatusCode(), null );
392 copyFile( new File( fileMetadata.getServerFileName() ), targetPath, filename, fixChecksums );
393 triggerAuditEvent( repository.getId(), path + "/" + filename, AuditEvent.UPLOAD_FILE );
394 queueRepositoryTask( repository.getId(), targetFile );
397 catch ( IOException ie )
399 throw new ArchivaRestServiceException(
400 "Overwriting released artifacts in repository '" + repoConfig.getId() + "' is not allowed.",
401 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), ie );
406 String pomFilename = filename;
407 if ( StringUtils.isNotEmpty( fileMetadata.getClassifier() ) )
409 pomFilename = StringUtils.remove( pomFilename, "-" + fileMetadata.getClassifier() );
411 pomFilename = FilenameUtils.removeExtension( pomFilename ) + ".pom";
415 File generatedPomFile =
416 createPom( targetPath, pomFilename, fileMetadata, groupId, artifactId, version, packaging );
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(), ie );
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, groupId, artifactId, version, packaging );
438 if ( VersionUtil.isSnapshot( version ) )
440 updateVersionMetadata( versionMetadata, versionMetadataFile, lastUpdatedTimestamp, timestamp,
441 newBuildNumber, fixChecksums, fileMetadata, groupId, artifactId, version,
446 catch ( RepositoryNotFoundException re )
448 throw new ArchivaRestServiceException( "Target repository cannot be found: " + re.getMessage(),
449 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), re );
451 catch ( RepositoryException rep )
453 throw new ArchivaRestServiceException( "Repository exception: " + rep.getMessage(),
454 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), rep );
456 catch ( RepositoryAdminException e )
458 throw new ArchivaRestServiceException( "RepositoryAdmin exception: " + e.getMessage(),
459 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
463 private ArchivaRepositoryMetadata getMetadata( File metadataFile )
464 throws RepositoryMetadataException
466 ArchivaRepositoryMetadata metadata = new ArchivaRepositoryMetadata();
467 if ( metadataFile.exists() )
471 metadata = MavenMetadataReader.read( metadataFile );
473 catch ( XMLException e )
475 throw new RepositoryMetadataException( e.getMessage(), e );
481 private File createPom( File targetPath, String filename, FileMetadata fileMetadata, String groupId,
482 String artifactId, String version, String packaging )
485 Model projectModel = new Model();
486 projectModel.setModelVersion( "4.0.0" );
487 projectModel.setGroupId( groupId );
488 projectModel.setArtifactId( artifactId );
489 projectModel.setVersion( version );
490 projectModel.setPackaging( packaging );
492 File pomFile = new File( targetPath, filename );
493 MavenXpp3Writer writer = new MavenXpp3Writer();
494 FileWriter w = new FileWriter( pomFile );
497 writer.write( w, projectModel );
501 IOUtils.closeQuietly( w );
507 private void fixChecksums( File file )
509 ChecksummedFile checksum = new ChecksummedFile( file );
510 checksum.fixChecksums( algorithms );
513 private void queueRepositoryTask( String repositoryId, File localFile )
515 RepositoryTask task = new RepositoryTask();
516 task.setRepositoryId( repositoryId );
517 task.setResourceFile( localFile );
518 task.setUpdateRelatedArtifacts( true );
519 task.setScanAll( false );
523 scheduler.queueTask( task );
525 catch ( TaskQueueException e )
527 log.error( "Unable to queue repository task to execute consumers on resource file ['" + localFile.getName()
532 private void copyFile( File sourceFile, File targetPath, String targetFilename, boolean fixChecksums )
535 FileOutputStream out = new FileOutputStream( new File( targetPath, targetFilename ) );
536 FileInputStream input = new FileInputStream( sourceFile );
540 IOUtils.copy( input, out );
550 fixChecksums( new File( targetPath, targetFilename ) );
555 * Update artifact level metadata. If it does not exist, create the metadata and fix checksums if necessary.
557 private void updateProjectMetadata( String targetPath, Date lastUpdatedTimestamp, String timestamp, int buildNumber,
558 boolean fixChecksums, FileMetadata fileMetadata, String groupId,
559 String artifactId, String version, String packaging )
560 throws RepositoryMetadataException
562 List<String> availableVersions = new ArrayList<String>();
563 String latestVersion = version;
565 File projectDir = new File( targetPath ).getParentFile();
566 File projectMetadataFile = new File( projectDir, MetadataTools.MAVEN_METADATA );
568 ArchivaRepositoryMetadata projectMetadata = getMetadata( projectMetadataFile );
570 if ( projectMetadataFile.exists() )
572 availableVersions = projectMetadata.getAvailableVersions();
574 Collections.sort( availableVersions, VersionComparator.getInstance() );
576 if ( !availableVersions.contains( version ) )
578 availableVersions.add( version );
581 latestVersion = availableVersions.get( availableVersions.size() - 1 );
585 availableVersions.add( version );
587 projectMetadata.setGroupId( groupId );
588 projectMetadata.setArtifactId( artifactId );
591 if ( projectMetadata.getGroupId() == null )
593 projectMetadata.setGroupId( groupId );
596 if ( projectMetadata.getArtifactId() == null )
598 projectMetadata.setArtifactId( artifactId );
601 projectMetadata.setLatestVersion( latestVersion );
602 projectMetadata.setLastUpdatedTimestamp( lastUpdatedTimestamp );
603 projectMetadata.setAvailableVersions( availableVersions );
605 if ( !VersionUtil.isSnapshot( version ) )
607 projectMetadata.setReleasedVersion( latestVersion );
610 RepositoryMetadataWriter.write( projectMetadata, projectMetadataFile );
614 fixChecksums( projectMetadataFile );
619 * Update version level metadata for snapshot artifacts. If it does not exist, create the metadata and fix checksums
622 private void updateVersionMetadata( ArchivaRepositoryMetadata metadata, File metadataFile,
623 Date lastUpdatedTimestamp, String timestamp, int buildNumber,
624 boolean fixChecksums, FileMetadata fileMetadata, String groupId,
625 String artifactId, String version, String packaging )
626 throws RepositoryMetadataException
628 if ( !metadataFile.exists() )
630 metadata.setGroupId( groupId );
631 metadata.setArtifactId( artifactId );
632 metadata.setVersion( version );
635 if ( metadata.getSnapshotVersion() == null )
637 metadata.setSnapshotVersion( new SnapshotVersion() );
640 metadata.getSnapshotVersion().setBuildNumber( buildNumber );
641 metadata.getSnapshotVersion().setTimestamp( timestamp );
642 metadata.setLastUpdatedTimestamp( lastUpdatedTimestamp );
644 RepositoryMetadataWriter.write( metadata, metadataFile );
648 fixChecksums( metadataFile );