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