001 package org.apache.archiva.rest.services; 002 003 /* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022 import org.apache.archiva.admin.model.RepositoryAdminException; 023 import org.apache.archiva.admin.model.admin.ArchivaAdministration; 024 import org.apache.archiva.admin.model.beans.ManagedRepository; 025 import org.apache.archiva.admin.model.managed.ManagedRepositoryAdmin; 026 import org.apache.archiva.audit.AuditEvent; 027 import org.apache.archiva.checksum.ChecksumAlgorithm; 028 import org.apache.archiva.checksum.ChecksummedFile; 029 import org.apache.archiva.common.plexusbridge.MavenIndexerUtils; 030 import org.apache.archiva.common.plexusbridge.PlexusSisuBridge; 031 import org.apache.archiva.common.utils.VersionComparator; 032 import org.apache.archiva.common.utils.VersionUtil; 033 import org.apache.archiva.maven2.metadata.MavenMetadataReader; 034 import org.apache.archiva.maven2.model.Artifact; 035 import org.apache.archiva.metadata.model.ArtifactMetadata; 036 import org.apache.archiva.metadata.model.maven2.MavenArtifactFacet; 037 import org.apache.archiva.metadata.repository.MetadataRepository; 038 import org.apache.archiva.metadata.repository.MetadataRepositoryException; 039 import org.apache.archiva.metadata.repository.MetadataResolutionException; 040 import org.apache.archiva.metadata.repository.RepositorySession; 041 import org.apache.archiva.metadata.repository.RepositorySessionFactory; 042 import org.apache.archiva.model.ArchivaRepositoryMetadata; 043 import org.apache.archiva.model.ArtifactReference; 044 import org.apache.archiva.model.VersionedReference; 045 import org.apache.archiva.redback.authentication.AuthenticationResult; 046 import org.apache.archiva.redback.authorization.AuthorizationException; 047 import org.apache.archiva.redback.components.taskqueue.TaskQueueException; 048 import org.apache.archiva.redback.system.DefaultSecuritySession; 049 import org.apache.archiva.redback.system.SecuritySession; 050 import org.apache.archiva.redback.system.SecuritySystem; 051 import org.apache.archiva.redback.users.User; 052 import org.apache.archiva.redback.users.UserManagerException; 053 import org.apache.archiva.redback.users.UserNotFoundException; 054 import org.apache.archiva.repository.ContentNotFoundException; 055 import org.apache.archiva.repository.ManagedRepositoryContent; 056 import org.apache.archiva.repository.RepositoryContentFactory; 057 import org.apache.archiva.repository.RepositoryException; 058 import org.apache.archiva.repository.RepositoryNotFoundException; 059 import org.apache.archiva.repository.events.RepositoryListener; 060 import org.apache.archiva.repository.metadata.MetadataTools; 061 import org.apache.archiva.repository.metadata.RepositoryMetadataException; 062 import org.apache.archiva.repository.metadata.RepositoryMetadataWriter; 063 import org.apache.archiva.repository.scanner.RepositoryScanStatistics; 064 import org.apache.archiva.repository.scanner.RepositoryScanner; 065 import org.apache.archiva.repository.scanner.RepositoryScannerException; 066 import org.apache.archiva.rest.api.model.ArtifactTransferRequest; 067 import org.apache.archiva.rest.api.services.ArchivaRestServiceException; 068 import org.apache.archiva.rest.api.services.RepositoriesService; 069 import org.apache.archiva.scheduler.ArchivaTaskScheduler; 070 import org.apache.archiva.scheduler.indexing.ArchivaIndexingTaskExecutor; 071 import org.apache.archiva.scheduler.indexing.ArtifactIndexingTask; 072 import org.apache.archiva.scheduler.indexing.DownloadRemoteIndexException; 073 import org.apache.archiva.scheduler.indexing.DownloadRemoteIndexScheduler; 074 import org.apache.archiva.scheduler.repository.model.RepositoryTask; 075 import org.apache.archiva.security.ArchivaSecurityException; 076 import org.apache.archiva.security.common.ArchivaRoleConstants; 077 import org.apache.archiva.xml.XMLException; 078 import org.apache.commons.io.FilenameUtils; 079 import org.apache.commons.io.IOUtils; 080 import org.apache.commons.lang.StringUtils; 081 import org.apache.maven.index.context.IndexingContext; 082 import org.slf4j.Logger; 083 import org.slf4j.LoggerFactory; 084 import org.springframework.stereotype.Service; 085 086 import javax.inject.Inject; 087 import javax.inject.Named; 088 import javax.ws.rs.core.Response; 089 import java.io.File; 090 import java.io.FileInputStream; 091 import java.io.FileOutputStream; 092 import java.io.IOException; 093 import java.text.DateFormat; 094 import java.text.SimpleDateFormat; 095 import java.util.ArrayList; 096 import java.util.Calendar; 097 import java.util.Collection; 098 import java.util.Collections; 099 import java.util.Date; 100 import java.util.List; 101 import java.util.Set; 102 import java.util.TimeZone; 103 104 /** 105 * @author Olivier Lamy 106 * @since 1.4-M1 107 */ 108 @Service( "repositoriesService#rest" ) 109 public class DefaultRepositoriesService 110 extends AbstractRestService 111 implements RepositoriesService 112 { 113 private Logger log = LoggerFactory.getLogger( getClass() ); 114 115 @Inject 116 @Named( value = "taskExecutor#indexing" ) 117 private ArchivaIndexingTaskExecutor archivaIndexingTaskExecutor; 118 119 @Inject 120 private ManagedRepositoryAdmin managedRepositoryAdmin; 121 122 @Inject 123 private PlexusSisuBridge plexusSisuBridge; 124 125 @Inject 126 private MavenIndexerUtils mavenIndexerUtils; 127 128 @Inject 129 private SecuritySystem securitySystem; 130 131 @Inject 132 private RepositoryContentFactory repositoryFactory; 133 134 @Inject 135 @Named( value = "archivaTaskScheduler#repository" ) 136 private ArchivaTaskScheduler scheduler; 137 138 @Inject 139 private DownloadRemoteIndexScheduler downloadRemoteIndexScheduler; 140 141 @Inject 142 @Named( value = "repositorySessionFactory" ) 143 protected RepositorySessionFactory repositorySessionFactory; 144 145 @Inject 146 protected List<RepositoryListener> listeners = new ArrayList<RepositoryListener>(); 147 148 @Inject 149 private RepositoryScanner repoScanner; 150 151 private ChecksumAlgorithm[] algorithms = new ChecksumAlgorithm[]{ ChecksumAlgorithm.SHA1, ChecksumAlgorithm.MD5 }; 152 153 public Boolean scanRepository( String repositoryId, boolean fullScan ) 154 { 155 return doScanRepository( repositoryId, fullScan ); 156 } 157 158 public Boolean alreadyScanning( String repositoryId ) 159 { 160 return repositoryTaskScheduler.isProcessingRepositoryTask( repositoryId ); 161 } 162 163 public Boolean removeScanningTaskFromQueue( String repositoryId ) 164 { 165 RepositoryTask task = new RepositoryTask(); 166 task.setRepositoryId( repositoryId ); 167 try 168 { 169 return repositoryTaskScheduler.unQueueTask( task ); 170 } 171 catch ( TaskQueueException e ) 172 { 173 log.error( "failed to unschedule scanning of repo with id {}", repositoryId, e ); 174 return false; 175 } 176 } 177 178 public Boolean scanRepositoryNow( String repositoryId, boolean fullScan ) 179 throws ArchivaRestServiceException 180 { 181 182 try 183 { 184 ManagedRepository repository = managedRepositoryAdmin.getManagedRepository( repositoryId ); 185 186 IndexingContext context = managedRepositoryAdmin.createIndexContext( repository ); 187 188 ArtifactIndexingTask task = 189 new ArtifactIndexingTask( repository, null, ArtifactIndexingTask.Action.FINISH, context ); 190 191 task.setExecuteOnEntireRepo( true ); 192 task.setOnlyUpdate( !fullScan ); 193 194 archivaIndexingTaskExecutor.executeTask( task ); 195 return Boolean.TRUE; 196 } 197 catch ( Exception e ) 198 { 199 log.error( e.getMessage(), e ); 200 throw new ArchivaRestServiceException( e.getMessage(), e ); 201 } 202 } 203 204 public Boolean scheduleDownloadRemoteIndex( String repositoryId, boolean now, boolean fullDownload ) 205 throws ArchivaRestServiceException 206 { 207 try 208 { 209 downloadRemoteIndexScheduler.scheduleDownloadRemote( repositoryId, now, fullDownload ); 210 } 211 catch ( DownloadRemoteIndexException e ) 212 { 213 log.error( e.getMessage(), e ); 214 throw new ArchivaRestServiceException( e.getMessage(), e ); 215 } 216 return Boolean.TRUE; 217 } 218 219 public Boolean copyArtifact( ArtifactTransferRequest artifactTransferRequest ) 220 throws ArchivaRestServiceException 221 { 222 // check parameters 223 String userName = getAuditInformation().getUser().getUsername(); 224 if ( StringUtils.isBlank( userName ) ) 225 { 226 throw new ArchivaRestServiceException( "copyArtifact call: userName not found", null ); 227 } 228 229 if ( StringUtils.isBlank( artifactTransferRequest.getRepositoryId() ) ) 230 { 231 throw new ArchivaRestServiceException( "copyArtifact call: sourceRepositoryId cannot be null", null ); 232 } 233 234 if ( StringUtils.isBlank( artifactTransferRequest.getTargetRepositoryId() ) ) 235 { 236 throw new ArchivaRestServiceException( "copyArtifact call: targetRepositoryId cannot be null", null ); 237 } 238 239 ManagedRepository source = null; 240 try 241 { 242 source = managedRepositoryAdmin.getManagedRepository( artifactTransferRequest.getRepositoryId() ); 243 } 244 catch ( RepositoryAdminException e ) 245 { 246 throw new ArchivaRestServiceException( e.getMessage(), e ); 247 } 248 249 if ( source == null ) 250 { 251 throw new ArchivaRestServiceException( 252 "cannot find repository with id " + artifactTransferRequest.getRepositoryId(), null ); 253 } 254 255 ManagedRepository target = null; 256 try 257 { 258 target = managedRepositoryAdmin.getManagedRepository( artifactTransferRequest.getTargetRepositoryId() ); 259 } 260 catch ( RepositoryAdminException e ) 261 { 262 throw new ArchivaRestServiceException( e.getMessage(), e ); 263 } 264 265 if ( target == null ) 266 { 267 throw new ArchivaRestServiceException( 268 "cannot find repository with id " + artifactTransferRequest.getTargetRepositoryId(), null ); 269 } 270 271 if ( StringUtils.isBlank( artifactTransferRequest.getGroupId() ) ) 272 { 273 throw new ArchivaRestServiceException( "groupId is mandatory", null ); 274 } 275 276 if ( StringUtils.isBlank( artifactTransferRequest.getArtifactId() ) ) 277 { 278 throw new ArchivaRestServiceException( "artifactId is mandatory", null ); 279 } 280 281 if ( StringUtils.isBlank( artifactTransferRequest.getVersion() ) ) 282 { 283 throw new ArchivaRestServiceException( "version is mandatory", null ); 284 } 285 286 if ( VersionUtil.isSnapshot( artifactTransferRequest.getVersion() ) ) 287 { 288 throw new ArchivaRestServiceException( "copy of SNAPSHOT not supported", null ); 289 } 290 291 // end check parameters 292 293 User user = null; 294 try 295 { 296 user = securitySystem.getUserManager().findUser( userName ); 297 } 298 catch ( UserNotFoundException e ) 299 { 300 throw new ArchivaRestServiceException( "user " + userName + " not found", e ); 301 } 302 catch ( UserManagerException e ) 303 { 304 throw new ArchivaRestServiceException( "ArchivaRestServiceException:" + e.getMessage(), e ); 305 } 306 307 // check karma on source : read 308 AuthenticationResult authn = new AuthenticationResult( true, userName, null ); 309 SecuritySession securitySession = new DefaultSecuritySession( authn, user ); 310 try 311 { 312 boolean authz = 313 securitySystem.isAuthorized( securitySession, ArchivaRoleConstants.OPERATION_REPOSITORY_ACCESS, 314 artifactTransferRequest.getRepositoryId() ); 315 if ( !authz ) 316 { 317 throw new ArchivaRestServiceException( 318 "not authorized to access repo:" + artifactTransferRequest.getRepositoryId(), null ); 319 } 320 } 321 catch ( AuthorizationException e ) 322 { 323 log.error( "error reading permission: " + e.getMessage(), e ); 324 throw new ArchivaRestServiceException( e.getMessage(), e ); 325 } 326 327 // check karma on target: write 328 try 329 { 330 boolean authz = 331 securitySystem.isAuthorized( securitySession, ArchivaRoleConstants.OPERATION_REPOSITORY_UPLOAD, 332 artifactTransferRequest.getTargetRepositoryId() ); 333 if ( !authz ) 334 { 335 throw new ArchivaRestServiceException( 336 "not authorized to write to repo:" + artifactTransferRequest.getTargetRepositoryId(), null ); 337 } 338 } 339 catch ( AuthorizationException e ) 340 { 341 log.error( "error reading permission: " + e.getMessage(), e ); 342 throw new ArchivaRestServiceException( e.getMessage(), e ); 343 } 344 345 // sounds good we can continue ! 346 347 ArtifactReference artifactReference = new ArtifactReference(); 348 artifactReference.setArtifactId( artifactTransferRequest.getArtifactId() ); 349 artifactReference.setGroupId( artifactTransferRequest.getGroupId() ); 350 artifactReference.setVersion( artifactTransferRequest.getVersion() ); 351 artifactReference.setClassifier( artifactTransferRequest.getClassifier() ); 352 String packaging = StringUtils.trim( artifactTransferRequest.getPackaging() ); 353 artifactReference.setType( StringUtils.isEmpty( packaging ) ? "jar" : packaging ); 354 355 try 356 { 357 358 ManagedRepositoryContent sourceRepository = 359 repositoryFactory.getManagedRepositoryContent( artifactTransferRequest.getRepositoryId() ); 360 361 String artifactSourcePath = sourceRepository.toPath( artifactReference ); 362 363 if ( StringUtils.isEmpty( artifactSourcePath ) ) 364 { 365 log.error( "cannot find artifact " + artifactTransferRequest.toString() ); 366 throw new ArchivaRestServiceException( "cannot find artifact " + artifactTransferRequest.toString(), 367 null ); 368 } 369 370 File artifactFile = new File( source.getLocation(), artifactSourcePath ); 371 372 if ( !artifactFile.exists() ) 373 { 374 log.error( "cannot find artifact " + artifactTransferRequest.toString() ); 375 throw new ArchivaRestServiceException( "cannot find artifact " + artifactTransferRequest.toString(), 376 null ); 377 } 378 379 ManagedRepositoryContent targetRepository = 380 repositoryFactory.getManagedRepositoryContent( artifactTransferRequest.getTargetRepositoryId() ); 381 382 String artifactPath = targetRepository.toPath( artifactReference ); 383 384 int lastIndex = artifactPath.lastIndexOf( '/' ); 385 386 String path = artifactPath.substring( 0, lastIndex ); 387 File targetPath = new File( target.getLocation(), path ); 388 389 Date lastUpdatedTimestamp = Calendar.getInstance().getTime(); 390 int newBuildNumber = 1; 391 String timestamp = null; 392 393 File versionMetadataFile = new File( targetPath, MetadataTools.MAVEN_METADATA ); 394 ArchivaRepositoryMetadata versionMetadata = getMetadata( versionMetadataFile ); 395 396 if ( !targetPath.exists() ) 397 { 398 targetPath.mkdirs(); 399 } 400 401 String filename = artifactPath.substring( lastIndex + 1 ); 402 403 boolean fixChecksums = 404 !( archivaAdministration.getKnownContentConsumers().contains( "create-missing-checksums" ) ); 405 406 File targetFile = new File( targetPath, filename ); 407 if ( targetFile.exists() && target.isBlockRedeployments() ) 408 { 409 throw new ArchivaRestServiceException( 410 "artifact already exists in target repo: " + artifactTransferRequest.getTargetRepositoryId() 411 + " and redeployment blocked", null ); 412 } 413 else 414 { 415 copyFile( artifactFile, targetPath, filename, fixChecksums ); 416 queueRepositoryTask( target.getId(), targetFile ); 417 } 418 419 // copy source pom to target repo 420 String pomFilename = filename; 421 if ( StringUtils.isNotBlank( artifactTransferRequest.getClassifier() ) ) 422 { 423 pomFilename = StringUtils.remove( pomFilename, "-" + artifactTransferRequest.getClassifier() ); 424 } 425 pomFilename = FilenameUtils.removeExtension( pomFilename ) + ".pom"; 426 427 File pomFile = new File( 428 new File( source.getLocation(), artifactSourcePath.substring( 0, artifactPath.lastIndexOf( '/' ) ) ), 429 pomFilename ); 430 431 if ( pomFile != null && pomFile.length() > 0 ) 432 { 433 copyFile( pomFile, targetPath, pomFilename, fixChecksums ); 434 queueRepositoryTask( target.getId(), new File( targetPath, pomFilename ) ); 435 436 437 } 438 439 // explicitly update only if metadata-updater consumer is not enabled! 440 if ( !archivaAdministration.getKnownContentConsumers().contains( "metadata-updater" ) ) 441 { 442 updateProjectMetadata( targetPath.getAbsolutePath(), lastUpdatedTimestamp, timestamp, newBuildNumber, 443 fixChecksums, artifactTransferRequest ); 444 445 446 } 447 448 String msg = 449 "Artifact \'" + artifactTransferRequest.getGroupId() + ":" + artifactTransferRequest.getArtifactId() 450 + ":" + artifactTransferRequest.getVersion() + "\' was successfully deployed to repository \'" 451 + artifactTransferRequest.getTargetRepositoryId() + "\'"; 452 453 } 454 catch ( RepositoryException e ) 455 { 456 log.error( "RepositoryException: " + e.getMessage(), e ); 457 throw new ArchivaRestServiceException( e.getMessage(), e ); 458 } 459 catch ( RepositoryAdminException e ) 460 { 461 log.error( "RepositoryAdminException: " + e.getMessage(), e ); 462 throw new ArchivaRestServiceException( e.getMessage(), e ); 463 } 464 catch ( IOException e ) 465 { 466 log.error( "IOException: " + e.getMessage(), e ); 467 throw new ArchivaRestServiceException( e.getMessage(), e ); 468 } 469 return true; 470 } 471 472 private void queueRepositoryTask( String repositoryId, File localFile ) 473 { 474 RepositoryTask task = new RepositoryTask(); 475 task.setRepositoryId( repositoryId ); 476 task.setResourceFile( localFile ); 477 task.setUpdateRelatedArtifacts( true ); 478 //task.setScanAll( true ); 479 480 try 481 { 482 scheduler.queueTask( task ); 483 } 484 catch ( TaskQueueException e ) 485 { 486 log.error( "Unable to queue repository task to execute consumers on resource file ['" + localFile.getName() 487 + "']." ); 488 } 489 } 490 491 private ArchivaRepositoryMetadata getMetadata( File metadataFile ) 492 throws RepositoryMetadataException 493 { 494 ArchivaRepositoryMetadata metadata = new ArchivaRepositoryMetadata(); 495 if ( metadataFile.exists() ) 496 { 497 try 498 { 499 metadata = MavenMetadataReader.read( metadataFile ); 500 } 501 catch ( XMLException e ) 502 { 503 throw new RepositoryMetadataException( e.getMessage(), e ); 504 } 505 } 506 return metadata; 507 } 508 509 private File getMetadata( String targetPath ) 510 { 511 String artifactPath = targetPath.substring( 0, targetPath.lastIndexOf( File.separatorChar ) ); 512 513 return new File( artifactPath, MetadataTools.MAVEN_METADATA ); 514 } 515 516 private void copyFile( File sourceFile, File targetPath, String targetFilename, boolean fixChecksums ) 517 throws IOException 518 { 519 FileOutputStream out = new FileOutputStream( new File( targetPath, targetFilename ) ); 520 FileInputStream input = new FileInputStream( sourceFile ); 521 522 try 523 { 524 IOUtils.copy( input, out ); 525 } 526 finally 527 { 528 IOUtils.closeQuietly( out ); 529 IOUtils.closeQuietly( input ); 530 } 531 532 if ( fixChecksums ) 533 { 534 fixChecksums( new File( targetPath, targetFilename ) ); 535 } 536 } 537 538 private void fixChecksums( File file ) 539 { 540 ChecksummedFile checksum = new ChecksummedFile( file ); 541 checksum.fixChecksums( algorithms ); 542 } 543 544 private void updateProjectMetadata( String targetPath, Date lastUpdatedTimestamp, String timestamp, int buildNumber, 545 boolean fixChecksums, ArtifactTransferRequest artifactTransferRequest ) 546 throws RepositoryMetadataException 547 { 548 List<String> availableVersions = new ArrayList<String>(); 549 String latestVersion = artifactTransferRequest.getVersion(); 550 551 File projectDir = new File( targetPath ).getParentFile(); 552 File projectMetadataFile = new File( projectDir, MetadataTools.MAVEN_METADATA ); 553 554 ArchivaRepositoryMetadata projectMetadata = getMetadata( projectMetadataFile ); 555 556 if ( projectMetadataFile.exists() ) 557 { 558 availableVersions = projectMetadata.getAvailableVersions(); 559 560 Collections.sort( availableVersions, VersionComparator.getInstance() ); 561 562 if ( !availableVersions.contains( artifactTransferRequest.getVersion() ) ) 563 { 564 availableVersions.add( artifactTransferRequest.getVersion() ); 565 } 566 567 latestVersion = availableVersions.get( availableVersions.size() - 1 ); 568 } 569 else 570 { 571 availableVersions.add( artifactTransferRequest.getVersion() ); 572 573 projectMetadata.setGroupId( artifactTransferRequest.getGroupId() ); 574 projectMetadata.setArtifactId( artifactTransferRequest.getArtifactId() ); 575 } 576 577 if ( projectMetadata.getGroupId() == null ) 578 { 579 projectMetadata.setGroupId( artifactTransferRequest.getGroupId() ); 580 } 581 582 if ( projectMetadata.getArtifactId() == null ) 583 { 584 projectMetadata.setArtifactId( artifactTransferRequest.getArtifactId() ); 585 } 586 587 projectMetadata.setLatestVersion( latestVersion ); 588 projectMetadata.setLastUpdatedTimestamp( lastUpdatedTimestamp ); 589 projectMetadata.setAvailableVersions( availableVersions ); 590 591 if ( !VersionUtil.isSnapshot( artifactTransferRequest.getVersion() ) ) 592 { 593 projectMetadata.setReleasedVersion( latestVersion ); 594 } 595 596 RepositoryMetadataWriter.write( projectMetadata, projectMetadataFile ); 597 598 if ( fixChecksums ) 599 { 600 fixChecksums( projectMetadataFile ); 601 } 602 } 603 604 public Boolean removeProjectVersion( String repositoryId, String namespace, String projectId, String version ) 605 throws ArchivaRestServiceException 606 { 607 // if not a generic we can use the standard way to delete artifact 608 if ( !VersionUtil.isGenericSnapshot( version ) ) 609 { 610 Artifact artifact = new Artifact( namespace, projectId, version ); 611 return deleteArtifact( artifact ); 612 } 613 614 if ( StringUtils.isEmpty( repositoryId ) ) 615 { 616 throw new ArchivaRestServiceException( "repositoryId cannot be null", 400, null ); 617 } 618 619 if ( !isAuthorizedToDeleteArtifacts( repositoryId ) ) 620 { 621 throw new ArchivaRestServiceException( "not authorized to delete artifacts", 403, null ); 622 } 623 624 if ( StringUtils.isEmpty( namespace ) ) 625 { 626 throw new ArchivaRestServiceException( "groupId cannot be null", 400, null ); 627 } 628 629 if ( StringUtils.isEmpty( projectId ) ) 630 { 631 throw new ArchivaRestServiceException( "artifactId cannot be null", 400, null ); 632 } 633 634 if ( StringUtils.isEmpty( version ) ) 635 { 636 throw new ArchivaRestServiceException( "version cannot be null", 400, null ); 637 } 638 639 RepositorySession repositorySession = repositorySessionFactory.createSession(); 640 641 try 642 { 643 ManagedRepositoryContent repository = repositoryFactory.getManagedRepositoryContent( repositoryId ); 644 645 VersionedReference ref = new VersionedReference(); 646 ref.setArtifactId( projectId ); 647 ref.setGroupId( namespace ); 648 ref.setVersion( version ); 649 650 repository.deleteVersion( ref ); 651 652 /* 653 ProjectReference projectReference = new ProjectReference(); 654 projectReference.setGroupId( namespace ); 655 projectReference.setArtifactId( projectId ); 656 657 repository.getVersions( ) 658 */ 659 660 ArtifactReference artifactReference = new ArtifactReference(); 661 artifactReference.setGroupId( namespace ); 662 artifactReference.setArtifactId( projectId ); 663 artifactReference.setVersion( version ); 664 665 MetadataRepository metadataRepository = repositorySession.getRepository(); 666 667 Set<ArtifactReference> related = repository.getRelatedArtifacts( artifactReference ); 668 log.debug( "related: {}", related ); 669 for ( ArtifactReference artifactRef : related ) 670 { 671 repository.deleteArtifact( artifactRef ); 672 } 673 674 Collection<ArtifactMetadata> artifacts = 675 metadataRepository.getArtifacts( repositoryId, namespace, projectId, version ); 676 677 for ( ArtifactMetadata artifactMetadata : artifacts ) 678 { 679 metadataRepository.removeArtifact( artifactMetadata, version ); 680 } 681 682 metadataRepository.removeProjectVersion( repositoryId, namespace, projectId, version ); 683 } 684 catch ( MetadataRepositoryException e ) 685 { 686 throw new ArchivaRestServiceException( "Repository exception: " + e.getMessage(), 500, e ); 687 } 688 catch ( MetadataResolutionException e ) 689 { 690 throw new ArchivaRestServiceException( "Repository exception: " + e.getMessage(), 500, e ); 691 } 692 catch ( RepositoryException e ) 693 { 694 throw new ArchivaRestServiceException( "Repository exception: " + e.getMessage(), 500, e ); 695 } 696 finally 697 { 698 699 repositorySession.save(); 700 701 repositorySession.close(); 702 } 703 704 return Boolean.TRUE; 705 } 706 707 public Boolean deleteArtifact( Artifact artifact ) 708 throws ArchivaRestServiceException 709 { 710 711 String repositoryId = artifact.getContext(); 712 if ( StringUtils.isEmpty( repositoryId ) ) 713 { 714 throw new ArchivaRestServiceException( "repositoryId cannot be null", 400, null ); 715 } 716 717 if ( !isAuthorizedToDeleteArtifacts( repositoryId ) ) 718 { 719 throw new ArchivaRestServiceException( "not authorized to delete artifacts", 403, null ); 720 } 721 722 if ( artifact == null ) 723 { 724 throw new ArchivaRestServiceException( "artifact cannot be null", 400, null ); 725 } 726 727 if ( StringUtils.isEmpty( artifact.getGroupId() ) ) 728 { 729 throw new ArchivaRestServiceException( "artifact.groupId cannot be null", 400, null ); 730 } 731 732 if ( StringUtils.isEmpty( artifact.getArtifactId() ) ) 733 { 734 throw new ArchivaRestServiceException( "artifact.artifactId cannot be null", 400, null ); 735 } 736 737 // TODO more control on artifact fields 738 739 boolean snapshotVersion = 740 VersionUtil.isSnapshot( artifact.getVersion() ) | VersionUtil.isGenericSnapshot( artifact.getVersion() ); 741 742 RepositorySession repositorySession = repositorySessionFactory.createSession(); 743 try 744 { 745 Date lastUpdatedTimestamp = Calendar.getInstance().getTime(); 746 747 TimeZone timezone = TimeZone.getTimeZone( "UTC" ); 748 DateFormat fmt = new SimpleDateFormat( "yyyyMMdd.HHmmss" ); 749 fmt.setTimeZone( timezone ); 750 ManagedRepository repoConfig = managedRepositoryAdmin.getManagedRepository( repositoryId ); 751 752 VersionedReference ref = new VersionedReference(); 753 ref.setArtifactId( artifact.getArtifactId() ); 754 ref.setGroupId( artifact.getGroupId() ); 755 ref.setVersion( artifact.getVersion() ); 756 757 ManagedRepositoryContent repository = repositoryFactory.getManagedRepositoryContent( repositoryId ); 758 759 ArtifactReference artifactReference = new ArtifactReference(); 760 artifactReference.setArtifactId( artifact.getArtifactId() ); 761 artifactReference.setGroupId( artifact.getGroupId() ); 762 artifactReference.setVersion( artifact.getVersion() ); 763 artifactReference.setClassifier( artifact.getClassifier() ); 764 artifactReference.setType( artifact.getPackaging() ); 765 766 MetadataRepository metadataRepository = repositorySession.getRepository(); 767 768 String path = repository.toMetadataPath( ref ); 769 770 if ( StringUtils.isNotBlank( artifact.getClassifier() ) ) 771 { 772 if ( StringUtils.isBlank( artifact.getPackaging() ) ) 773 { 774 throw new ArchivaRestServiceException( "You must configure a type/packaging when using classifier", 775 400, null ); 776 } 777 778 repository.deleteArtifact( artifactReference ); 779 780 } 781 else 782 { 783 784 int index = path.lastIndexOf( '/' ); 785 path = path.substring( 0, index ); 786 File targetPath = new File( repoConfig.getLocation(), path ); 787 788 if ( !targetPath.exists() ) 789 { 790 //throw new ContentNotFoundException( 791 // artifact.getGroupId() + ":" + artifact.getArtifactId() + ":" + artifact.getVersion() ); 792 log.warn( "targetPath {} not found skip file deletion", targetPath ); 793 } 794 795 // TODO: this should be in the storage mechanism so that it is all tied together 796 // delete from file system 797 if ( !snapshotVersion ) 798 { 799 repository.deleteVersion( ref ); 800 } 801 else 802 { 803 Set<ArtifactReference> related = repository.getRelatedArtifacts( artifactReference ); 804 log.debug( "related: {}", related ); 805 for ( ArtifactReference artifactRef : related ) 806 { 807 repository.deleteArtifact( artifactRef ); 808 } 809 } 810 File metadataFile = getMetadata( targetPath.getAbsolutePath() ); 811 ArchivaRepositoryMetadata metadata = getMetadata( metadataFile ); 812 813 updateMetadata( metadata, metadataFile, lastUpdatedTimestamp, artifact ); 814 } 815 Collection<ArtifactMetadata> artifacts = Collections.emptyList(); 816 817 if ( snapshotVersion ) 818 { 819 String baseVersion = VersionUtil.getBaseVersion( artifact.getVersion() ); 820 artifacts = 821 metadataRepository.getArtifacts( repositoryId, artifact.getGroupId(), artifact.getArtifactId(), 822 baseVersion ); 823 } 824 else 825 { 826 artifacts = 827 metadataRepository.getArtifacts( repositoryId, artifact.getGroupId(), artifact.getArtifactId(), 828 artifact.getVersion() ); 829 } 830 831 log.debug( "artifacts: {}", artifacts ); 832 833 if ( artifacts.isEmpty() ) 834 { 835 if ( !snapshotVersion ) 836 { 837 // verify metata repository doesn't contains anymore the version 838 Collection<String> projectVersions = 839 metadataRepository.getProjectVersions( repositoryId, artifact.getGroupId(), 840 artifact.getArtifactId() ); 841 842 if ( projectVersions.contains( artifact.getVersion() ) ) 843 { 844 log.warn( "artifact not found when deleted but version still here ! so force cleanup" ); 845 metadataRepository.removeProjectVersion( repositoryId, artifact.getGroupId(), 846 artifact.getArtifactId(), artifact.getVersion() ); 847 } 848 849 } 850 } 851 852 for ( ArtifactMetadata artifactMetadata : artifacts ) 853 { 854 855 // TODO: mismatch between artifact (snapshot) version and project (base) version here 856 if ( artifactMetadata.getVersion().equals( artifact.getVersion() ) ) 857 { 858 if ( StringUtils.isNotBlank( artifact.getClassifier() ) ) 859 { 860 if ( StringUtils.isBlank( artifact.getPackaging() ) ) 861 { 862 throw new ArchivaRestServiceException( 863 "You must configure a type/packaging when using classifier", 400, null ); 864 } 865 // cleanup facet which contains classifier information 866 MavenArtifactFacet mavenArtifactFacet = 867 (MavenArtifactFacet) artifactMetadata.getFacet( MavenArtifactFacet.FACET_ID ); 868 869 if ( StringUtils.equals( artifact.getClassifier(), mavenArtifactFacet.getClassifier() ) ) 870 { 871 artifactMetadata.removeFacet( MavenArtifactFacet.FACET_ID ); 872 String groupId = artifact.getGroupId(), artifactId = artifact.getArtifactId(), version = 873 artifact.getVersion(); 874 MavenArtifactFacet mavenArtifactFacetToCompare = new MavenArtifactFacet(); 875 mavenArtifactFacetToCompare.setClassifier( artifact.getClassifier() ); 876 metadataRepository.removeArtifact( repositoryId, groupId, artifactId, version, 877 mavenArtifactFacetToCompare ); 878 metadataRepository.save(); 879 } 880 881 } 882 else 883 { 884 if ( snapshotVersion ) 885 { 886 metadataRepository.removeArtifact( artifactMetadata, 887 VersionUtil.getBaseVersion( artifact.getVersion() ) ); 888 } 889 else 890 { 891 metadataRepository.removeArtifact( artifactMetadata.getRepositoryId(), 892 artifactMetadata.getNamespace(), 893 artifactMetadata.getProject(), artifact.getVersion(), 894 artifactMetadata.getId() ); 895 } 896 } 897 // TODO: move into the metadata repository proper - need to differentiate attachment of 898 // repository metadata to an artifact 899 for ( RepositoryListener listener : listeners ) 900 { 901 listener.deleteArtifact( metadataRepository, repository.getId(), 902 artifactMetadata.getNamespace(), artifactMetadata.getProject(), 903 artifactMetadata.getVersion(), artifactMetadata.getId() ); 904 } 905 906 triggerAuditEvent( repositoryId, path, AuditEvent.REMOVE_FILE ); 907 } 908 } 909 } 910 catch ( ContentNotFoundException e ) 911 { 912 throw new ArchivaRestServiceException( "Artifact does not exist: " + e.getMessage(), 400, e ); 913 } 914 catch ( RepositoryNotFoundException e ) 915 { 916 throw new ArchivaRestServiceException( "Target repository cannot be found: " + e.getMessage(), 400, e ); 917 } 918 catch ( RepositoryException e ) 919 { 920 throw new ArchivaRestServiceException( "Repository exception: " + e.getMessage(), 500, e ); 921 } 922 catch ( MetadataResolutionException e ) 923 { 924 throw new ArchivaRestServiceException( "Repository exception: " + e.getMessage(), 500, e ); 925 } 926 catch ( MetadataRepositoryException e ) 927 { 928 throw new ArchivaRestServiceException( "Repository exception: " + e.getMessage(), 500, e ); 929 } 930 catch ( RepositoryAdminException e ) 931 { 932 throw new ArchivaRestServiceException( "RepositoryAdmin exception: " + e.getMessage(), 500, e ); 933 } 934 finally 935 { 936 937 repositorySession.save(); 938 939 repositorySession.close(); 940 } 941 return Boolean.TRUE; 942 } 943 944 public Boolean deleteGroupId( String groupId, String repositoryId ) 945 throws ArchivaRestServiceException 946 { 947 if ( StringUtils.isEmpty( repositoryId ) ) 948 { 949 throw new ArchivaRestServiceException( "repositoryId cannot be null", 400, null ); 950 } 951 952 if ( !isAuthorizedToDeleteArtifacts( repositoryId ) ) 953 { 954 throw new ArchivaRestServiceException( "not authorized to delete artifacts", 403, null ); 955 } 956 957 if ( StringUtils.isEmpty( groupId ) ) 958 { 959 throw new ArchivaRestServiceException( "groupId cannot be null", 400, null ); 960 } 961 962 RepositorySession repositorySession = repositorySessionFactory.createSession(); 963 964 try 965 { 966 ManagedRepositoryContent repository = repositoryFactory.getManagedRepositoryContent( repositoryId ); 967 968 repository.deleteGroupId( groupId ); 969 970 MetadataRepository metadataRepository = repositorySession.getRepository(); 971 972 metadataRepository.removeNamespace( repositoryId, groupId ); 973 974 metadataRepository.save(); 975 } 976 catch ( MetadataRepositoryException e ) 977 { 978 log.error( e.getMessage(), e ); 979 throw new ArchivaRestServiceException( "Repository exception: " + e.getMessage(), 500, e ); 980 } 981 catch ( RepositoryException e ) 982 { 983 log.error( e.getMessage(), e ); 984 throw new ArchivaRestServiceException( "Repository exception: " + e.getMessage(), 500, e ); 985 } 986 finally 987 { 988 989 repositorySession.close(); 990 } 991 return true; 992 } 993 994 public Boolean deleteProject( String groupId, String projectId, String repositoryId ) 995 throws ArchivaRestServiceException 996 { 997 if ( StringUtils.isEmpty( repositoryId ) ) 998 { 999 throw new ArchivaRestServiceException( "repositoryId cannot be null", 400, null ); 1000 } 1001 1002 if ( !isAuthorizedToDeleteArtifacts( repositoryId ) ) 1003 { 1004 throw new ArchivaRestServiceException( "not authorized to delete artifacts", 403, null ); 1005 } 1006 1007 if ( StringUtils.isEmpty( groupId ) ) 1008 { 1009 throw new ArchivaRestServiceException( "groupId cannot be null", 400, null ); 1010 } 1011 1012 if ( StringUtils.isEmpty( projectId ) ) 1013 { 1014 throw new ArchivaRestServiceException( "artifactId cannot be null", 400, null ); 1015 } 1016 1017 RepositorySession repositorySession = repositorySessionFactory.createSession(); 1018 1019 try 1020 { 1021 ManagedRepositoryContent repository = repositoryFactory.getManagedRepositoryContent( repositoryId ); 1022 1023 repository.deleteProject( groupId, projectId ); 1024 } 1025 catch ( ContentNotFoundException e ) 1026 { 1027 log.warn( "skip ContentNotFoundException: {}", e.getMessage() ); 1028 } 1029 catch ( RepositoryException e ) 1030 { 1031 log.error( e.getMessage(), e ); 1032 throw new ArchivaRestServiceException( "Repository exception: " + e.getMessage(), 500, e ); 1033 } 1034 1035 try 1036 { 1037 1038 MetadataRepository metadataRepository = repositorySession.getRepository(); 1039 1040 metadataRepository.removeProject( repositoryId, groupId, projectId ); 1041 1042 metadataRepository.save(); 1043 } 1044 catch ( MetadataRepositoryException e ) 1045 { 1046 log.error( e.getMessage(), e ); 1047 throw new ArchivaRestServiceException( "Repository exception: " + e.getMessage(), 500, e ); 1048 } 1049 finally 1050 { 1051 1052 repositorySession.close(); 1053 } 1054 return true; 1055 1056 } 1057 1058 public Boolean isAuthorizedToDeleteArtifacts( String repoId ) 1059 throws ArchivaRestServiceException 1060 { 1061 String userName = 1062 getAuditInformation().getUser() == null ? "guest" : getAuditInformation().getUser().getUsername(); 1063 1064 try 1065 { 1066 return userRepositories.isAuthorizedToDeleteArtifacts( userName, repoId ); 1067 } 1068 catch ( ArchivaSecurityException e ) 1069 { 1070 throw new ArchivaRestServiceException( e.getMessage(), 1071 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e ); 1072 } 1073 } 1074 1075 public RepositoryScanStatistics scanRepositoryDirectoriesNow( String repositoryId ) 1076 throws ArchivaRestServiceException 1077 { 1078 long sinceWhen = RepositoryScanner.FRESH_SCAN; 1079 try 1080 { 1081 return repoScanner.scan( getManagedRepositoryAdmin().getManagedRepository( repositoryId ), sinceWhen ); 1082 } 1083 catch ( RepositoryScannerException e ) 1084 { 1085 log.error( e.getMessage(), e ); 1086 throw new ArchivaRestServiceException( "RepositoryScannerException exception: " + e.getMessage(), 500, e ); 1087 } 1088 catch ( RepositoryAdminException e ) 1089 { 1090 log.error( e.getMessage(), e ); 1091 throw new ArchivaRestServiceException( "RepositoryScannerException exception: " + e.getMessage(), 500, e ); 1092 } 1093 } 1094 1095 /** 1096 * Update artifact level metadata. Creates one if metadata does not exist after artifact deletion. 1097 * 1098 * @param metadata 1099 */ 1100 private void updateMetadata( ArchivaRepositoryMetadata metadata, File metadataFile, Date lastUpdatedTimestamp, 1101 Artifact artifact ) 1102 throws RepositoryMetadataException 1103 { 1104 List<String> availableVersions = new ArrayList<String>(); 1105 String latestVersion = ""; 1106 1107 if ( metadataFile.exists() ) 1108 { 1109 if ( metadata.getAvailableVersions() != null ) 1110 { 1111 availableVersions = metadata.getAvailableVersions(); 1112 1113 if ( availableVersions.size() > 0 ) 1114 { 1115 Collections.sort( availableVersions, VersionComparator.getInstance() ); 1116 1117 if ( availableVersions.contains( artifact.getVersion() ) ) 1118 { 1119 availableVersions.remove( availableVersions.indexOf( artifact.getVersion() ) ); 1120 } 1121 if ( availableVersions.size() > 0 ) 1122 { 1123 latestVersion = availableVersions.get( availableVersions.size() - 1 ); 1124 } 1125 } 1126 } 1127 } 1128 1129 if ( metadata.getGroupId() == null ) 1130 { 1131 metadata.setGroupId( artifact.getGroupId() ); 1132 } 1133 if ( metadata.getArtifactId() == null ) 1134 { 1135 metadata.setArtifactId( artifact.getArtifactId() ); 1136 } 1137 1138 if ( !VersionUtil.isSnapshot( artifact.getVersion() ) ) 1139 { 1140 if ( metadata.getReleasedVersion() != null && metadata.getReleasedVersion().equals( 1141 artifact.getVersion() ) ) 1142 { 1143 metadata.setReleasedVersion( latestVersion ); 1144 } 1145 } 1146 1147 metadata.setLatestVersion( latestVersion ); 1148 metadata.setLastUpdatedTimestamp( lastUpdatedTimestamp ); 1149 metadata.setAvailableVersions( availableVersions ); 1150 1151 RepositoryMetadataWriter.write( metadata, metadataFile ); 1152 ChecksummedFile checksum = new ChecksummedFile( metadataFile ); 1153 checksum.fixChecksums( algorithms ); 1154 } 1155 1156 public ManagedRepositoryAdmin getManagedRepositoryAdmin() 1157 { 1158 return managedRepositoryAdmin; 1159 } 1160 1161 public void setManagedRepositoryAdmin( ManagedRepositoryAdmin managedRepositoryAdmin ) 1162 { 1163 this.managedRepositoryAdmin = managedRepositoryAdmin; 1164 } 1165 1166 public RepositoryContentFactory getRepositoryFactory() 1167 { 1168 return repositoryFactory; 1169 } 1170 1171 public void setRepositoryFactory( RepositoryContentFactory repositoryFactory ) 1172 { 1173 this.repositoryFactory = repositoryFactory; 1174 } 1175 1176 public RepositorySessionFactory getRepositorySessionFactory() 1177 { 1178 return repositorySessionFactory; 1179 } 1180 1181 public void setRepositorySessionFactory( RepositorySessionFactory repositorySessionFactory ) 1182 { 1183 this.repositorySessionFactory = repositorySessionFactory; 1184 } 1185 1186 public List<RepositoryListener> getListeners() 1187 { 1188 return listeners; 1189 } 1190 1191 public void setListeners( List<RepositoryListener> listeners ) 1192 { 1193 this.listeners = listeners; 1194 } 1195 1196 public ArchivaAdministration getArchivaAdministration() 1197 { 1198 return archivaAdministration; 1199 } 1200 1201 public void setArchivaAdministration( ArchivaAdministration archivaAdministration ) 1202 { 1203 this.archivaAdministration = archivaAdministration; 1204 } 1205 } 1206 1207