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