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