001package org.apache.archiva.rest.services; 002/* 003 * Licensed to the Apache Software Foundation (ASF) under one 004 * or more contributor license agreements. See the NOTICE file 005 * distributed with this work for additional information 006 * regarding copyright ownership. The ASF licenses this file 007 * to you under the Apache License, Version 2.0 (the 008 * "License"); you may not use this file except in compliance 009 * with the License. You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, 014 * software distributed under the License is distributed on an 015 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 016 * KIND, either express or implied. See the License for the 017 * specific language governing permissions and limitations 018 * under the License. 019 */ 020 021import org.apache.archiva.admin.model.beans.ManagedRepository; 022import org.apache.archiva.common.utils.VersionComparator; 023import org.apache.archiva.common.utils.VersionUtil; 024import org.apache.archiva.dependency.tree.maven2.DependencyTreeBuilder; 025import org.apache.archiva.maven2.metadata.MavenMetadataReader; 026import org.apache.archiva.maven2.model.Artifact; 027import org.apache.archiva.maven2.model.TreeEntry; 028import org.apache.archiva.metadata.generic.GenericMetadataFacet; 029import org.apache.archiva.metadata.model.ArtifactMetadata; 030import org.apache.archiva.metadata.model.MetadataFacet; 031import org.apache.archiva.metadata.model.ProjectVersionMetadata; 032import org.apache.archiva.metadata.model.ProjectVersionReference; 033import org.apache.archiva.metadata.repository.*; 034import org.apache.archiva.metadata.repository.storage.maven2.ArtifactMetadataVersionComparator; 035import org.apache.archiva.metadata.repository.storage.maven2.MavenProjectFacet; 036import org.apache.archiva.model.ArchivaArtifact; 037import org.apache.archiva.model.ArchivaRepositoryMetadata; 038import org.apache.archiva.proxy.ProxyRegistry; 039import org.apache.archiva.proxy.model.RepositoryProxyHandler; 040import org.apache.archiva.components.cache.Cache; 041import org.apache.archiva.repository.ManagedRepositoryContent; 042import org.apache.archiva.repository.ReleaseScheme; 043import org.apache.archiva.repository.RepositoryException; 044import org.apache.archiva.repository.RepositoryNotFoundException; 045import org.apache.archiva.repository.metadata.base.MetadataTools; 046import org.apache.archiva.repository.storage.StorageAsset; 047import org.apache.archiva.repository.storage.StorageUtil; 048import org.apache.archiva.rest.api.model.*; 049import org.apache.archiva.rest.api.services.ArchivaRestServiceException; 050import org.apache.archiva.rest.api.services.BrowseService; 051import org.apache.archiva.rest.services.utils.ArtifactContentEntryComparator; 052import org.apache.archiva.security.ArchivaSecurityException; 053import org.apache.archiva.xml.XMLException; 054import org.apache.commons.collections4.CollectionUtils; 055import org.apache.commons.io.IOUtils; 056import org.apache.commons.lang3.StringUtils; 057import org.springframework.stereotype.Service; 058 059import javax.inject.Inject; 060import javax.inject.Named; 061import javax.ws.rs.core.Response; 062import java.io.IOException; 063import java.io.InputStream; 064import java.nio.charset.Charset; 065import java.nio.file.Files; 066import java.util.*; 067import java.util.jar.JarEntry; 068import java.util.jar.JarFile; 069import java.util.zip.ZipEntry; 070 071/** 072 * @author Olivier Lamy 073 * @since 1.4-M3 074 */ 075@Service( "browseService#rest" ) 076public class DefaultBrowseService 077 extends AbstractRestService 078 implements BrowseService 079{ 080 081 private final Charset ARTIFACT_CONTENT_ENCODING=Charset.forName( "UTF-8" ); 082 083 @Inject 084 private DependencyTreeBuilder dependencyTreeBuilder; 085 086 @Inject 087 ProxyRegistry proxyRegistry; 088 089 @Inject 090 @Named( value = "browse#versionMetadata" ) 091 private Cache<String, ProjectVersionMetadata> versionMetadataCache; 092 093 private ManagedRepositoryContent getManagedRepositoryContent(String id) throws RepositoryException 094 { 095 org.apache.archiva.repository.ManagedRepository repo = repositoryRegistry.getManagedRepository( id ); 096 if (repo==null) { 097 throw new RepositoryException( "Could not find repository "+id ); 098 } 099 return repo.getContent(); 100 } 101 102 @Override 103 public BrowseResult getRootGroups( String repositoryId ) 104 throws ArchivaRestServiceException 105 { 106 List<String> selectedRepos = getSelectedRepos( repositoryId ); 107 108 Set<String> namespaces = new LinkedHashSet<String>(); 109 110 // TODO: this logic should be optional, particularly remembering we want to keep this code simple 111 // it is located here to avoid the content repository implementation needing to do too much for what 112 // is essentially presentation code 113 Set<String> namespacesToCollapse = new LinkedHashSet<String>(); 114 RepositorySession repositorySession = null; 115 try 116 { 117 repositorySession = repositorySessionFactory.createSession(); 118 } 119 catch ( MetadataRepositoryException e ) 120 { 121 e.printStackTrace( ); 122 } 123 try 124 { 125 MetadataResolver metadataResolver = repositorySession.getResolver(); 126 127 for ( String repoId : selectedRepos ) 128 { 129 namespacesToCollapse.addAll( metadataResolver.resolveRootNamespaces( repositorySession, repoId ) ); 130 } 131 for ( String n : namespacesToCollapse ) 132 { 133 // TODO: check performance of this 134 namespaces.add( collapseNamespaces( repositorySession, metadataResolver, selectedRepos, n ) ); 135 } 136 } 137 catch ( MetadataResolutionException e ) 138 { 139 throw new ArchivaRestServiceException( e.getMessage(), 140 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e ); 141 } 142 finally 143 { 144 repositorySession.close(); 145 } 146 147 List<BrowseResultEntry> browseGroupResultEntries = new ArrayList<>( namespaces.size() ); 148 for ( String namespace : namespaces ) 149 { 150 browseGroupResultEntries.add( new BrowseResultEntry( namespace, false ) ); 151 } 152 153 Collections.sort( browseGroupResultEntries ); 154 return new BrowseResult( browseGroupResultEntries ); 155 } 156 157 @Override 158 public BrowseResult browseGroupId( String groupId, String repositoryId ) 159 throws ArchivaRestServiceException 160 { 161 List<String> selectedRepos = getSelectedRepos( repositoryId ); 162 163 Set<String> projects = new LinkedHashSet<>(); 164 165 RepositorySession repositorySession = null; 166 try 167 { 168 repositorySession = repositorySessionFactory.createSession(); 169 } 170 catch ( MetadataRepositoryException e ) 171 { 172 e.printStackTrace( ); 173 } 174 Set<String> namespaces; 175 try 176 { 177 MetadataResolver metadataResolver = repositorySession.getResolver(); 178 179 Set<String> namespacesToCollapse = new LinkedHashSet<>(); 180 for ( String repoId : selectedRepos ) 181 { 182 namespacesToCollapse.addAll( metadataResolver.resolveNamespaces( repositorySession, repoId, groupId ) ); 183 184 projects.addAll( metadataResolver.resolveProjects( repositorySession, repoId, groupId ) ); 185 } 186 187 // TODO: this logic should be optional, particularly remembering we want to keep this code simple 188 // it is located here to avoid the content repository implementation needing to do too much for what 189 // is essentially presentation code 190 namespaces = new LinkedHashSet<>(); 191 for ( String n : namespacesToCollapse ) 192 { 193 // TODO: check performance of this 194 namespaces.add( 195 collapseNamespaces( repositorySession, metadataResolver, selectedRepos, groupId + "." + n ) ); 196 } 197 } 198 catch ( MetadataResolutionException e ) 199 { 200 throw new ArchivaRestServiceException( e.getMessage(), 201 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e ); 202 } 203 finally 204 { 205 repositorySession.close(); 206 } 207 List<BrowseResultEntry> browseGroupResultEntries = new ArrayList<>( namespaces.size() + projects.size() ); 208 for ( String namespace : namespaces ) 209 { 210 browseGroupResultEntries.add( new BrowseResultEntry( namespace, false ).groupId( namespace ) ); 211 } 212 for ( String project : projects ) 213 { 214 browseGroupResultEntries.add( 215 new BrowseResultEntry( groupId + '.' + project, true ).groupId( groupId ).artifactId( project ) ); 216 } 217 Collections.sort( browseGroupResultEntries ); 218 return new BrowseResult( browseGroupResultEntries ); 219 220 } 221 222 @Override 223 public VersionsList getVersionsList( String groupId, String artifactId, String repositoryId ) 224 throws ArchivaRestServiceException 225 { 226 List<String> selectedRepos = getSelectedRepos( repositoryId ); 227 228 try 229 { 230 Collection<String> versions = getVersions( selectedRepos, groupId, artifactId ); 231 return new VersionsList( new ArrayList<>( versions ) ); 232 } 233 catch ( MetadataResolutionException e ) 234 { 235 throw new ArchivaRestServiceException( e.getMessage(), 236 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e ); 237 } 238 239 } 240 241 private Collection<String> getVersions( List<String> selectedRepos, String groupId, String artifactId ) 242 throws MetadataResolutionException 243 244 { 245 RepositorySession repositorySession = null; 246 try 247 { 248 repositorySession = repositorySessionFactory.createSession(); 249 } 250 catch ( MetadataRepositoryException e ) 251 { 252 e.printStackTrace( ); 253 } 254 try 255 { 256 MetadataResolver metadataResolver = repositorySession.getResolver(); 257 258 Set<String> versions = new LinkedHashSet<String>(); 259 260 for ( String repoId : selectedRepos ) 261 { 262 Collection<String> projectVersions = 263 metadataResolver.resolveProjectVersions( repositorySession, repoId, groupId, artifactId ); 264 versions.addAll( projectVersions ); 265 } 266 267 List<String> sortedVersions = new ArrayList<>( versions ); 268 269 Collections.sort( sortedVersions, VersionComparator.getInstance() ); 270 271 return sortedVersions; 272 } 273 finally 274 { 275 repositorySession.close(); 276 } 277 } 278 279 @Override 280 public ProjectVersionMetadata getProjectMetadata( String groupId, String artifactId, String version, 281 String repositoryId ) 282 throws ArchivaRestServiceException 283 { 284 List<String> selectedRepos = getSelectedRepos( repositoryId ); 285 286 RepositorySession repositorySession = null; 287 try 288 { 289 repositorySession = repositorySessionFactory.createSession(); 290 291 MetadataResolver metadataResolver = repositorySession.getResolver(); 292 293 ProjectVersionMetadata versionMetadata = null; 294 for ( String repoId : selectedRepos ) 295 { 296 if ( versionMetadata == null || versionMetadata.isIncomplete() ) 297 { 298 try 299 { 300 ProjectVersionMetadata versionMetadataTmp = 301 metadataResolver.resolveProjectVersion( repositorySession, repoId, groupId, artifactId, 302 version ); 303 304 if ( versionMetadata == null && versionMetadataTmp != null ) 305 { 306 versionMetadata = versionMetadataTmp; 307 } 308 309 310 } 311 catch ( MetadataResolutionException e ) 312 { 313 log.warn( "Skipping invalid metadata while compiling shared model for {}:{} in repo {}: {}", 314 groupId, artifactId, repoId, e.getMessage() ); 315 } 316 } 317 } 318 319 return versionMetadata; 320 } catch (MetadataRepositoryException e) { 321 throw new ArchivaRestServiceException(e.getMessage(), e); 322 } finally 323 { 324 if ( repositorySession != null ) 325 { 326 repositorySession.close(); 327 } 328 } 329 330 } 331 332 @Override 333 public ProjectVersionMetadata getProjectVersionMetadata( String groupId, String artifactId, String repositoryId ) 334 throws ArchivaRestServiceException 335 { 336 337 List<String> selectedRepos = getSelectedRepos( repositoryId ); 338 339 RepositorySession repositorySession = null; 340 try 341 { 342 343 Collection<String> projectVersions = getVersions( selectedRepos, groupId, artifactId ); 344 345 repositorySession = repositorySessionFactory.createSession(); 346 347 MetadataResolver metadataResolver = repositorySession.getResolver(); 348 349 ProjectVersionMetadata sharedModel = new ProjectVersionMetadata(); 350 351 MavenProjectFacet mavenFacet = new MavenProjectFacet(); 352 mavenFacet.setGroupId( groupId ); 353 mavenFacet.setArtifactId( artifactId ); 354 sharedModel.addFacet( mavenFacet ); 355 356 boolean isFirstVersion = true; 357 358 for ( String version : projectVersions ) 359 { 360 ProjectVersionMetadata versionMetadata = null; 361 for ( String repoId : selectedRepos ) 362 { 363 if ( versionMetadata == null || versionMetadata.isIncomplete() ) 364 { 365 try 366 { 367 ProjectVersionMetadata projectVersionMetadataResolved = null; 368 boolean useCache = !StringUtils.endsWith( version, VersionUtil.SNAPSHOT ); 369 String cacheKey = null; 370 boolean cacheToUpdate = false; 371 // FIXME a bit maven centric!!! 372 // not a snapshot so get it from cache 373 if ( useCache ) 374 { 375 cacheKey = repoId + groupId + artifactId + version; 376 projectVersionMetadataResolved = versionMetadataCache.get( cacheKey ); 377 } 378 if ( useCache && projectVersionMetadataResolved != null ) 379 { 380 versionMetadata = projectVersionMetadataResolved; 381 } 382 else 383 { 384 projectVersionMetadataResolved = 385 metadataResolver.resolveProjectVersion( repositorySession, repoId, groupId, 386 artifactId, version ); 387 versionMetadata = projectVersionMetadataResolved; 388 cacheToUpdate = true; 389 } 390 391 if ( useCache && cacheToUpdate ) 392 { 393 versionMetadataCache.put( cacheKey, projectVersionMetadataResolved ); 394 } 395 396 } 397 catch ( MetadataResolutionException e ) 398 { 399 log.error( "Skipping invalid metadata while compiling shared model for " + groupId + ":" 400 + artifactId + " in repo " + repoId + ": " + e.getMessage() ); 401 } 402 } 403 } 404 405 if ( versionMetadata == null ) 406 { 407 continue; 408 } 409 410 if ( isFirstVersion ) 411 { 412 sharedModel = versionMetadata; 413 sharedModel.setId( null ); 414 } 415 else 416 { 417 MavenProjectFacet versionMetadataMavenFacet = 418 (MavenProjectFacet) versionMetadata.getFacet( MavenProjectFacet.FACET_ID ); 419 if ( versionMetadataMavenFacet != null ) 420 { 421 if ( mavenFacet.getPackaging() != null // 422 && !StringUtils.equalsIgnoreCase( mavenFacet.getPackaging(), 423 versionMetadataMavenFacet.getPackaging() ) ) 424 { 425 mavenFacet.setPackaging( null ); 426 } 427 } 428 429 if ( StringUtils.isEmpty( sharedModel.getName() ) // 430 && !StringUtils.isEmpty( versionMetadata.getName() ) ) 431 { 432 sharedModel.setName( versionMetadata.getName() ); 433 } 434 435 if ( sharedModel.getDescription() != null // 436 && !StringUtils.equalsIgnoreCase( sharedModel.getDescription(), 437 versionMetadata.getDescription() ) ) 438 { 439 sharedModel.setDescription( StringUtils.isNotEmpty( versionMetadata.getDescription() ) 440 ? versionMetadata.getDescription() 441 : "" ); 442 } 443 444 if ( sharedModel.getIssueManagement() != null // 445 && versionMetadata.getIssueManagement() != null // 446 && !StringUtils.equalsIgnoreCase( sharedModel.getIssueManagement().getUrl(), 447 versionMetadata.getIssueManagement().getUrl() ) ) 448 { 449 sharedModel.setIssueManagement( versionMetadata.getIssueManagement() ); 450 } 451 452 if ( sharedModel.getCiManagement() != null // 453 && versionMetadata.getCiManagement() != null // 454 && !StringUtils.equalsIgnoreCase( sharedModel.getCiManagement().getUrl(), 455 versionMetadata.getCiManagement().getUrl() ) ) 456 { 457 sharedModel.setCiManagement( versionMetadata.getCiManagement() ); 458 } 459 460 if ( sharedModel.getOrganization() != null // 461 && versionMetadata.getOrganization() != null // 462 && !StringUtils.equalsIgnoreCase( sharedModel.getOrganization().getName(), 463 versionMetadata.getOrganization().getName() ) ) 464 { 465 sharedModel.setOrganization( versionMetadata.getOrganization() ); 466 } 467 468 if ( sharedModel.getUrl() != null // 469 && !StringUtils.equalsIgnoreCase( sharedModel.getUrl(), versionMetadata.getUrl() ) ) 470 { 471 sharedModel.setUrl( versionMetadata.getUrl() ); 472 } 473 } 474 475 isFirstVersion = false; 476 } 477 return sharedModel; 478 } 479 catch (MetadataResolutionException | MetadataRepositoryException e ) 480 { 481 throw new ArchivaRestServiceException( e.getMessage(), 482 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e ); 483 } 484 finally 485 { 486 if ( repositorySession != null ) 487 { 488 repositorySession.close(); 489 } 490 } 491 } 492 493 @Override 494 public List<TreeEntry> getTreeEntries( String groupId, String artifactId, String version, String repositoryId ) 495 throws ArchivaRestServiceException 496 { 497 List<String> selectedRepos = getSelectedRepos( repositoryId ); 498 499 try 500 { 501 return dependencyTreeBuilder.buildDependencyTree( selectedRepos, groupId, artifactId, version ); 502 } 503 catch ( Exception e ) 504 { 505 log.error( e.getMessage(), e ); 506 } 507 508 return Collections.emptyList(); 509 } 510 511 @Override 512 public List<ManagedRepository> getUserRepositories() 513 throws ArchivaRestServiceException 514 { 515 try 516 { 517 return userRepositories.getAccessibleRepositories( getPrincipal() ); 518 } 519 catch ( ArchivaSecurityException e ) 520 { 521 throw new ArchivaRestServiceException( "repositories.read.observable.error", 522 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e ); 523 } 524 } 525 526 @Override 527 public List<ManagedRepository> getUserManagableRepositories() throws ArchivaRestServiceException { 528 try 529 { 530 return userRepositories.getManagableRepositories( getPrincipal() ); 531 } 532 catch ( ArchivaSecurityException e ) 533 { 534 throw new ArchivaRestServiceException( "repositories.read.managable.error", 535 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e ); 536 } 537 } 538 539 @Override 540 public List<Artifact> getDependees( String groupId, String artifactId, String version, String repositoryId ) 541 throws ArchivaRestServiceException 542 { 543 List<ProjectVersionReference> references = new ArrayList<>(); 544 // TODO: what if we get duplicates across repositories? 545 RepositorySession repositorySession = null; 546 try 547 { 548 repositorySession = repositorySessionFactory.createSession(); 549 } 550 catch ( MetadataRepositoryException e ) 551 { 552 e.printStackTrace( ); 553 } 554 try 555 { 556 MetadataResolver metadataResolver = repositorySession.getResolver(); 557 for ( String repoId : getObservableRepos() ) 558 { 559 // TODO: what about if we want to see this irrespective of version? 560 references.addAll( 561 metadataResolver.resolveProjectReferences( repositorySession, repoId, groupId, artifactId, 562 version ) ); 563 } 564 } 565 catch ( MetadataResolutionException e ) 566 { 567 throw new ArchivaRestServiceException( e.getMessage(), 568 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e ); 569 } 570 finally 571 { 572 repositorySession.close(); 573 } 574 575 List<Artifact> artifacts = new ArrayList<>( references.size() ); 576 577 for ( ProjectVersionReference projectVersionReference : references ) 578 { 579 artifacts.add( new Artifact( projectVersionReference.getNamespace(), projectVersionReference.getProjectId(), 580 projectVersionReference.getProjectVersion() ) ); 581 } 582 return artifacts; 583 } 584 585 @Override 586 public List<Entry> getMetadatas( String groupId, String artifactId, String version, String repositoryId ) 587 throws ArchivaRestServiceException 588 { 589 ProjectVersionMetadata projectVersionMetadata = 590 getProjectMetadata( groupId, artifactId, version, repositoryId ); 591 if ( projectVersionMetadata == null ) 592 { 593 return Collections.emptyList(); 594 } 595 MetadataFacet metadataFacet = projectVersionMetadata.getFacet( GenericMetadataFacet.FACET_ID ); 596 597 if ( metadataFacet == null ) 598 { 599 return Collections.emptyList(); 600 } 601 Map<String, String> map = metadataFacet.toProperties(); 602 List<Entry> entries = new ArrayList<>( map.size() ); 603 604 for ( Map.Entry<String, String> entry : map.entrySet() ) 605 { 606 entries.add( new Entry( entry.getKey(), entry.getValue() ) ); 607 } 608 609 return entries; 610 } 611 612 @Override 613 public Boolean addMetadata( String groupId, String artifactId, String version, String key, String value, 614 String repositoryId ) 615 throws ArchivaRestServiceException 616 { 617 ProjectVersionMetadata projectVersionMetadata = 618 getProjectMetadata( groupId, artifactId, version, repositoryId ); 619 620 if ( projectVersionMetadata == null ) 621 { 622 return Boolean.FALSE; 623 } 624 625 Map<String, String> properties = new HashMap<>(); 626 627 MetadataFacet metadataFacet = projectVersionMetadata.getFacet( GenericMetadataFacet.FACET_ID ); 628 629 if ( metadataFacet != null && metadataFacet.toProperties() != null ) 630 { 631 properties.putAll( metadataFacet.toProperties() ); 632 } 633 else 634 { 635 metadataFacet = new GenericMetadataFacet(); 636 } 637 638 properties.put( key, value ); 639 640 metadataFacet.fromProperties( properties ); 641 642 projectVersionMetadata.addFacet( metadataFacet ); 643 644 RepositorySession repositorySession = null; 645 try 646 { 647 repositorySession = repositorySessionFactory.createSession(); 648 } 649 catch ( MetadataRepositoryException e ) 650 { 651 e.printStackTrace( ); 652 } 653 654 try 655 { 656 MetadataRepository metadataRepository = repositorySession.getRepository(); 657 658 metadataRepository.updateProjectVersion(repositorySession , repositoryId, groupId, artifactId, projectVersionMetadata ); 659 660 repositorySession.save(); 661 } 662 catch (MetadataRepositoryException | MetadataSessionException e ) 663 { 664 log.error( e.getMessage(), e ); 665 throw new ArchivaRestServiceException( e.getMessage(), 666 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e ); 667 } 668 finally 669 { 670 repositorySession.close(); 671 } 672 return Boolean.TRUE; 673 } 674 675 @Override 676 public Boolean deleteMetadata( String groupId, String artifactId, String version, String key, String repositoryId ) 677 throws ArchivaRestServiceException 678 { 679 ProjectVersionMetadata projectVersionMetadata = 680 getProjectMetadata( groupId, artifactId, version, repositoryId ); 681 682 if ( projectVersionMetadata == null ) 683 { 684 return Boolean.FALSE; 685 } 686 687 GenericMetadataFacet metadataFacet = 688 (GenericMetadataFacet) projectVersionMetadata.getFacet( GenericMetadataFacet.FACET_ID ); 689 690 if ( metadataFacet != null && metadataFacet.toProperties() != null ) 691 { 692 Map<String, String> properties = metadataFacet.toProperties(); 693 properties.remove( key ); 694 metadataFacet.setAdditionalProperties( properties ); 695 } 696 else 697 { 698 return Boolean.TRUE; 699 } 700 701 RepositorySession repositorySession = null; 702 try 703 { 704 repositorySession = repositorySessionFactory.createSession(); 705 } 706 catch ( MetadataRepositoryException e ) 707 { 708 e.printStackTrace( ); 709 } 710 711 try 712 { 713 MetadataRepository metadataRepository = repositorySession.getRepository(); 714 715 metadataRepository.updateProjectVersion(repositorySession , repositoryId, groupId, artifactId, projectVersionMetadata ); 716 717 repositorySession.save(); 718 } 719 catch (MetadataRepositoryException | MetadataSessionException e ) 720 { 721 log.error( e.getMessage(), e ); 722 throw new ArchivaRestServiceException( e.getMessage(), 723 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e ); 724 } 725 finally 726 { 727 repositorySession.close(); 728 } 729 return Boolean.TRUE; 730 } 731 732 @Override 733 public List<ArtifactContentEntry> getArtifactContentEntries( String groupId, String artifactId, String version, 734 String classifier, String type, String path, 735 String repositoryId ) 736 throws ArchivaRestServiceException 737 { 738 List<String> selectedRepos = getSelectedRepos( repositoryId ); 739 try 740 { 741 for ( String repoId : selectedRepos ) 742 { 743 744 ManagedRepositoryContent managedRepositoryContent = 745 getManagedRepositoryContent( repoId ); 746 ArchivaArtifact archivaArtifact = new ArchivaArtifact( groupId, artifactId, version, classifier, 747 StringUtils.isEmpty( type ) ? "jar" : type, 748 repoId ); 749 StorageAsset file = managedRepositoryContent.toFile( archivaArtifact ); 750 if ( file.exists() ) 751 { 752 return readFileEntries( file, path, repoId ); 753 } 754 } 755 } 756 catch ( IOException e ) 757 { 758 log.error( e.getMessage(), e ); 759 throw new ArchivaRestServiceException( e.getMessage(), 760 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e ); 761 } 762 catch ( RepositoryNotFoundException e ) 763 { 764 log.error( e.getMessage(), e ); 765 throw new ArchivaRestServiceException( e.getMessage(), 766 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e ); 767 } 768 catch ( RepositoryException e ) 769 { 770 log.error( e.getMessage(), e ); 771 throw new ArchivaRestServiceException( e.getMessage(), 772 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e ); 773 } 774 return Collections.emptyList(); 775 } 776 777 @Override 778 public List<Artifact> getArtifactDownloadInfos( String groupId, String artifactId, String version, 779 String repositoryId ) 780 throws ArchivaRestServiceException 781 { 782 List<String> selectedRepos = getSelectedRepos( repositoryId ); 783 784 List<Artifact> artifactDownloadInfos = new ArrayList<>(); 785 786 try (RepositorySession session = repositorySessionFactory.createSession()) 787 { 788 MetadataResolver metadataResolver = session.getResolver(); 789 for ( String repoId : selectedRepos ) 790 { 791 List<ArtifactMetadata> artifacts = new ArrayList<>( 792 metadataResolver.resolveArtifacts( session, repoId, groupId, artifactId, version ) ); 793 Collections.sort( artifacts, ArtifactMetadataVersionComparator.INSTANCE ); 794 if ( artifacts != null && !artifacts.isEmpty() ) 795 { 796 return buildArtifacts( artifacts, repoId ); 797 } 798 } 799 } 800 catch ( MetadataResolutionException e ) 801 { 802 log.error( e.getMessage(), e ); 803 throw new ArchivaRestServiceException( e.getMessage(), 804 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e ); 805 } 806 catch ( MetadataRepositoryException e ) 807 { 808 e.printStackTrace( ); 809 } 810 811 return artifactDownloadInfos; 812 } 813 814 @Override 815 public ArtifactContent getArtifactContentText( String groupId, String artifactId, String version, String classifier, 816 String type, String path, String repositoryId ) 817 throws ArchivaRestServiceException 818 { 819 List<String> selectedRepos = getSelectedRepos( repositoryId ); 820 try 821 { 822 for ( String repoId : selectedRepos ) 823 { 824 825 ManagedRepositoryContent managedRepositoryContent = null; 826 try 827 { 828 managedRepositoryContent = getManagedRepositoryContent( repoId ); 829 } 830 catch ( RepositoryException e ) 831 { 832 log.error("No repository content found for "+repoId); 833 continue; 834 } 835 ArchivaArtifact archivaArtifact = new ArchivaArtifact( groupId, artifactId, version, classifier, 836 StringUtils.isEmpty( type ) ? "jar" : type, 837 repoId ); 838 StorageAsset file = managedRepositoryContent.toFile( archivaArtifact ); 839 if ( !file.exists() ) 840 { 841 log.debug( "file: {} not exists for repository: {} try next repository", file, repoId ); 842 continue; 843 } 844 if ( StringUtils.isNotBlank( path ) ) 845 { 846 // zip entry of the path -> path must a real file entry of the archive 847 StorageUtil.PathInformation pathInfo = StorageUtil.getAssetDataAsPath(file); 848 JarFile jarFile = new JarFile( pathInfo.getPath().toFile()); 849 ZipEntry zipEntry = jarFile.getEntry( path ); 850 try (InputStream inputStream = jarFile.getInputStream( zipEntry )) 851 { 852 return new ArtifactContent( IOUtils.toString( inputStream, ARTIFACT_CONTENT_ENCODING ), repoId ); 853 } 854 finally 855 { 856 closeQuietly( jarFile ); 857 if (pathInfo.isTmpFile()) { 858 Files.deleteIfExists(pathInfo.getPath()); 859 } 860 } 861 } 862 try(InputStream readStream = file.getReadStream()) { 863 return new ArtifactContent(IOUtils.toString(readStream, ARTIFACT_CONTENT_ENCODING), repoId); 864 } 865 } 866 } 867 catch ( IOException e ) 868 { 869 log.error( e.getMessage(), e ); 870 throw new ArchivaRestServiceException( e.getMessage(), 871 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e ); 872 } 873 log.debug( "artifact: {}:{}:{}:{}:{} not found", groupId, artifactId, version, classifier, type ); 874 // 404 ? 875 return new ArtifactContent(); 876 } 877 878 @Override 879 public Boolean artifactAvailable( String groupId, String artifactId, String version, String classifier, 880 String repositoryId ) 881 throws ArchivaRestServiceException 882 { 883 List<String> selectedRepos = getSelectedRepos( repositoryId ); 884 885 boolean snapshot = VersionUtil.isSnapshot( version ); 886 887 try 888 { 889 for ( String repoId : selectedRepos ) 890 { 891 892 org.apache.archiva.repository.ManagedRepository managedRepo = repositoryRegistry.getManagedRepository(repoId); 893 if (!proxyRegistry.hasHandler(managedRepo.getType())) { 894 throw new RepositoryException( "No proxy handler found for repository type "+managedRepo.getType()); 895 } 896 RepositoryProxyHandler proxyHandler = proxyRegistry.getHandler(managedRepo.getType()).get(0); 897 if ( ( snapshot && !managedRepo.getActiveReleaseSchemes().contains(ReleaseScheme.SNAPSHOT) ) || ( !snapshot 898 && managedRepo.getActiveReleaseSchemes().contains(ReleaseScheme.SNAPSHOT) ) ) 899 { 900 continue; 901 } 902 ManagedRepositoryContent managedRepositoryContent = getManagedRepositoryContent( repoId ); 903 904 // FIXME default to jar which can be wrong for war zip etc.... 905 ArchivaArtifact archivaArtifact = new ArchivaArtifact( groupId, artifactId, version, 906 StringUtils.isEmpty( classifier ) 907 ? "" 908 : classifier, "jar", repoId ); 909 StorageAsset file = managedRepositoryContent.toFile( archivaArtifact ); 910 911 if ( file != null && file.exists() ) 912 { 913 return true; 914 } 915 916 // in case of SNAPSHOT we can have timestamped version locally ! 917 if ( StringUtils.endsWith( version, VersionUtil.SNAPSHOT ) ) 918 { 919 StorageAsset metadataFile = file.getStorage().getAsset(file.getParent().getPath()+"/"+MetadataTools.MAVEN_METADATA ); 920 if ( metadataFile.exists() ) 921 { 922 try 923 { 924 ArchivaRepositoryMetadata archivaRepositoryMetadata = 925 MavenMetadataReader.read( metadataFile ); 926 int buildNumber = archivaRepositoryMetadata.getSnapshotVersion().getBuildNumber(); 927 String timeStamp = archivaRepositoryMetadata.getSnapshotVersion().getTimestamp(); 928 // rebuild file name with timestamped version and build number 929 String timeStampFileName = new StringBuilder( artifactId ).append( '-' ) // 930 .append( StringUtils.remove( version, "-" + VersionUtil.SNAPSHOT ) ) // 931 .append( '-' ).append( timeStamp ) // 932 .append( '-' ).append( Integer.toString( buildNumber ) ) // 933 .append( ( StringUtils.isEmpty( classifier ) ? "" : "-" + classifier ) ) // 934 .append( ".jar" ).toString(); 935 936 StorageAsset timeStampFile = file.getStorage().getAsset(file.getParent().getPath() + "/" + timeStampFileName ); 937 log.debug( "try to find timestamped snapshot version file: {}", timeStampFile.getPath() ); 938 if ( timeStampFile.exists() ) 939 { 940 return true; 941 } 942 } 943 catch (XMLException | IOException e ) 944 { 945 log.warn( "skip fail to find timestamped snapshot file: {}", e.getMessage() ); 946 } 947 } 948 } 949 950 String path = managedRepositoryContent.toPath( archivaArtifact ); 951 952 file = proxyHandler.fetchFromProxies( managedRepositoryContent.getRepository(), path ); 953 954 if ( file != null && file.exists() ) 955 { 956 // download pom now 957 String pomPath = StringUtils.substringBeforeLast( path, ".jar" ) + ".pom"; 958 proxyHandler.fetchFromProxies( managedRepositoryContent.getRepository(), pomPath ); 959 return true; 960 } 961 } 962 } catch ( RepositoryException e ) 963 { 964 log.error( e.getMessage(), e ); 965 throw new ArchivaRestServiceException( e.getMessage(), 966 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e ); 967 } 968 969 return false; 970 } 971 972 @Override 973 public Boolean artifactAvailable( String groupId, String artifactId, String version, String repositoryId ) 974 throws ArchivaRestServiceException 975 { 976 return artifactAvailable( groupId, artifactId, version, null, repositoryId ); 977 } 978 979 @Override 980 public List<Artifact> getArtifacts( String repositoryId ) 981 throws ArchivaRestServiceException 982 { 983 RepositorySession repositorySession = null; 984 try 985 { 986 repositorySession = repositorySessionFactory.createSession(); 987 } 988 catch ( MetadataRepositoryException e ) 989 { 990 e.printStackTrace( ); 991 } 992 try 993 { 994 List<ArtifactMetadata> artifactMetadatas = repositorySession.getRepository().getArtifacts(repositorySession , repositoryId ); 995 return buildArtifacts( artifactMetadatas, repositoryId ); 996 } 997 catch ( MetadataRepositoryException e ) 998 { 999 throw new ArchivaRestServiceException( e.getMessage(), e ); 1000 } 1001 finally 1002 { 1003 repositorySession.close(); 1004 } 1005 } 1006 1007 @Override 1008 public List<Artifact> getArtifactsByProjectVersionMetadata( String key, String value, String repositoryId ) 1009 throws ArchivaRestServiceException 1010 { 1011 RepositorySession repositorySession = null; 1012 try 1013 { 1014 repositorySession = repositorySessionFactory.createSession(); 1015 } 1016 catch ( MetadataRepositoryException e ) 1017 { 1018 e.printStackTrace( ); 1019 } 1020 try 1021 { 1022 List<ArtifactMetadata> artifactMetadatas = repositorySession.getRepository().getArtifactsByProjectVersionFacet(repositorySession , key, value, repositoryId ); 1023 return buildArtifacts( artifactMetadatas, repositoryId ); 1024 } 1025 catch ( MetadataRepositoryException e ) 1026 { 1027 throw new ArchivaRestServiceException( e.getMessage(), e ); 1028 } 1029 finally 1030 { 1031 repositorySession.close(); 1032 } 1033 } 1034 1035 @Override 1036 public List<Artifact> getArtifactsByMetadata( String key, String value, String repositoryId ) 1037 throws ArchivaRestServiceException 1038 { 1039 RepositorySession repositorySession = null; 1040 try 1041 { 1042 repositorySession = repositorySessionFactory.createSession(); 1043 } 1044 catch ( MetadataRepositoryException e ) 1045 { 1046 e.printStackTrace( ); 1047 } 1048 try 1049 { 1050 List<ArtifactMetadata> artifactMetadatas = repositorySession.getRepository().getArtifactsByAttribute(repositorySession , key, value, repositoryId ); 1051 return buildArtifacts( artifactMetadatas, repositoryId ); 1052 } 1053 catch ( MetadataRepositoryException e ) 1054 { 1055 throw new ArchivaRestServiceException( e.getMessage(), e ); 1056 } 1057 finally 1058 { 1059 repositorySession.close(); 1060 } 1061 } 1062 1063 @Override 1064 public List<Artifact> getArtifactsByProperty( String key, String value, String repositoryId ) 1065 throws ArchivaRestServiceException 1066 { 1067 RepositorySession repositorySession = null; 1068 try 1069 { 1070 repositorySession = repositorySessionFactory.createSession(); 1071 } 1072 catch ( MetadataRepositoryException e ) 1073 { 1074 e.printStackTrace( ); 1075 } 1076 try 1077 { 1078 List<ArtifactMetadata> artifactMetadatas = repositorySession.getRepository().getArtifactsByProjectVersionAttribute(repositorySession , key, value, repositoryId ); 1079 return buildArtifacts( artifactMetadatas, repositoryId ); 1080 } 1081 catch ( MetadataRepositoryException e ) 1082 { 1083 throw new ArchivaRestServiceException( e.getMessage(), e ); 1084 } 1085 finally 1086 { 1087 repositorySession.close(); 1088 } 1089 } 1090 1091 @Override 1092 public Boolean importMetadata( MetadataAddRequest metadataAddRequest, String repositoryId ) 1093 throws ArchivaRestServiceException 1094 { 1095 boolean result = true; 1096 for ( Map.Entry<String, String> metadata : metadataAddRequest.getMetadatas().entrySet() ) 1097 { 1098 result = addMetadata( metadataAddRequest.getGroupId(), metadataAddRequest.getArtifactId(), 1099 metadataAddRequest.getVersion(), metadata.getKey(), metadata.getValue(), 1100 repositoryId ); 1101 if ( !result ) 1102 { 1103 break; 1104 } 1105 } 1106 return result; 1107 } 1108 1109 @Override 1110 public List<Artifact> searchArtifacts( String text, String repositoryId, Boolean exact ) 1111 throws ArchivaRestServiceException 1112 { 1113 try(RepositorySession repositorySession = repositorySessionFactory.createSession()) 1114 { 1115 List<ArtifactMetadata> artifactMetadatas = 1116 repositorySession.getRepository().searchArtifacts(repositorySession , repositoryId, text, exact == null ? false : exact ); 1117 return buildArtifacts( artifactMetadatas, repositoryId ); 1118 } 1119 catch ( MetadataRepositoryException e ) 1120 { 1121 throw new ArchivaRestServiceException( e.getMessage(), e ); 1122 } 1123 } 1124 1125 @Override 1126 public List<Artifact> searchArtifacts( String key, String text, String repositoryId, Boolean exact ) 1127 throws ArchivaRestServiceException 1128 { 1129 RepositorySession repositorySession = null; 1130 try 1131 { 1132 repositorySession = repositorySessionFactory.createSession(); 1133 } 1134 catch ( MetadataRepositoryException e ) 1135 { 1136 e.printStackTrace( ); 1137 } 1138 try 1139 { 1140 List<ArtifactMetadata> artifactMetadatas = 1141 repositorySession.getRepository().searchArtifacts(repositorySession , repositoryId, key, text, exact == null ? false : exact ); 1142 return buildArtifacts( artifactMetadatas, repositoryId ); 1143 } 1144 catch ( MetadataRepositoryException e ) 1145 { 1146 throw new ArchivaRestServiceException( e.getMessage(), e ); 1147 } 1148 finally 1149 { 1150 repositorySession.close(); 1151 } 1152 } 1153 1154 //--------------------------- 1155 // internals 1156 //--------------------------- 1157 1158 private void closeQuietly( JarFile jarFile ) 1159 { 1160 if ( jarFile != null ) 1161 { 1162 try 1163 { 1164 jarFile.close(); 1165 } 1166 catch ( IOException e ) 1167 { 1168 log.warn( "ignore error closing jarFile {}", jarFile.getName() ); 1169 } 1170 } 1171 } 1172 1173 protected List<ArtifactContentEntry> readFileEntries(final StorageAsset file, final String filterPath, final String repoId ) 1174 throws IOException 1175 { 1176 String cleanedfilterPath = filterPath==null ? "" : (StringUtils.startsWith(filterPath, "/") ? 1177 StringUtils.substringAfter(filterPath, "/") : filterPath); 1178 Map<String, ArtifactContentEntry> artifactContentEntryMap = new HashMap<>(); 1179 int filterDepth = StringUtils.countMatches( cleanedfilterPath, "/" ); 1180 if (!StringUtils.endsWith(cleanedfilterPath,"/") && !StringUtils.isEmpty(cleanedfilterPath)) { 1181 filterDepth++; 1182 } 1183 1184 StorageUtil.PathInformation pathInfo = StorageUtil.getAssetDataAsPath(file); 1185 JarFile jarFile = new JarFile(pathInfo.getPath().toFile()); 1186 try 1187 { 1188 Enumeration<JarEntry> jarEntryEnumeration = jarFile.entries(); 1189 while ( jarEntryEnumeration.hasMoreElements() ) 1190 { 1191 JarEntry currentEntry = jarEntryEnumeration.nextElement(); 1192 String cleanedEntryName = StringUtils.endsWith( currentEntry.getName(), "/" ) ? // 1193 StringUtils.substringBeforeLast( currentEntry.getName(), "/" ) : currentEntry.getName(); 1194 String entryRootPath = getRootPath( cleanedEntryName ); 1195 int depth = StringUtils.countMatches( cleanedEntryName, "/" ); 1196 if ( StringUtils.isEmpty( cleanedfilterPath ) // 1197 && !artifactContentEntryMap.containsKey( entryRootPath ) // 1198 && depth == filterDepth ) 1199 { 1200 1201 artifactContentEntryMap.put( entryRootPath, 1202 new ArtifactContentEntry( entryRootPath, !currentEntry.isDirectory(), 1203 depth, repoId ) ); 1204 } 1205 else 1206 { 1207 if ( StringUtils.startsWith( cleanedEntryName, cleanedfilterPath ) // 1208 && ( depth == filterDepth || ( !currentEntry.isDirectory() && depth == filterDepth ) ) ) 1209 { 1210 artifactContentEntryMap.put( cleanedEntryName, new ArtifactContentEntry( cleanedEntryName, 1211 !currentEntry.isDirectory(), 1212 depth, repoId ) ); 1213 } 1214 } 1215 } 1216 1217 if ( StringUtils.isNotEmpty( cleanedfilterPath ) ) 1218 { 1219 Map<String, ArtifactContentEntry> filteredArtifactContentEntryMap = new HashMap<>(); 1220 1221 for ( Map.Entry<String, ArtifactContentEntry> entry : artifactContentEntryMap.entrySet() ) 1222 { 1223 filteredArtifactContentEntryMap.put( entry.getKey(), entry.getValue() ); 1224 } 1225 1226 List<ArtifactContentEntry> sorted = getSmallerDepthEntries( filteredArtifactContentEntryMap ); 1227 if ( sorted == null ) 1228 { 1229 return Collections.emptyList(); 1230 } 1231 Collections.sort( sorted, ArtifactContentEntryComparator.INSTANCE ); 1232 return sorted; 1233 } 1234 } 1235 finally 1236 { 1237 if ( jarFile != null ) 1238 { 1239 jarFile.close(); 1240 } 1241 if (pathInfo.isTmpFile()) { 1242 Files.deleteIfExists(pathInfo.getPath()); 1243 } 1244 } 1245 List<ArtifactContentEntry> sorted = new ArrayList<>( artifactContentEntryMap.values() ); 1246 Collections.sort( sorted, ArtifactContentEntryComparator.INSTANCE ); 1247 return sorted; 1248 } 1249 1250 private List<ArtifactContentEntry> getSmallerDepthEntries( Map<String, ArtifactContentEntry> entries ) 1251 { 1252 int smallestDepth = Integer.MAX_VALUE; 1253 Map<Integer, List<ArtifactContentEntry>> perDepthList = new HashMap<>(); 1254 for ( Map.Entry<String, ArtifactContentEntry> entry : entries.entrySet() ) 1255 { 1256 1257 ArtifactContentEntry current = entry.getValue(); 1258 1259 if ( current.getDepth() < smallestDepth ) 1260 { 1261 smallestDepth = current.getDepth(); 1262 } 1263 1264 List<ArtifactContentEntry> currentList = perDepthList.get( current.getDepth() ); 1265 1266 if ( currentList == null ) 1267 { 1268 currentList = new ArrayList<>(); 1269 currentList.add( current ); 1270 perDepthList.put( current.getDepth(), currentList ); 1271 } 1272 else 1273 { 1274 currentList.add( current ); 1275 } 1276 1277 } 1278 1279 return perDepthList.get( smallestDepth ); 1280 } 1281 1282 /** 1283 * @param path 1284 * @return org/apache -> org , org -> org 1285 */ 1286 private String getRootPath( String path ) 1287 { 1288 if ( StringUtils.contains( path, '/' ) ) 1289 { 1290 return StringUtils.substringBefore( path, "/" ); 1291 } 1292 return path; 1293 } 1294 1295 private List<String> getSelectedRepos( String repositoryId ) 1296 throws ArchivaRestServiceException 1297 { 1298 1299 List<String> selectedRepos = getObservableRepos(); 1300 1301 if ( CollectionUtils.isEmpty( selectedRepos ) ) 1302 { 1303 return Collections.emptyList(); 1304 } 1305 1306 if ( StringUtils.isNotEmpty( repositoryId ) ) 1307 { 1308 // check user has karma on the repository 1309 if ( !selectedRepos.contains( repositoryId ) ) 1310 { 1311 throw new ArchivaRestServiceException( "browse.root.groups.repositoy.denied", 1312 Response.Status.FORBIDDEN.getStatusCode(), null ); 1313 } 1314 selectedRepos = Collections.singletonList( repositoryId ); 1315 } 1316 return selectedRepos; 1317 } 1318 1319 1320 private String collapseNamespaces( RepositorySession repositorySession, MetadataResolver metadataResolver, 1321 Collection<String> repoIds, String n ) 1322 throws MetadataResolutionException 1323 { 1324 Set<String> subNamespaces = new LinkedHashSet<String>(); 1325 for ( String repoId : repoIds ) 1326 { 1327 subNamespaces.addAll( metadataResolver.resolveNamespaces( repositorySession, repoId, n ) ); 1328 } 1329 if ( subNamespaces.size() != 1 ) 1330 { 1331 log.debug( "{} is not collapsible as it has sub-namespaces: {}", n, subNamespaces ); 1332 return n; 1333 } 1334 else 1335 { 1336 for ( String repoId : repoIds ) 1337 { 1338 Collection<String> projects = metadataResolver.resolveProjects( repositorySession, repoId, n ); 1339 if ( projects != null && !projects.isEmpty() ) 1340 { 1341 log.debug( "{} is not collapsible as it has projects", n ); 1342 return n; 1343 } 1344 } 1345 return collapseNamespaces( repositorySession, metadataResolver, repoIds, 1346 n + "." + subNamespaces.iterator().next() ); 1347 } 1348 } 1349 1350 public Cache<String, ProjectVersionMetadata> getVersionMetadataCache() 1351 { 1352 return versionMetadataCache; 1353 } 1354 1355 public void setVersionMetadataCache( Cache<String, ProjectVersionMetadata> versionMetadataCache ) 1356 { 1357 this.versionMetadataCache = versionMetadataCache; 1358 } 1359}