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