001package org.apache.archiva.repository.metadata.base; 002 003/* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022import org.apache.archiva.checksum.ChecksumAlgorithm; 023import org.apache.archiva.checksum.ChecksummedFile; 024import org.apache.archiva.common.utils.PathUtil; 025import org.apache.archiva.common.utils.VersionComparator; 026import org.apache.archiva.common.utils.VersionUtil; 027import org.apache.archiva.configuration.ArchivaConfiguration; 028import org.apache.archiva.configuration.ConfigurationNames; 029import org.apache.archiva.configuration.FileTypes; 030import org.apache.archiva.configuration.ProxyConnectorConfiguration; 031import org.apache.archiva.maven2.metadata.MavenMetadataReader; 032import org.apache.archiva.model.ArchivaRepositoryMetadata; 033import org.apache.archiva.model.ArtifactReference; 034import org.apache.archiva.model.Plugin; 035import org.apache.archiva.model.ProjectReference; 036import org.apache.archiva.model.SnapshotVersion; 037import org.apache.archiva.model.VersionedReference; 038import org.apache.archiva.components.registry.Registry; 039import org.apache.archiva.components.registry.RegistryListener; 040import org.apache.archiva.repository.ContentNotFoundException; 041import org.apache.archiva.repository.LayoutException; 042import org.apache.archiva.repository.ManagedRepositoryContent; 043import org.apache.archiva.repository.RemoteRepositoryContent; 044import org.apache.archiva.repository.metadata.RepositoryMetadataException; 045import org.apache.archiva.repository.storage.StorageAsset; 046import org.apache.archiva.xml.XMLException; 047import org.apache.commons.collections4.CollectionUtils; 048import org.apache.commons.lang3.StringUtils; 049import org.apache.commons.lang3.math.NumberUtils; 050import org.slf4j.Logger; 051import org.slf4j.LoggerFactory; 052import org.springframework.stereotype.Service; 053 054import javax.annotation.PostConstruct; 055import javax.inject.Inject; 056import javax.inject.Named; 057import java.io.IOException; 058import java.nio.file.Files; 059import java.nio.file.Path; 060import java.nio.file.Paths; 061import java.text.ParseException; 062import java.text.SimpleDateFormat; 063import java.util.*; 064import java.util.regex.Matcher; 065import java.util.stream.Stream; 066 067/** 068 * MetadataTools 069 * 070 * 071 */ 072@Service( "metadataTools#default" ) 073public class MetadataTools 074 implements RegistryListener 075{ 076 private Logger log = LoggerFactory.getLogger( getClass() ); 077 078 public static final String MAVEN_METADATA = "maven-metadata.xml"; 079 080 public static final String MAVEN_ARCHETYPE_CATALOG ="archetype-catalog.xml"; 081 082 private static final char PATH_SEPARATOR = '/'; 083 084 private static final char GROUP_SEPARATOR = '.'; 085 086 /** 087 * 088 */ 089 @Inject 090 @Named( value = "archivaConfiguration#default" ) 091 private ArchivaConfiguration configuration; 092 093 /** 094 * 095 */ 096 @Inject 097 @Named( value = "fileTypes" ) 098 private FileTypes filetypes; 099 100 private List<ChecksumAlgorithm> algorithms = Arrays.asList(ChecksumAlgorithm.SHA256, ChecksumAlgorithm.SHA1, ChecksumAlgorithm.MD5 ); 101 102 private List<String> artifactPatterns; 103 104 private Map<String, Set<String>> proxies; 105 106 private static final char NUMS[] = new char[]{ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }; 107 108 private SimpleDateFormat lastUpdatedFormat; 109 110 public MetadataTools() 111 { 112 lastUpdatedFormat = new SimpleDateFormat( "yyyyMMddHHmmss" ); 113 lastUpdatedFormat.setTimeZone(TimeZone.getTimeZone("UTC")); 114 } 115 116 @Override 117 public void afterConfigurationChange( Registry registry, String propertyName, Object propertyValue ) 118 { 119 if ( ConfigurationNames.isProxyConnector( propertyName ) ) 120 { 121 initConfigVariables(); 122 } 123 } 124 125 @Override 126 public void beforeConfigurationChange( Registry registry, String propertyName, Object propertyValue ) 127 { 128 /* nothing to do */ 129 } 130 131 /** 132 * Gather the set of snapshot versions found in a particular versioned reference. 133 * 134 * @return the Set of snapshot artifact versions found. 135 * @throws LayoutException 136 * @throws ContentNotFoundException 137 */ 138 public Set<String> gatherSnapshotVersions( ManagedRepositoryContent managedRepository, 139 VersionedReference reference ) 140 throws LayoutException, IOException, ContentNotFoundException 141 { 142 Set<String> foundVersions = managedRepository.getVersions( reference ); 143 144 // Next gather up the referenced 'latest' versions found in any proxied repositories 145 // maven-metadata-${proxyId}.xml files that may be present. 146 147 // Does this repository have a set of remote proxied repositories? 148 Set<String> proxiedRepoIds = this.proxies.get( managedRepository.getId() ); 149 150 if ( CollectionUtils.isNotEmpty( proxiedRepoIds ) ) 151 { 152 String baseVersion = VersionUtil.getBaseVersion( reference.getVersion() ); 153 baseVersion = baseVersion.substring( 0, baseVersion.indexOf( VersionUtil.SNAPSHOT ) - 1 ); 154 155 // Add in the proxied repo version ids too. 156 Iterator<String> it = proxiedRepoIds.iterator(); 157 while ( it.hasNext() ) 158 { 159 String proxyId = it.next(); 160 161 ArchivaRepositoryMetadata proxyMetadata = readProxyMetadata( managedRepository, reference, proxyId ); 162 if ( proxyMetadata == null ) 163 { 164 // There is no proxy metadata, skip it. 165 continue; 166 } 167 168 // Is there some snapshot info? 169 SnapshotVersion snapshot = proxyMetadata.getSnapshotVersion(); 170 if ( snapshot != null ) 171 { 172 String timestamp = snapshot.getTimestamp(); 173 int buildNumber = snapshot.getBuildNumber(); 174 175 // Only interested in the timestamp + buildnumber. 176 if ( StringUtils.isNotBlank( timestamp ) && ( buildNumber > 0 ) ) 177 { 178 foundVersions.add( baseVersion + "-" + timestamp + "-" + buildNumber ); 179 } 180 } 181 } 182 } 183 184 return foundVersions; 185 } 186 187 /** 188 * Take a path to a maven-metadata.xml, and attempt to translate it to a VersionedReference. 189 * 190 * @param path 191 * @return 192 */ 193 public VersionedReference toVersionedReference( String path ) 194 throws RepositoryMetadataException 195 { 196 if ( !path.endsWith( "/" + MAVEN_METADATA ) ) 197 { 198 throw new RepositoryMetadataException( "Cannot convert to versioned reference, not a metadata file. " ); 199 } 200 201 VersionedReference reference = new VersionedReference(); 202 203 String normalizedPath = StringUtils.replace( path, "\\", "/" ); 204 String pathParts[] = StringUtils.split( normalizedPath, '/' ); 205 206 int versionOffset = pathParts.length - 2; 207 int artifactIdOffset = versionOffset - 1; 208 int groupIdEnd = artifactIdOffset - 1; 209 210 reference.setVersion( pathParts[versionOffset] ); 211 212 if ( !hasNumberAnywhere( reference.getVersion() ) ) 213 { 214 // Scary check, but without it, all paths are version references; 215 throw new RepositoryMetadataException( 216 "Not a versioned reference, as version id on path has no number in it." ); 217 } 218 219 reference.setArtifactId( pathParts[artifactIdOffset] ); 220 221 StringBuilder gid = new StringBuilder(); 222 for ( int i = 0; i <= groupIdEnd; i++ ) 223 { 224 if ( i > 0 ) 225 { 226 gid.append( "." ); 227 } 228 gid.append( pathParts[i] ); 229 } 230 231 reference.setGroupId( gid.toString() ); 232 233 return reference; 234 } 235 236 private boolean hasNumberAnywhere( String version ) 237 { 238 return StringUtils.indexOfAny( version, NUMS ) != ( -1 ); 239 } 240 241 public ProjectReference toProjectReference( String path ) 242 throws RepositoryMetadataException 243 { 244 if ( !path.endsWith( "/" + MAVEN_METADATA ) ) 245 { 246 throw new RepositoryMetadataException( "Cannot convert to versioned reference, not a metadata file. " ); 247 } 248 249 ProjectReference reference = new ProjectReference(); 250 251 String normalizedPath = StringUtils.replace( path, "\\", "/" ); 252 String pathParts[] = StringUtils.split( normalizedPath, '/' ); 253 254 // Assume last part of the path is the version. 255 256 int artifactIdOffset = pathParts.length - 2; 257 int groupIdEnd = artifactIdOffset - 1; 258 259 reference.setArtifactId( pathParts[artifactIdOffset] ); 260 261 StringBuilder gid = new StringBuilder(); 262 for ( int i = 0; i <= groupIdEnd; i++ ) 263 { 264 if ( i > 0 ) 265 { 266 gid.append( "." ); 267 } 268 gid.append( pathParts[i] ); 269 } 270 271 reference.setGroupId( gid.toString() ); 272 273 return reference; 274 } 275 276 277 278 public String toPath( ProjectReference reference ) 279 { 280 StringBuilder path = new StringBuilder(); 281 282 path.append( formatAsDirectory( reference.getGroupId() ) ).append( PATH_SEPARATOR ); 283 path.append( reference.getArtifactId() ).append( PATH_SEPARATOR ); 284 path.append( MAVEN_METADATA ); 285 286 return path.toString(); 287 } 288 289 public String toPath( VersionedReference reference ) 290 { 291 StringBuilder path = new StringBuilder(); 292 293 path.append( formatAsDirectory( reference.getGroupId() ) ).append( PATH_SEPARATOR ); 294 path.append( reference.getArtifactId() ).append( PATH_SEPARATOR ); 295 if ( reference.getVersion() != null ) 296 { 297 // add the version only if it is present 298 path.append( VersionUtil.getBaseVersion( reference.getVersion() ) ).append( PATH_SEPARATOR ); 299 } 300 path.append( MAVEN_METADATA ); 301 302 return path.toString(); 303 } 304 305 private String formatAsDirectory( String directory ) 306 { 307 return directory.replace( GROUP_SEPARATOR, PATH_SEPARATOR ); 308 } 309 310 /** 311 * Adjusts a path for a metadata.xml file to its repository specific path. 312 * 313 * @param repository the repository to base new path off of. 314 * @param path the path to the metadata.xml file to adjust the name of. 315 * @return the newly adjusted path reference to the repository specific metadata path. 316 */ 317 public String getRepositorySpecificName( RemoteRepositoryContent repository, String path ) 318 { 319 return getRepositorySpecificName( repository.getId(), path ); 320 } 321 322 /** 323 * Adjusts a path for a metadata.xml file to its repository specific path. 324 * 325 * @param proxyId the repository id to base new path off of. 326 * @param path the path to the metadata.xml file to adjust the name of. 327 * @return the newly adjusted path reference to the repository specific metadata path. 328 */ 329 public String getRepositorySpecificName( String proxyId, String path ) 330 { 331 StringBuilder ret = new StringBuilder(); 332 333 int idx = path.lastIndexOf( '/' ); 334 if ( idx > 0 ) 335 { 336 ret.append( path.substring( 0, idx + 1 ) ); 337 } 338 339 // TODO: need to filter out 'bad' characters from the proxy id. 340 ret.append( "maven-metadata-" ).append( proxyId ).append( ".xml" ); 341 342 return ret.toString(); 343 } 344 345 @PostConstruct 346 public void initialize() 347 { 348 assert(configuration != null); 349 this.artifactPatterns = new ArrayList<>(); 350 this.proxies = new HashMap<>(); 351 initConfigVariables(); 352 353 configuration.addChangeListener( this ); 354 } 355 356 public ArchivaRepositoryMetadata readProxyMetadata( ManagedRepositoryContent managedRepository, 357 ProjectReference reference, String proxyId ) 358 { 359 String metadataPath = getRepositorySpecificName( proxyId, toPath( reference ) ); 360 StorageAsset metadataFile = managedRepository.getRepository().getAsset( metadataPath ); 361 362 if ( !metadataFile.exists() || metadataFile.isContainer()) 363 { 364 // Nothing to do. return null. 365 return null; 366 } 367 368 try 369 { 370 return MavenMetadataReader.read( metadataFile ); 371 } 372 catch (XMLException | IOException e ) 373 { 374 // TODO: [monitor] consider a monitor for this event. 375 // TODO: consider a read-redo on monitor return code? 376 log.warn( "Unable to read metadata: {}", metadataFile.getPath(), e ); 377 return null; 378 } 379 } 380 381 public ArchivaRepositoryMetadata readProxyMetadata( ManagedRepositoryContent managedRepository, 382 String logicalResource, String proxyId ) 383 { 384 String metadataPath = getRepositorySpecificName( proxyId, logicalResource ); 385 StorageAsset metadataFile = managedRepository.getRepository().getAsset( metadataPath ); 386 387 if ( !metadataFile.exists() || metadataFile.isContainer()) 388 { 389 // Nothing to do. return null. 390 return null; 391 } 392 393 try 394 { 395 return MavenMetadataReader.read( metadataFile ); 396 } 397 catch (XMLException | IOException e ) 398 { 399 // TODO: [monitor] consider a monitor for this event. 400 // TODO: consider a read-redo on monitor return code? 401 log.warn( "Unable to read metadata: {}", metadataFile.getPath(), e ); 402 return null; 403 } 404 } 405 406 public ArchivaRepositoryMetadata readProxyMetadata( ManagedRepositoryContent managedRepository, 407 VersionedReference reference, String proxyId ) 408 { 409 String metadataPath = getRepositorySpecificName( proxyId, toPath( reference ) ); 410 StorageAsset metadataFile = managedRepository.getRepository().getAsset( metadataPath ); 411 412 if ( !metadataFile.exists() || metadataFile.isContainer()) 413 { 414 // Nothing to do. return null. 415 return null; 416 } 417 418 try 419 { 420 return MavenMetadataReader.read( metadataFile ); 421 } 422 catch (XMLException | IOException e ) 423 { 424 // TODO: [monitor] consider a monitor for this event. 425 // TODO: consider a read-redo on monitor return code? 426 log.warn( "Unable to read metadata: {}", metadataFile.getPath(), e ); 427 return null; 428 } 429 } 430 431 public void updateMetadata( ManagedRepositoryContent managedRepository, String logicalResource ) 432 throws RepositoryMetadataException 433 { 434 final StorageAsset metadataFile = managedRepository.getRepository().getAsset( logicalResource ); 435 ArchivaRepositoryMetadata metadata = null; 436 437 //Gather and merge all metadata available 438 List<ArchivaRepositoryMetadata> metadatas = 439 getMetadatasForManagedRepository( managedRepository, logicalResource ); 440 for ( ArchivaRepositoryMetadata proxiedMetadata : metadatas ) 441 { 442 if ( metadata == null ) 443 { 444 metadata = proxiedMetadata; 445 continue; 446 } 447 metadata = RepositoryMetadataMerge.merge( metadata, proxiedMetadata ); 448 } 449 450 if ( metadata == null ) 451 { 452 log.debug( "No metadata to update for {}", logicalResource ); 453 return; 454 } 455 456 Set<String> availableVersions = new HashSet<String>(); 457 List<String> metadataAvailableVersions = metadata.getAvailableVersions(); 458 if ( metadataAvailableVersions != null ) 459 { 460 availableVersions.addAll( metadataAvailableVersions ); 461 } 462 availableVersions = findPossibleVersions( availableVersions, metadataFile.getParent() ); 463 464 if ( availableVersions.size() > 0 ) 465 { 466 updateMetadataVersions( availableVersions, metadata ); 467 } 468 469 RepositoryMetadataWriter.write( metadata, metadataFile ); 470 471 ChecksummedFile checksum = new ChecksummedFile( metadataFile.getFilePath() ); 472 checksum.fixChecksums( algorithms ); 473 } 474 475 /** 476 * Skims the parent directory of a metadata in vain hope of finding 477 * subdirectories that contain poms. 478 * 479 * @param metadataParentDirectory 480 * @return origional set plus newly found versions 481 */ 482 private Set<String> findPossibleVersions( Set<String> versions, StorageAsset metadataParentDirectory ) 483 { 484 485 Set<String> result = new HashSet<String>( versions ); 486 487 metadataParentDirectory.list().stream().filter(asset -> 488 asset.isContainer()).filter(asset -> { 489 return asset.list().stream().anyMatch(f -> !f.isContainer() && f.getName().endsWith(".pom")); 490 } 491 ).forEach( p -> result.add(p.getName())); 492 493 return result; 494 } 495 496 private List<ArchivaRepositoryMetadata> getMetadatasForManagedRepository( 497 ManagedRepositoryContent managedRepository, String logicalResource ) 498 { 499 List<ArchivaRepositoryMetadata> metadatas = new ArrayList<>(); 500 StorageAsset file = managedRepository.getRepository().getAsset( logicalResource ); 501 502 if ( file.exists() ) 503 { 504 try 505 { 506 ArchivaRepositoryMetadata existingMetadata = MavenMetadataReader.read( file ); 507 if ( existingMetadata != null ) 508 { 509 metadatas.add( existingMetadata ); 510 } 511 } 512 catch (XMLException | IOException e ) 513 { 514 log.debug( "Could not read metadata at {}. Metadata will be removed.", file.getPath() ); 515 try { 516 file.getStorage().removeAsset(file); 517 } catch (IOException ex) { 518 log.error("Could not remove asset {}", file.getPath()); 519 } 520 } 521 } 522 523 Set<String> proxyIds = proxies.get( managedRepository.getId() ); 524 if ( proxyIds != null ) 525 { 526 for ( String proxyId : proxyIds ) 527 { 528 ArchivaRepositoryMetadata proxyMetadata = 529 readProxyMetadata( managedRepository, logicalResource, proxyId ); 530 if ( proxyMetadata != null ) 531 { 532 metadatas.add( proxyMetadata ); 533 } 534 } 535 } 536 537 return metadatas; 538 } 539 540 541 /** 542 * Update the metadata to represent the all versions/plugins of 543 * the provided groupId:artifactId project or group reference, 544 * based off of information present in the repository, 545 * the maven-metadata.xml files, and the proxy/repository specific 546 * metadata file contents. 547 * <p> 548 * We must treat this as a group or a project metadata file as there is no way to know in advance 549 * 550 * @param managedRepository the managed repository where the metadata is kept. 551 * @param reference the reference to update. 552 * @throws LayoutException 553 * @throws RepositoryMetadataException 554 * @throws IOException 555 * @throws ContentNotFoundException 556 * @deprecated 557 */ 558 public void updateMetadata( ManagedRepositoryContent managedRepository, ProjectReference reference ) 559 throws LayoutException, RepositoryMetadataException, IOException, ContentNotFoundException 560 { 561 562 StorageAsset metadataFile = managedRepository.getRepository().getAsset( toPath( reference ) ); 563 564 long lastUpdated = getExistingLastUpdated( metadataFile ); 565 566 ArchivaRepositoryMetadata metadata = new ArchivaRepositoryMetadata(); 567 metadata.setGroupId( reference.getGroupId() ); 568 metadata.setArtifactId( reference.getArtifactId() ); 569 570 // Gather up all versions found in the managed repository. 571 Set<String> allVersions = managedRepository.getVersions( reference ); 572 573 // Gather up all plugins found in the managed repository. 574 // TODO: do we know this information instead? 575// Set<Plugin> allPlugins = managedRepository.getPlugins( reference ); 576 Set<Plugin> allPlugins; 577 if ( metadataFile.exists()) 578 { 579 try 580 { 581 allPlugins = new LinkedHashSet<Plugin>( MavenMetadataReader.read( metadataFile ).getPlugins() ); 582 } 583 catch ( XMLException e ) 584 { 585 throw new RepositoryMetadataException( e.getMessage(), e ); 586 } 587 } 588 else 589 { 590 allPlugins = new LinkedHashSet<Plugin>(); 591 } 592 593 // Does this repository have a set of remote proxied repositories? 594 Set<String> proxiedRepoIds = this.proxies.get( managedRepository.getId() ); 595 596 if ( CollectionUtils.isNotEmpty( proxiedRepoIds ) ) 597 { 598 // Add in the proxied repo version ids too. 599 Iterator<String> it = proxiedRepoIds.iterator(); 600 while ( it.hasNext() ) 601 { 602 String proxyId = it.next(); 603 604 ArchivaRepositoryMetadata proxyMetadata = readProxyMetadata( managedRepository, reference, proxyId ); 605 if ( proxyMetadata != null ) 606 { 607 allVersions.addAll( proxyMetadata.getAvailableVersions() ); 608 allPlugins.addAll( proxyMetadata.getPlugins() ); 609 long proxyLastUpdated = getLastUpdated( proxyMetadata ); 610 611 lastUpdated = Math.max( lastUpdated, proxyLastUpdated ); 612 } 613 } 614 } 615 616 if ( !allVersions.isEmpty() ) 617 { 618 updateMetadataVersions( allVersions, metadata ); 619 } 620 else 621 { 622 // Add the plugins to the metadata model. 623 metadata.setPlugins( new ArrayList<>( allPlugins ) ); 624 625 // artifact ID was actually the last part of the group 626 metadata.setGroupId( metadata.getGroupId() + "." + metadata.getArtifactId() ); 627 metadata.setArtifactId( null ); 628 } 629 630 if ( lastUpdated > 0 ) 631 { 632 metadata.setLastUpdatedTimestamp( toLastUpdatedDate( lastUpdated ) ); 633 } 634 635 // Save the metadata model to disk. 636 RepositoryMetadataWriter.write( metadata, metadataFile ); 637 ChecksummedFile checksum = new ChecksummedFile( metadataFile.getFilePath() ); 638 checksum.fixChecksums( algorithms ); 639 } 640 641 private void updateMetadataVersions( Collection<String> allVersions, ArchivaRepositoryMetadata metadata ) 642 { 643 // Sort the versions 644 List<String> sortedVersions = new ArrayList<>( allVersions ); 645 Collections.sort( sortedVersions, VersionComparator.getInstance() ); 646 647 // Split the versions into released and snapshots. 648 List<String> releasedVersions = new ArrayList<>(); 649 List<String> snapshotVersions = new ArrayList<>(); 650 651 for ( String version : sortedVersions ) 652 { 653 if ( VersionUtil.isSnapshot( version ) ) 654 { 655 snapshotVersions.add( version ); 656 } 657 else 658 { 659 releasedVersions.add( version ); 660 } 661 } 662 663 Collections.sort( releasedVersions, VersionComparator.getInstance() ); 664 Collections.sort( snapshotVersions, VersionComparator.getInstance() ); 665 666 String latestVersion = sortedVersions.get( sortedVersions.size() - 1 ); 667 String releaseVersion = null; 668 669 if ( CollectionUtils.isNotEmpty( releasedVersions ) ) 670 { 671 releaseVersion = releasedVersions.get( releasedVersions.size() - 1 ); 672 } 673 674 // Add the versions to the metadata model. 675 metadata.setAvailableVersions( sortedVersions ); 676 677 metadata.setLatestVersion( latestVersion ); 678 metadata.setReleasedVersion( releaseVersion ); 679 } 680 681 private Date toLastUpdatedDate( long lastUpdated ) 682 { 683 Calendar cal = Calendar.getInstance( TimeZone.getTimeZone("UTC") ); 684 cal.setTimeInMillis( lastUpdated ); 685 686 return cal.getTime(); 687 } 688 689 private long toLastUpdatedLong( String timestampString ) 690 { 691 try 692 { 693 Date date = lastUpdatedFormat.parse( timestampString ); 694 Calendar cal = Calendar.getInstance( TimeZone.getTimeZone("UTC")); 695 cal.setTime( date ); 696 697 return cal.getTimeInMillis(); 698 } 699 catch ( ParseException e ) 700 { 701 return 0; 702 } 703 } 704 705 private long getLastUpdated( ArchivaRepositoryMetadata metadata ) 706 { 707 if ( metadata == null ) 708 { 709 // Doesn't exist. 710 return 0; 711 } 712 713 try 714 { 715 String lastUpdated = metadata.getLastUpdated(); 716 if ( StringUtils.isBlank( lastUpdated ) ) 717 { 718 // Not set. 719 return 0; 720 } 721 722 Date lastUpdatedDate = lastUpdatedFormat.parse( lastUpdated ); 723 return lastUpdatedDate.getTime(); 724 } 725 catch ( ParseException e ) 726 { 727 // Bad format on the last updated string. 728 return 0; 729 } 730 } 731 732 private long getExistingLastUpdated( StorageAsset metadataFile ) 733 { 734 if ( !metadataFile.exists() ) 735 { 736 // Doesn't exist. 737 return 0; 738 } 739 740 try 741 { 742 ArchivaRepositoryMetadata metadata = MavenMetadataReader.read( metadataFile ); 743 744 return getLastUpdated( metadata ); 745 } 746 catch (XMLException | IOException e ) 747 { 748 // Error. 749 return 0; 750 } 751 } 752 753 /** 754 * Update the metadata based on the following rules. 755 * <p> 756 * 1) If this is a SNAPSHOT reference, then utilize the proxy/repository specific 757 * metadata files to represent the current / latest SNAPSHOT available. 758 * 2) If this is a RELEASE reference, and the metadata file does not exist, then 759 * create the metadata file with contents required of the VersionedReference 760 * 761 * @param managedRepository the managed repository where the metadata is kept. 762 * @param reference the versioned reference to update 763 * @throws LayoutException 764 * @throws RepositoryMetadataException 765 * @throws IOException 766 * @throws ContentNotFoundException 767 * @deprecated 768 */ 769 public void updateMetadata( ManagedRepositoryContent managedRepository, VersionedReference reference ) 770 throws LayoutException, RepositoryMetadataException, IOException, ContentNotFoundException 771 { 772 StorageAsset metadataFile = managedRepository.getRepository().getAsset( toPath( reference ) ); 773 774 long lastUpdated = getExistingLastUpdated( metadataFile ); 775 776 ArchivaRepositoryMetadata metadata = new ArchivaRepositoryMetadata(); 777 metadata.setGroupId( reference.getGroupId() ); 778 metadata.setArtifactId( reference.getArtifactId() ); 779 780 if ( VersionUtil.isSnapshot( reference.getVersion() ) ) 781 { 782 // Do SNAPSHOT handling. 783 metadata.setVersion( VersionUtil.getBaseVersion( reference.getVersion() ) ); 784 785 // Gather up all of the versions found in the reference dir, and any 786 // proxied maven-metadata.xml files. 787 Set<String> snapshotVersions = gatherSnapshotVersions( managedRepository, reference ); 788 789 if ( snapshotVersions.isEmpty() ) 790 { 791 throw new ContentNotFoundException( 792 "No snapshot versions found on reference [" + VersionedReference.toKey( reference ) + "]." ); 793 } 794 795 // sort the list to determine to aide in determining the Latest version. 796 List<String> sortedVersions = new ArrayList<>(); 797 sortedVersions.addAll( snapshotVersions ); 798 Collections.sort( sortedVersions, new VersionComparator() ); 799 800 String latestVersion = sortedVersions.get( sortedVersions.size() - 1 ); 801 802 if ( VersionUtil.isUniqueSnapshot( latestVersion ) ) 803 { 804 // The latestVersion will contain the full version string "1.0-alpha-5-20070821.213044-8" 805 // This needs to be broken down into ${base}-${timestamp}-${build_number} 806 807 Matcher m = VersionUtil.UNIQUE_SNAPSHOT_PATTERN.matcher( latestVersion ); 808 if ( m.matches() ) 809 { 810 metadata.setSnapshotVersion( new SnapshotVersion() ); 811 int buildNumber = NumberUtils.toInt( m.group( 3 ), -1 ); 812 metadata.getSnapshotVersion().setBuildNumber( buildNumber ); 813 814 Matcher mtimestamp = VersionUtil.TIMESTAMP_PATTERN.matcher( m.group( 2 ) ); 815 if ( mtimestamp.matches() ) 816 { 817 String tsDate = mtimestamp.group( 1 ); 818 String tsTime = mtimestamp.group( 2 ); 819 820 long snapshotLastUpdated = toLastUpdatedLong( tsDate + tsTime ); 821 822 lastUpdated = Math.max( lastUpdated, snapshotLastUpdated ); 823 824 metadata.getSnapshotVersion().setTimestamp( m.group( 2 ) ); 825 } 826 } 827 } 828 else if ( VersionUtil.isGenericSnapshot( latestVersion ) ) 829 { 830 // The latestVersion ends with the generic version string. 831 // Example: 1.0-alpha-5-SNAPSHOT 832 833 metadata.setSnapshotVersion( new SnapshotVersion() ); 834 835 /* Disabled due to decision in [MRM-535]. 836 * Do not set metadata.lastUpdated to file.lastModified. 837 * 838 * Should this be the last updated timestamp of the file, or in the case of an 839 * archive, the most recent timestamp in the archive? 840 * 841 ArtifactReference artifact = getFirstArtifact( managedRepository, reference ); 842 843 if ( artifact == null ) 844 { 845 throw new IOException( "Not snapshot artifact found to reference in " + reference ); 846 } 847 848 File artifactFile = managedRepository.toFile( artifact ); 849 850 if ( artifactFile.exists() ) 851 { 852 Date lastModified = new Date( artifactFile.lastModified() ); 853 metadata.setLastUpdatedTimestamp( lastModified ); 854 } 855 */ 856 } 857 else 858 { 859 throw new RepositoryMetadataException( 860 "Unable to process snapshot version <" + latestVersion + "> reference <" + reference + ">" ); 861 } 862 } 863 else 864 { 865 // Do RELEASE handling. 866 metadata.setVersion( reference.getVersion() ); 867 } 868 869 // Set last updated 870 if ( lastUpdated > 0 ) 871 { 872 metadata.setLastUpdatedTimestamp( toLastUpdatedDate( lastUpdated ) ); 873 } 874 875 // Save the metadata model to disk. 876 RepositoryMetadataWriter.write( metadata, metadataFile ); 877 ChecksummedFile checksum = new ChecksummedFile( metadataFile.getFilePath() ); 878 checksum.fixChecksums( algorithms ); 879 } 880 881 private void initConfigVariables() 882 { 883 assert(this.artifactPatterns!=null); 884 assert(proxies!=null); 885 synchronized ( this.artifactPatterns ) 886 { 887 this.artifactPatterns.clear(); 888 889 this.artifactPatterns.addAll( filetypes.getFileTypePatterns( FileTypes.ARTIFACTS ) ); 890 } 891 892 synchronized ( proxies ) 893 { 894 this.proxies.clear(); 895 896 List<ProxyConnectorConfiguration> proxyConfigs = configuration.getConfiguration().getProxyConnectors(); 897 for ( ProxyConnectorConfiguration proxyConfig : proxyConfigs ) 898 { 899 String key = proxyConfig.getSourceRepoId(); 900 901 Set<String> remoteRepoIds = this.proxies.get( key ); 902 903 if ( remoteRepoIds == null ) 904 { 905 remoteRepoIds = new HashSet<String>(); 906 } 907 908 remoteRepoIds.add( proxyConfig.getTargetRepoId() ); 909 910 this.proxies.put( key, remoteRepoIds ); 911 } 912 } 913 } 914 915 /** 916 * Get the first Artifact found in the provided VersionedReference location. 917 * 918 * @param managedRepository the repository to search within. 919 * @param reference the reference to the versioned reference to search within 920 * @return the ArtifactReference to the first artifact located within the versioned reference. or null if 921 * no artifact was found within the versioned reference. 922 * @throws IOException if the versioned reference is invalid (example: doesn't exist, or isn't a directory) 923 * @throws LayoutException 924 */ 925 public ArtifactReference getFirstArtifact( ManagedRepositoryContent managedRepository, 926 VersionedReference reference ) 927 throws LayoutException, IOException 928 { 929 String path = toPath( reference ); 930 931 int idx = path.lastIndexOf( '/' ); 932 if ( idx > 0 ) 933 { 934 path = path.substring( 0, idx ); 935 } 936 937 Path repoDir = Paths.get( managedRepository.getRepoRoot(), path ); 938 939 if ( !Files.exists(repoDir)) 940 { 941 throw new IOException( "Unable to gather the list of snapshot versions on a non-existant directory: " 942 + repoDir.toAbsolutePath() ); 943 } 944 945 if ( !Files.isDirectory( repoDir )) 946 { 947 throw new IOException( 948 "Unable to gather the list of snapshot versions on a non-directory: " + repoDir.toAbsolutePath() ); 949 } 950 951 try(Stream<Path> stream = Files.list(repoDir)) { 952 String result = stream.filter( Files::isRegularFile ).map( path1 -> 953 PathUtil.getRelative( managedRepository.getRepoRoot(), path1 ) 954 ).filter( filetypes::matchesArtifactPattern ).findFirst().orElse( null ); 955 if (result!=null) { 956 return managedRepository.toArtifactReference( result ); 957 } 958 } 959 // No artifact was found. 960 return null; 961 } 962 963 public ArchivaConfiguration getConfiguration() 964 { 965 return configuration; 966 } 967 968 public void setConfiguration( ArchivaConfiguration configuration ) 969 { 970 this.configuration = configuration; 971 } 972 973 public FileTypes getFiletypes() 974 { 975 return filetypes; 976 } 977 978 public void setFiletypes( FileTypes filetypes ) 979 { 980 this.filetypes = filetypes; 981 } 982}