1 package org.apache.archiva.web.api;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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;
62
63
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;
69 import java.io.File;
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;
84
85
86
87
88 @Service( "fileUploadService#rest" )
89 public class DefaultFileUploadService
90 extends AbstractRestService
91 implements FileUploadService
92 {
93 private Logger log = LoggerFactory.getLogger( getClass() );
94
95 @Context
96 private HttpServletRequest httpServletRequest;
97
98 @Inject
99 private ManagedRepositoryAdmin managedRepositoryAdmin;
100
101 @Inject
102 private RepositoryContentFactory repositoryFactory;
103
104 @Inject
105 private ArchivaAdministration archivaAdministration;
106
107 private ChecksumAlgorithm[] algorithms = new ChecksumAlgorithm[]{ ChecksumAlgorithm.SHA1, ChecksumAlgorithm.MD5 };
108
109 @Inject
110 @Named( value = "archivaTaskScheduler#repository" )
111 private ArchivaTaskScheduler scheduler;
112
113 private String getStringValue( MultipartBody multipartBody, String attachmentId )
114 throws IOException
115 {
116 Attachment attachment = multipartBody.getAttachment( attachmentId );
117 return attachment == null ? "" : IOUtils.toString( attachment.getDataHandler().getInputStream() );
118 }
119
120 public FileMetadata post( MultipartBody multipartBody )
121 throws ArchivaRestServiceException
122 {
123
124 try
125 {
126
127 String classifier = getStringValue( multipartBody, "classifier" );
128
129
130 boolean pomFile = BooleanUtils.toBoolean( getStringValue( multipartBody, "pomFile" ) );
131
132 Attachment file = multipartBody.getAttachment( "files[]" );
133
134
135 String fileName = file.getContentDisposition().getParameter( "filename" );
136
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 );
145
146 log.info( "uploading file: {}", fileMetadata );
147
148 List<FileMetadata> fileMetadatas = getSessionFilesList();
149
150 fileMetadatas.add( fileMetadata );
151
152 return fileMetadata;
153 }
154 catch ( IOException e )
155 {
156 throw new ArchivaRestServiceException( e.getMessage(),
157 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
158 }
159
160 }
161
162
163
164
165
166
167 protected synchronized List<FileMetadata> getSessionFilesList()
168 {
169 List<FileMetadata> fileMetadatas =
170 (List<FileMetadata>) httpServletRequest.getSession().getAttribute( FILES_SESSION_KEY );
171 if ( fileMetadatas == null )
172 {
173 fileMetadatas = new CopyOnWriteArrayList<FileMetadata>();
174 httpServletRequest.getSession().setAttribute( FILES_SESSION_KEY, fileMetadatas );
175 }
176 return fileMetadatas;
177 }
178
179 public Boolean deleteFile( String fileName )
180 throws ArchivaRestServiceException
181 {
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 ) );
186 if ( file.exists() )
187 {
188 return file.delete();
189 }
190 return Boolean.FALSE;
191 }
192
193 public Boolean clearUploadedFiles()
194 throws ArchivaRestServiceException
195 {
196 List<FileMetadata> fileMetadatas = new ArrayList( getSessionFileMetadatas() );
197 for ( FileMetadata fileMetadata : fileMetadatas )
198 {
199 deleteFile( new File( fileMetadata.getServerFileName() ).getName() );
200 }
201 return Boolean.TRUE;
202 }
203
204 public List<FileMetadata> getSessionFileMetadatas()
205 throws ArchivaRestServiceException
206 {
207 List<FileMetadata> fileMetadatas =
208 (List<FileMetadata>) httpServletRequest.getSession().getAttribute( FILES_SESSION_KEY );
209
210 return fileMetadatas == null ? Collections.<FileMetadata>emptyList() : fileMetadatas;
211 }
212
213 public Boolean save( String repositoryId, final String groupId, final String artifactId, String version,
214 String packaging, final boolean generatePom )
215 throws ArchivaRestServiceException
216 {
217 List<FileMetadata> fileMetadatas = getSessionFilesList();
218 if ( fileMetadatas == null || fileMetadatas.isEmpty() )
219 {
220 return Boolean.FALSE;
221 }
222
223
224 Iterable<FileMetadata> filesToAdd = Iterables.filter( fileMetadatas, new Predicate<FileMetadata>()
225 {
226 public boolean apply( FileMetadata fileMetadata )
227 {
228 return fileMetadata != null && !fileMetadata.isPomFile();
229 }
230 } );
231 Iterator<FileMetadata> iterator = filesToAdd.iterator();
232 boolean pomGenerated = false;
233 while ( iterator.hasNext() )
234 {
235 FileMetadata fileMetadata = iterator.next();
236 log.debug( "fileToAdd: {}", fileMetadata );
237 saveFile( repositoryId, fileMetadata, generatePom && !pomGenerated, groupId, artifactId, version,
238 packaging );
239 pomGenerated = true;
240 deleteFile( fileMetadata.getServerFileName() );
241 }
242
243 filesToAdd = Iterables.filter( fileMetadatas, new Predicate<FileMetadata>()
244 {
245 public boolean apply( FileMetadata fileMetadata )
246 {
247 return fileMetadata != null && fileMetadata.isPomFile();
248 }
249 } );
250
251 iterator = filesToAdd.iterator();
252 while ( iterator.hasNext() )
253 {
254 FileMetadata fileMetadata = iterator.next();
255 log.debug( "fileToAdd: {}", fileMetadata );
256 savePomFile( repositoryId, fileMetadata, groupId, artifactId, version, packaging );
257 deleteFile( fileMetadata.getServerFileName() );
258 }
259
260 return Boolean.TRUE;
261 }
262
263 protected void savePomFile( String repositoryId, FileMetadata fileMetadata, String groupId, String artifactId,
264 String version, String packaging )
265 throws ArchivaRestServiceException
266 {
267
268 try
269 {
270 boolean fixChecksums =
271 !( archivaAdministration.getKnownContentConsumers().contains( "create-missing-checksums" ) );
272
273 ManagedRepository repoConfig = managedRepositoryAdmin.getManagedRepository( repositoryId );
274
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 );
281
282 ManagedRepositoryContent repository = repositoryFactory.getManagedRepositoryContent( repositoryId );
283
284 String artifactPath = repository.toPath( artifactReference );
285
286 int lastIndex = artifactPath.lastIndexOf( '/' );
287
288 String path = artifactPath.substring( 0, lastIndex );
289 File targetPath = new File( repoConfig.getLocation(), path );
290
291 String pomFilename = artifactPath.substring( lastIndex + 1 );
292 if ( StringUtils.isNotEmpty( fileMetadata.getClassifier() ) )
293 {
294 pomFilename = StringUtils.remove( pomFilename, "-" + fileMetadata.getClassifier() );
295 }
296 pomFilename = FilenameUtils.removeExtension( pomFilename ) + ".pom";
297
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 ) );
301 }
302 catch ( IOException ie )
303 {
304 throw new ArchivaRestServiceException( "Error encountered while uploading pom file: " + ie.getMessage(),
305 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), ie );
306 }
307 catch ( RepositoryException rep )
308 {
309 throw new ArchivaRestServiceException( "Repository exception: " + rep.getMessage(),
310 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), rep );
311 }
312 catch ( RepositoryAdminException e )
313 {
314 throw new ArchivaRestServiceException( "RepositoryAdmin exception: " + e.getMessage(),
315 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
316 }
317 }
318
319 protected void saveFile( String repositoryId, FileMetadata fileMetadata, boolean generatePom, String groupId,
320 String artifactId, String version, String packaging )
321 throws ArchivaRestServiceException
322 {
323 try
324 {
325
326 ManagedRepository repoConfig = managedRepositoryAdmin.getManagedRepository( repositoryId );
327
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 );
334
335 ManagedRepositoryContent repository = repositoryFactory.getManagedRepositoryContent( repositoryId );
336
337 String artifactPath = repository.toPath( artifactReference );
338
339 int lastIndex = artifactPath.lastIndexOf( '/' );
340
341 String path = artifactPath.substring( 0, lastIndex );
342 File targetPath = new File( repoConfig.getLocation(), path );
343
344 log.debug( "artifactPath: {} found targetPath: {}", artifactPath, targetPath );
345
346 Date lastUpdatedTimestamp = Calendar.getInstance().getTime();
347 int newBuildNumber = -1;
348 String timestamp = null;
349
350 File versionMetadataFile = new File( targetPath, MetadataTools.MAVEN_METADATA );
351 ArchivaRepositoryMetadata versionMetadata = getMetadata( versionMetadataFile );
352
353 if ( VersionUtil.isSnapshot( version ) )
354 {
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 )
360 {
361 newBuildNumber = versionMetadata.getSnapshotVersion().getBuildNumber() + 1;
362 }
363 else
364 {
365 newBuildNumber = 1;
366 }
367 }
368
369 if ( !targetPath.exists() )
370 {
371 targetPath.mkdirs();
372 }
373
374 String filename = artifactPath.substring( lastIndex + 1 );
375 if ( VersionUtil.isSnapshot( version ) )
376 {
377 filename = filename.replaceAll( VersionUtil.SNAPSHOT, timestamp + "-" + newBuildNumber );
378 }
379
380 boolean fixChecksums =
381 !( archivaAdministration.getKnownContentConsumers().contains( "create-missing-checksums" ) );
382
383 try
384 {
385 File targetFile = new File( targetPath, filename );
386 if ( targetFile.exists() && !VersionUtil.isSnapshot( version ) && repoConfig.isBlockRedeployments() )
387 {
388 throw new ArchivaRestServiceException(
389 "Overwriting released artifacts in repository '" + repoConfig.getId() + "' is not allowed.",
390 Response.Status.BAD_REQUEST.getStatusCode(), null );
391 }
392 else
393 {
394 copyFile( new File( fileMetadata.getServerFileName() ), targetPath, filename, fixChecksums );
395 triggerAuditEvent( repository.getId(), path + "/" + filename, AuditEvent.UPLOAD_FILE );
396 queueRepositoryTask( repository.getId(), targetFile );
397 }
398 }
399 catch ( IOException ie )
400 {
401 throw new ArchivaRestServiceException(
402 "Overwriting released artifacts in repository '" + repoConfig.getId() + "' is not allowed.",
403 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), ie );
404 }
405
406 if ( generatePom )
407 {
408 String pomFilename = filename;
409 if ( StringUtils.isNotEmpty( fileMetadata.getClassifier() ) )
410 {
411 pomFilename = StringUtils.remove( pomFilename, "-" + fileMetadata.getClassifier() );
412 }
413 pomFilename = FilenameUtils.removeExtension( pomFilename ) + ".pom";
414
415 try
416 {
417 File generatedPomFile =
418 createPom( targetPath, pomFilename, fileMetadata, groupId, artifactId, version, packaging );
419 triggerAuditEvent( repoConfig.getId(), path + "/" + pomFilename, AuditEvent.UPLOAD_FILE );
420 if ( fixChecksums )
421 {
422 fixChecksums( generatedPomFile );
423 }
424 queueRepositoryTask( repoConfig.getId(), generatedPomFile );
425 }
426 catch ( IOException ie )
427 {
428 throw new ArchivaRestServiceException(
429 "Error encountered while writing pom file: " + ie.getMessage(),
430 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), ie );
431 }
432 }
433
434
435 if ( !archivaAdministration.getKnownContentConsumers().contains( "metadata-updater" ) )
436 {
437 updateProjectMetadata( targetPath.getAbsolutePath(), lastUpdatedTimestamp, timestamp, newBuildNumber,
438 fixChecksums, fileMetadata, groupId, artifactId, version, packaging );
439
440 if ( VersionUtil.isSnapshot( version ) )
441 {
442 updateVersionMetadata( versionMetadata, versionMetadataFile, lastUpdatedTimestamp, timestamp,
443 newBuildNumber, fixChecksums, fileMetadata, groupId, artifactId, version,
444 packaging );
445 }
446 }
447 }
448 catch ( RepositoryNotFoundException re )
449 {
450 throw new ArchivaRestServiceException( "Target repository cannot be found: " + re.getMessage(),
451 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), re );
452 }
453 catch ( RepositoryException rep )
454 {
455 throw new ArchivaRestServiceException( "Repository exception: " + rep.getMessage(),
456 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), rep );
457 }
458 catch ( RepositoryAdminException e )
459 {
460 throw new ArchivaRestServiceException( "RepositoryAdmin exception: " + e.getMessage(),
461 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
462 }
463 }
464
465 private ArchivaRepositoryMetadata getMetadata( File metadataFile )
466 throws RepositoryMetadataException
467 {
468 ArchivaRepositoryMetadata metadata = new ArchivaRepositoryMetadata();
469 if ( metadataFile.exists() )
470 {
471 try
472 {
473 metadata = MavenMetadataReader.read( metadataFile );
474 }
475 catch ( XMLException e )
476 {
477 throw new RepositoryMetadataException( e.getMessage(), e );
478 }
479 }
480 return metadata;
481 }
482
483 private File createPom( File targetPath, String filename, FileMetadata fileMetadata, String groupId,
484 String artifactId, String version, String packaging )
485 throws IOException
486 {
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 );
493
494 File pomFile = new File( targetPath, filename );
495 MavenXpp3Writer writer = new MavenXpp3Writer();
496 FileWriter w = new FileWriter( pomFile );
497 try
498 {
499 writer.write( w, projectModel );
500 }
501 finally
502 {
503 IOUtils.closeQuietly( w );
504 }
505
506 return pomFile;
507 }
508
509 private void fixChecksums( File file )
510 {
511 ChecksummedFile checksum = new ChecksummedFile( file );
512 checksum.fixChecksums( algorithms );
513 }
514
515 private void queueRepositoryTask( String repositoryId, File localFile )
516 {
517 RepositoryTask task = new RepositoryTask();
518 task.setRepositoryId( repositoryId );
519 task.setResourceFile( localFile );
520 task.setUpdateRelatedArtifacts( true );
521 task.setScanAll( false );
522
523 try
524 {
525 scheduler.queueTask( task );
526 }
527 catch ( TaskQueueException e )
528 {
529 log.error( "Unable to queue repository task to execute consumers on resource file ['" + localFile.getName()
530 + "']." );
531 }
532 }
533
534 private void copyFile( File sourceFile, File targetPath, String targetFilename, boolean fixChecksums )
535 throws IOException
536 {
537 FileOutputStream out = new FileOutputStream( new File( targetPath, targetFilename ) );
538 FileInputStream input = new FileInputStream( sourceFile );
539
540 try
541 {
542 IOUtils.copy( input, out );
543 }
544 finally
545 {
546 out.close();
547 input.close();
548 }
549
550 if ( fixChecksums )
551 {
552 fixChecksums( new File( targetPath, targetFilename ) );
553 }
554 }
555
556
557
558
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
563 {
564 List<String> availableVersions = new ArrayList<String>();
565 String latestVersion = version;
566
567 File projectDir = new File( targetPath ).getParentFile();
568 File projectMetadataFile = new File( projectDir, MetadataTools.MAVEN_METADATA );
569
570 ArchivaRepositoryMetadata projectMetadata = getMetadata( projectMetadataFile );
571
572 if ( projectMetadataFile.exists() )
573 {
574 availableVersions = projectMetadata.getAvailableVersions();
575
576 Collections.sort( availableVersions, VersionComparator.getInstance() );
577
578 if ( !availableVersions.contains( version ) )
579 {
580 availableVersions.add( version );
581 }
582
583 latestVersion = availableVersions.get( availableVersions.size() - 1 );
584 }
585 else
586 {
587 availableVersions.add( version );
588
589 projectMetadata.setGroupId( groupId );
590 projectMetadata.setArtifactId( artifactId );
591 }
592
593 if ( projectMetadata.getGroupId() == null )
594 {
595 projectMetadata.setGroupId( groupId );
596 }
597
598 if ( projectMetadata.getArtifactId() == null )
599 {
600 projectMetadata.setArtifactId( artifactId );
601 }
602
603 projectMetadata.setLatestVersion( latestVersion );
604 projectMetadata.setLastUpdatedTimestamp( lastUpdatedTimestamp );
605 projectMetadata.setAvailableVersions( availableVersions );
606
607 if ( !VersionUtil.isSnapshot( version ) )
608 {
609 projectMetadata.setReleasedVersion( latestVersion );
610 }
611
612 RepositoryMetadataWriter.write( projectMetadata, projectMetadataFile );
613
614 if ( fixChecksums )
615 {
616 fixChecksums( projectMetadataFile );
617 }
618 }
619
620
621
622
623
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
629 {
630 if ( !metadataFile.exists() )
631 {
632 metadata.setGroupId( groupId );
633 metadata.setArtifactId( artifactId );
634 metadata.setVersion( version );
635 }
636
637 if ( metadata.getSnapshotVersion() == null )
638 {
639 metadata.setSnapshotVersion( new SnapshotVersion() );
640 }
641
642 metadata.getSnapshotVersion().setBuildNumber( buildNumber );
643 metadata.getSnapshotVersion().setTimestamp( timestamp );
644 metadata.setLastUpdatedTimestamp( lastUpdatedTimestamp );
645
646 RepositoryMetadataWriter.write( metadata, metadataFile );
647
648 if ( fixChecksums )
649 {
650 fixChecksums( metadataFile );
651 }
652 }
653
654
655 }