This project has retired. For details please refer to its Attic page.
MetadataTools xref
View Javadoc
1   package org.apache.archiva.repository.metadata;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import org.apache.archiva.checksum.ChecksumAlgorithm;
23  import org.apache.archiva.checksum.ChecksummedFile;
24  import org.apache.archiva.common.utils.PathUtil;
25  import org.apache.archiva.common.utils.VersionComparator;
26  import org.apache.archiva.common.utils.VersionUtil;
27  import org.apache.archiva.configuration.ArchivaConfiguration;
28  import org.apache.archiva.configuration.ConfigurationNames;
29  import org.apache.archiva.configuration.FileTypes;
30  import org.apache.archiva.configuration.ProxyConnectorConfiguration;
31  import org.apache.archiva.maven2.metadata.MavenMetadataReader;
32  import org.apache.archiva.model.ArchivaRepositoryMetadata;
33  import org.apache.archiva.model.ArtifactReference;
34  import org.apache.archiva.model.Plugin;
35  import org.apache.archiva.model.ProjectReference;
36  import org.apache.archiva.model.SnapshotVersion;
37  import org.apache.archiva.model.VersionedReference;
38  import org.apache.archiva.redback.components.registry.Registry;
39  import org.apache.archiva.redback.components.registry.RegistryListener;
40  import org.apache.archiva.repository.ContentNotFoundException;
41  import org.apache.archiva.repository.ManagedRepositoryContent;
42  import org.apache.archiva.repository.RemoteRepositoryContent;
43  import org.apache.archiva.repository.layout.LayoutException;
44  import org.apache.archiva.xml.XMLException;
45  import org.apache.commons.collections.CollectionUtils;
46  import org.apache.commons.io.FileUtils;
47  import org.apache.commons.lang.StringUtils;
48  import org.apache.commons.lang.math.NumberUtils;
49  import org.apache.commons.lang.time.DateUtils;
50  import org.slf4j.Logger;
51  import org.slf4j.LoggerFactory;
52  import org.springframework.stereotype.Service;
53  
54  import javax.annotation.PostConstruct;
55  import javax.inject.Inject;
56  import javax.inject.Named;
57  import java.io.File;
58  import java.io.IOException;
59  import java.text.ParseException;
60  import java.text.SimpleDateFormat;
61  import java.util.ArrayList;
62  import java.util.Calendar;
63  import java.util.Collection;
64  import java.util.Collections;
65  import java.util.Date;
66  import java.util.HashMap;
67  import java.util.HashSet;
68  import java.util.Iterator;
69  import java.util.LinkedHashSet;
70  import java.util.List;
71  import java.util.Map;
72  import java.util.Set;
73  import java.util.regex.Matcher;
74  
75  /**
76   * MetadataTools
77   *
78   *
79   */
80  @Service( "metadataTools#default" )
81  public class MetadataTools
82      implements RegistryListener
83  {
84      private Logger log = LoggerFactory.getLogger( getClass() );
85  
86      public static final String MAVEN_METADATA = "maven-metadata.xml";
87  
88      public static final String MAVEN_ARCHETYPE_CATALOG ="archetype-catalog.xml";
89  
90      private static final char PATH_SEPARATOR = '/';
91  
92      private static final char GROUP_SEPARATOR = '.';
93  
94      /**
95       *
96       */
97      @Inject
98      @Named( value = "archivaConfiguration#default" )
99      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 }