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