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