]> source.dussan.org Git - archiva.git/blob
35af14277503ff535f782f0369248d86b4e02ca1
[archiva.git] /
1 package org.apache.maven.archiva.repository.metadata;
2
3 /*
4  * Copyright 2001-2007 The Apache Software Foundation.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *      http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18
19 import org.apache.commons.collections.CollectionUtils;
20 import org.apache.commons.lang.StringUtils;
21 import org.apache.commons.lang.math.NumberUtils;
22 import org.apache.commons.lang.time.DateUtils;
23 import org.apache.maven.archiva.common.utils.Checksums;
24 import org.apache.maven.archiva.common.utils.PathUtil;
25 import org.apache.maven.archiva.common.utils.VersionComparator;
26 import org.apache.maven.archiva.common.utils.VersionUtil;
27 import org.apache.maven.archiva.configuration.ArchivaConfiguration;
28 import org.apache.maven.archiva.configuration.ConfigurationNames;
29 import org.apache.maven.archiva.configuration.FileTypes;
30 import org.apache.maven.archiva.configuration.ProxyConnectorConfiguration;
31 import org.apache.maven.archiva.model.ArchivaRepositoryMetadata;
32 import org.apache.maven.archiva.model.ArtifactReference;
33 import org.apache.maven.archiva.model.Plugin;
34 import org.apache.maven.archiva.model.ProjectReference;
35 import org.apache.maven.archiva.model.SnapshotVersion;
36 import org.apache.maven.archiva.model.VersionedReference;
37 import org.apache.maven.archiva.repository.ContentNotFoundException;
38 import org.apache.maven.archiva.repository.ManagedRepositoryContent;
39 import org.apache.maven.archiva.repository.RemoteRepositoryContent;
40 import org.apache.maven.archiva.repository.layout.LayoutException;
41 import org.codehaus.plexus.personality.plexus.lifecycle.phase.Initializable;
42 import org.codehaus.plexus.personality.plexus.lifecycle.phase.InitializationException;
43 import org.codehaus.plexus.registry.Registry;
44 import org.codehaus.plexus.registry.RegistryListener;
45 import org.codehaus.plexus.util.SelectorUtils;
46 import org.slf4j.Logger;
47 import org.slf4j.LoggerFactory;
48
49 import java.io.File;
50 import java.io.IOException;
51 import java.text.ParseException;
52 import java.text.SimpleDateFormat;
53 import java.util.ArrayList;
54 import java.util.Calendar;
55 import java.util.Collections;
56 import java.util.Date;
57 import java.util.HashMap;
58 import java.util.HashSet;
59 import java.util.Iterator;
60 import java.util.LinkedHashSet;
61 import java.util.List;
62 import java.util.Map;
63 import java.util.Set;
64 import java.util.regex.Matcher;
65
66 /**
67  * MetadataTools
68  *
69  * @author <a href="mailto:joakime@apache.org">Joakim Erdfelt</a>
70  * @version $Id$
71  * 
72  * @plexus.component role="org.apache.maven.archiva.repository.metadata.MetadataTools"
73  */
74 public class MetadataTools
75     implements RegistryListener, Initializable
76 {
77     private static Logger log = LoggerFactory.getLogger( MetadataTools.class );
78
79     public static final String MAVEN_METADATA = "maven-metadata.xml";
80
81     private static final char PATH_SEPARATOR = '/';
82
83     private static final char GROUP_SEPARATOR = '.';
84
85     /**
86      * @plexus.requirement
87      */
88     private ArchivaConfiguration configuration;
89
90     /**
91      * @plexus.requirement
92      */
93     private FileTypes filetypes;
94     
95     /**
96      * @plexus.requirement
97      */
98     private Checksums checksums;
99
100     private List<String> artifactPatterns;
101
102     private Map<String, Set<String>> proxies;
103
104     private static final char NUMS[] = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
105
106     private SimpleDateFormat lastUpdatedFormat;
107
108     public MetadataTools()
109     {
110         lastUpdatedFormat = new SimpleDateFormat( "yyyyMMddHHmmss" );
111         lastUpdatedFormat.setTimeZone( DateUtils.UTC_TIME_ZONE );
112     }
113
114     public void afterConfigurationChange( Registry registry, String propertyName, Object propertyValue )
115     {
116         if ( ConfigurationNames.isProxyConnector( propertyName ) )
117         {
118             initConfigVariables();
119         }
120     }
121
122     public void beforeConfigurationChange( Registry registry, String propertyName, Object propertyValue )
123     {
124         /* nothing to do */
125     }
126
127     /**
128      * Gather the set of snapshot versions found in a particular versioned reference.
129      *
130      * @return the Set of snapshot artifact versions found.
131      * @throws LayoutException
132      * @throws ContentNotFoundException 
133      */
134     public Set<String> gatherSnapshotVersions( ManagedRepositoryContent managedRepository, VersionedReference reference )
135         throws LayoutException, IOException, ContentNotFoundException
136     {
137         Set<String> foundVersions = managedRepository.getVersions( reference );
138
139         // Next gather up the referenced 'latest' versions found in any proxied repositories
140         // maven-metadata-${proxyId}.xml files that may be present.
141
142         // Does this repository have a set of remote proxied repositories?
143         Set<String> proxiedRepoIds = this.proxies.get( managedRepository.getId() );
144
145         if ( CollectionUtils.isNotEmpty( proxiedRepoIds ) )
146         {
147             String baseVersion = VersionUtil.getBaseVersion( reference.getVersion() );
148             baseVersion = baseVersion.substring( 0, baseVersion.indexOf( VersionUtil.SNAPSHOT ) - 1 );
149
150             // Add in the proxied repo version ids too.
151             Iterator<String> it = proxiedRepoIds.iterator();
152             while ( it.hasNext() )
153             {
154                 String proxyId = it.next();
155
156                 ArchivaRepositoryMetadata proxyMetadata = readProxyMetadata( managedRepository, reference, proxyId );
157                 if ( proxyMetadata == null )
158                 {
159                     // There is no proxy metadata, skip it.
160                     continue;
161                 }
162
163                 // Is there some snapshot info?
164                 SnapshotVersion snapshot = proxyMetadata.getSnapshotVersion();
165                 if ( snapshot != null )
166                 {
167                     String timestamp = snapshot.getTimestamp();
168                     int buildNumber = snapshot.getBuildNumber();
169
170                     // Only interested in the timestamp + buildnumber.
171                     if ( StringUtils.isNotBlank( timestamp ) && ( buildNumber > 0 ) )
172                     {
173                         foundVersions.add( baseVersion + "-" + timestamp + "-" + buildNumber );
174                     }
175                 }
176             }
177         }
178
179         return foundVersions;
180     }
181
182     /**
183      * Take a path to a maven-metadata.xml, and attempt to translate it to a VersionedReference.
184      *
185      * @param path
186      * @return
187      */
188     public VersionedReference toVersionedReference( String path )
189         throws RepositoryMetadataException
190     {
191         if ( !path.endsWith( "/" + MAVEN_METADATA ) )
192         {
193             throw new RepositoryMetadataException( "Cannot convert to versioned reference, not a metadata file. " );
194         }
195
196         VersionedReference reference = new VersionedReference();
197
198         String normalizedPath = StringUtils.replace( path, "\\", "/" );
199         String pathParts[] = StringUtils.split( normalizedPath, '/' );
200
201         int versionOffset = pathParts.length - 2;
202         int artifactIdOffset = versionOffset - 1;
203         int groupIdEnd = artifactIdOffset - 1;
204
205         reference.setVersion( pathParts[versionOffset] );
206
207         if ( !hasNumberAnywhere( reference.getVersion() ) )
208         {
209             // Scary check, but without it, all paths are version references;
210             throw new RepositoryMetadataException(
211                                                    "Not a versioned reference, as version id on path has no number in it." );
212         }
213
214         reference.setArtifactId( pathParts[artifactIdOffset] );
215
216         StringBuffer gid = new StringBuffer();
217         for ( int i = 0; i <= groupIdEnd; i++ )
218         {
219             if ( i > 0 )
220             {
221                 gid.append( "." );
222             }
223             gid.append( pathParts[i] );
224         }
225
226         reference.setGroupId( gid.toString() );
227
228         return reference;
229     }
230
231     private boolean hasNumberAnywhere( String version )
232     {
233         return StringUtils.indexOfAny( version, NUMS ) != ( -1 );
234     }
235
236     public ProjectReference toProjectReference( String path )
237         throws RepositoryMetadataException
238     {
239         if ( !path.endsWith( "/" + MAVEN_METADATA ) )
240         {
241             throw new RepositoryMetadataException( "Cannot convert to versioned reference, not a metadata file. " );
242         }
243
244         ProjectReference reference = new ProjectReference();
245
246         String normalizedPath = StringUtils.replace( path, "\\", "/" );
247         String pathParts[] = StringUtils.split( normalizedPath, '/' );
248
249         // Assume last part of the path is the version.
250
251         int artifactIdOffset = pathParts.length - 2;
252         int groupIdEnd = artifactIdOffset - 1;
253
254         reference.setArtifactId( pathParts[artifactIdOffset] );
255
256         StringBuffer gid = new StringBuffer();
257         for ( int i = 0; i <= groupIdEnd; i++ )
258         {
259             if ( i > 0 )
260             {
261                 gid.append( "." );
262             }
263             gid.append( pathParts[i] );
264         }
265
266         reference.setGroupId( gid.toString() );
267
268         return reference;
269     }
270
271     public String toPath( ProjectReference reference )
272     {
273         StringBuffer path = new StringBuffer();
274
275         path.append( formatAsDirectory( reference.getGroupId() ) ).append( PATH_SEPARATOR );
276         path.append( reference.getArtifactId() ).append( PATH_SEPARATOR );
277         path.append( MAVEN_METADATA );
278
279         return path.toString();
280     }
281
282     public String toPath( VersionedReference reference )
283     {
284         StringBuffer path = new StringBuffer();
285
286         path.append( formatAsDirectory( reference.getGroupId() ) ).append( PATH_SEPARATOR );
287         path.append( reference.getArtifactId() ).append( PATH_SEPARATOR );
288         if ( reference.getVersion() != null )
289         {
290             // add the version only if it is present
291             path.append( VersionUtil.getBaseVersion( reference.getVersion() ) ).append( PATH_SEPARATOR );
292         }
293         path.append( MAVEN_METADATA );
294
295         return path.toString();
296     }
297
298     private String formatAsDirectory( String directory )
299     {
300         return directory.replace( GROUP_SEPARATOR, PATH_SEPARATOR );
301     }
302
303     /**
304      * Adjusts a path for a metadata.xml file to its repository specific path.
305      *
306      * @param repository the repository to base new path off of.
307      * @param path       the path to the metadata.xml file to adjust the name of.
308      * @return the newly adjusted path reference to the repository specific metadata path.
309      */
310     public String getRepositorySpecificName( RemoteRepositoryContent repository, String path )
311     {
312         return getRepositorySpecificName( repository.getId(), path );
313     }
314
315     /**
316      * Adjusts a path for a metadata.xml file to its repository specific path.
317      *
318      * @param proxyId the repository id to base new path off of.
319      * @param path    the path to the metadata.xml file to adjust the name of.
320      * @return the newly adjusted path reference to the repository specific metadata path.
321      */
322     public String getRepositorySpecificName( String proxyId, String path )
323     {
324         StringBuffer ret = new StringBuffer();
325
326         int idx = path.lastIndexOf( "/" );
327         if ( idx > 0 )
328         {
329             ret.append( path.substring( 0, idx + 1 ) );
330         }
331
332         // TODO: need to filter out 'bad' characters from the proxy id.
333         ret.append( "maven-metadata-" ).append( proxyId ).append( ".xml" );
334
335         return ret.toString();
336     }
337
338     public void initialize()
339         throws InitializationException
340     {
341         this.artifactPatterns = new ArrayList<String>();
342         this.proxies = new HashMap<String, Set<String>>();
343         initConfigVariables();
344
345         configuration.addChangeListener( this );
346     }
347
348     public ArchivaRepositoryMetadata readProxyMetadata( ManagedRepositoryContent managedRepository,
349                                                         ProjectReference reference, String proxyId )
350     {
351         String metadataPath = getRepositorySpecificName( proxyId, toPath( reference ) );
352         File metadataFile = new File( managedRepository.getRepoRoot(), metadataPath );
353         
354         if ( !metadataFile.exists() || !metadataFile.isFile() )
355         {
356             // Nothing to do. return null.
357             return null;
358         }
359
360         try
361         {
362             return RepositoryMetadataReader.read( metadataFile );
363         }
364         catch ( RepositoryMetadataException e )
365         {
366             // TODO: [monitor] consider a monitor for this event.
367             // TODO: consider a read-redo on monitor return code?
368             log.warn( "Unable to read metadata: " + metadataFile.getAbsolutePath(), e );
369             return null;
370         }
371     }
372
373     public ArchivaRepositoryMetadata readProxyMetadata( ManagedRepositoryContent managedRepository,
374                                                         VersionedReference reference, String proxyId )
375     {
376         String metadataPath = getRepositorySpecificName( proxyId, toPath( reference ) );
377         File metadataFile = new File( managedRepository.getRepoRoot(), metadataPath );
378         
379         if ( !metadataFile.exists() || !metadataFile.isFile() )
380         {
381             // Nothing to do. return null.
382             return null;
383         }
384
385         try
386         {
387             return RepositoryMetadataReader.read( metadataFile );
388         }
389         catch ( RepositoryMetadataException e )
390         {
391             // TODO: [monitor] consider a monitor for this event.
392             // TODO: consider a read-redo on monitor return code?
393             log.warn( "Unable to read metadata: " + metadataFile.getAbsolutePath(), e );
394             return null;
395         }
396     }
397
398     /**
399      * Update the metadata to represent the all versions/plugins of
400      * the provided groupId:artifactId project or group reference,
401      * based off of information present in the repository,
402      * the maven-metadata.xml files, and the proxy/repository specific
403      * metadata file contents.
404      *
405      * We must treat this as a group or a project metadata file as there is no way to know in advance
406      *
407      * @param managedRepository the managed repository where the metadata is kept.
408      * @param reference         the reference to update.
409      * @throws LayoutException
410      * @throws RepositoryMetadataException
411      * @throws IOException
412      * @throws ContentNotFoundException 
413      */
414     public void updateMetadata( ManagedRepositoryContent managedRepository, ProjectReference reference )
415         throws LayoutException, RepositoryMetadataException, IOException, ContentNotFoundException
416     {
417         File metadataFile = new File( managedRepository.getRepoRoot(), toPath( reference ) );
418
419         long lastUpdated = getExistingLastUpdated( metadataFile );
420
421         ArchivaRepositoryMetadata metadata = new ArchivaRepositoryMetadata();
422         metadata.setGroupId( reference.getGroupId() );
423         metadata.setArtifactId( reference.getArtifactId() );
424
425         // Gather up all versions found in the managed repository.
426         Set<String> allVersions = managedRepository.getVersions( reference );
427
428         // Gather up all plugins found in the managed repository.
429         // TODO: do we know this information instead?
430 //        Set<Plugin> allPlugins = managedRepository.getPlugins( reference );
431         Set<Plugin> allPlugins;
432         if ( metadataFile.exists() )
433         {
434             allPlugins = new LinkedHashSet<Plugin>( RepositoryMetadataReader.read( metadataFile ).getPlugins() );
435         }
436         else
437         {
438             allPlugins = new LinkedHashSet<Plugin>();
439         }
440
441         // Does this repository have a set of remote proxied repositories?
442         Set<String> proxiedRepoIds = this.proxies.get( managedRepository.getId() );
443
444         if ( CollectionUtils.isNotEmpty( proxiedRepoIds ) )
445         {
446             // Add in the proxied repo version ids too.
447             Iterator<String> it = proxiedRepoIds.iterator();
448             while ( it.hasNext() )
449             {
450                 String proxyId = it.next();
451
452                 ArchivaRepositoryMetadata proxyMetadata = readProxyMetadata( managedRepository, reference, proxyId );
453                 if ( proxyMetadata != null )
454                 {
455                     allVersions.addAll( proxyMetadata.getAvailableVersions() );
456                     allPlugins.addAll( proxyMetadata.getPlugins() );
457                     long proxyLastUpdated = getLastUpdated( proxyMetadata );
458
459                     lastUpdated = Math.max( lastUpdated, proxyLastUpdated );
460                 }
461             }
462         }
463
464         if ( !allVersions.isEmpty() )
465         {
466             // Sort the versions
467             List<String> sortedVersions = new ArrayList<String>( allVersions );
468             Collections.sort( sortedVersions, VersionComparator.getInstance() );
469
470             // Split the versions into released and snapshots.
471             List<String> releasedVersions = new ArrayList<String>();
472             List<String> snapshotVersions = new ArrayList<String>();
473
474             for ( String version : sortedVersions )
475             {
476                 if ( VersionUtil.isSnapshot( version ) )
477                 {
478                     snapshotVersions.add( version );
479                 }
480                 else
481                 {
482                     releasedVersions.add( version );
483                 }
484             }
485
486             Collections.sort( releasedVersions, VersionComparator.getInstance() );
487             Collections.sort( snapshotVersions, VersionComparator.getInstance() );
488
489             String latestVersion = sortedVersions.get( sortedVersions.size() - 1 );
490             String releaseVersion = null;
491
492             if ( CollectionUtils.isNotEmpty( releasedVersions ) )
493             {
494                 releaseVersion = releasedVersions.get( releasedVersions.size() - 1 );
495             }
496
497             // Add the versions to the metadata model.
498             metadata.setAvailableVersions( sortedVersions );
499
500             metadata.setLatestVersion( latestVersion );
501             metadata.setReleasedVersion( releaseVersion );
502         }
503         else
504         {
505             // Add the plugins to the metadata model.
506             metadata.setPlugins( new ArrayList<Plugin>( allPlugins ) );
507
508             // artifact ID was actually the last part of the group
509             metadata.setGroupId( metadata.getGroupId() + "." + metadata.getArtifactId() );
510             metadata.setArtifactId( null );
511         }
512
513         if ( lastUpdated > 0 )
514         {
515             metadata.setLastUpdatedTimestamp( toLastUpdatedDate( lastUpdated ) );
516         }
517
518         // Save the metadata model to disk.
519         RepositoryMetadataWriter.write( metadata, metadataFile );
520         checksums.update( metadataFile );
521     }
522
523     private Date toLastUpdatedDate( long lastUpdated )
524     {
525         Calendar cal = Calendar.getInstance( DateUtils.UTC_TIME_ZONE );
526         cal.setTimeInMillis( lastUpdated );
527
528         return cal.getTime();
529     }
530     
531     private long toLastUpdatedLong( String timestampString )
532     {
533         try
534         {
535             Date date = lastUpdatedFormat.parse( timestampString );
536             Calendar cal = Calendar.getInstance( DateUtils.UTC_TIME_ZONE );
537             cal.setTime( date );
538
539             return cal.getTimeInMillis();
540         }
541         catch ( ParseException e )
542         {
543             return 0;
544         }
545     }
546
547     private long getLastUpdated( ArchivaRepositoryMetadata metadata )
548     {
549         if ( metadata == null )
550         {
551             // Doesn't exist.
552             return 0;
553         }
554
555         try
556         {
557             String lastUpdated = metadata.getLastUpdated();
558             if ( StringUtils.isBlank( lastUpdated ) )
559             {
560                 // Not set.
561                 return 0;
562             }
563
564             Date lastUpdatedDate = lastUpdatedFormat.parse( lastUpdated );
565             return lastUpdatedDate.getTime();
566         }
567         catch ( ParseException e )
568         {
569             // Bad format on the last updated string.
570             return 0;
571         }
572     }
573
574     private long getExistingLastUpdated( File metadataFile )
575     {
576         if ( !metadataFile.exists() )
577         {
578             // Doesn't exist.
579             return 0;
580         }
581
582         try
583         {
584             ArchivaRepositoryMetadata metadata = RepositoryMetadataReader.read( metadataFile );
585
586             return getLastUpdated( metadata );
587         }
588         catch ( RepositoryMetadataException e )
589         {
590             // Error.
591             return 0;
592         }
593     }
594
595     /**
596      * Update the metadata based on the following rules.
597      * <p/>
598      * 1) If this is a SNAPSHOT reference, then utilize the proxy/repository specific
599      * metadata files to represent the current / latest SNAPSHOT available.
600      * 2) If this is a RELEASE reference, and the metadata file does not exist, then
601      * create the metadata file with contents required of the VersionedReference
602      *
603      * @param managedRepository the managed repository where the metadata is kept.
604      * @param reference         the versioned reference to update
605      * @throws LayoutException
606      * @throws RepositoryMetadataException
607      * @throws IOException
608      * @throws ContentNotFoundException 
609      */
610     public void updateMetadata( ManagedRepositoryContent managedRepository, VersionedReference reference )
611         throws LayoutException, RepositoryMetadataException, IOException, ContentNotFoundException
612     {
613         File metadataFile = new File( managedRepository.getRepoRoot(), toPath( reference ) );
614
615         long lastUpdated = getExistingLastUpdated( metadataFile );
616
617         ArchivaRepositoryMetadata metadata = new ArchivaRepositoryMetadata();
618         metadata.setGroupId( reference.getGroupId() );
619         metadata.setArtifactId( reference.getArtifactId() );
620         
621         if ( VersionUtil.isSnapshot( reference.getVersion() ) )
622         {
623             // Do SNAPSHOT handling.
624             metadata.setVersion( VersionUtil.getBaseVersion( reference.getVersion() ) );
625
626             // Gather up all of the versions found in the reference dir, and any
627             // proxied maven-metadata.xml files.
628             Set<String> snapshotVersions = gatherSnapshotVersions( managedRepository, reference );
629
630             if ( snapshotVersions.isEmpty() )
631             {
632                 throw new ContentNotFoundException( "No snapshot versions found on reference ["
633                     + VersionedReference.toKey( reference ) + "]." );
634             }
635
636             // sort the list to determine to aide in determining the Latest version.
637             List<String> sortedVersions = new ArrayList<String>();
638             sortedVersions.addAll( snapshotVersions );
639             Collections.sort( sortedVersions, new VersionComparator() );
640
641             String latestVersion = sortedVersions.get( sortedVersions.size() - 1 );
642
643             if ( VersionUtil.isUniqueSnapshot( latestVersion ) )
644             {
645                 // The latestVersion will contain the full version string "1.0-alpha-5-20070821.213044-8"
646                 // This needs to be broken down into ${base}-${timestamp}-${build_number}
647
648                 Matcher m = VersionUtil.UNIQUE_SNAPSHOT_PATTERN.matcher( latestVersion );
649                 if ( m.matches() )
650                 {
651                     metadata.setSnapshotVersion( new SnapshotVersion() );
652                     int buildNumber = NumberUtils.toInt( m.group( 3 ), -1 );
653                     metadata.getSnapshotVersion().setBuildNumber( buildNumber );
654
655                     Matcher mtimestamp = VersionUtil.TIMESTAMP_PATTERN.matcher( m.group( 2 ) );
656                     if ( mtimestamp.matches() )
657                     {
658                         String tsDate = mtimestamp.group( 1 );
659                         String tsTime = mtimestamp.group( 2 );
660                         
661                         long snapshotLastUpdated = toLastUpdatedLong( tsDate + tsTime );
662                         
663                         lastUpdated = Math.max( lastUpdated, snapshotLastUpdated );
664                         
665                         metadata.getSnapshotVersion().setTimestamp( m.group( 2 ) );
666                     }
667                 }
668             }
669             else if ( VersionUtil.isGenericSnapshot( latestVersion ) )
670             {
671                 // The latestVersion ends with the generic version string.
672                 // Example: 1.0-alpha-5-SNAPSHOT
673
674                 metadata.setSnapshotVersion( new SnapshotVersion() );
675
676                 /* Disabled due to decision in [MRM-535].
677                  * Do not set metadata.lastUpdated to file.lastModified.
678                  * 
679                  * Should this be the last updated timestamp of the file, or in the case of an 
680                  * archive, the most recent timestamp in the archive?
681                  * 
682                 ArtifactReference artifact = getFirstArtifact( managedRepository, reference );
683
684                 if ( artifact == null )
685                 {
686                     throw new IOException( "Not snapshot artifact found to reference in " + reference );
687                 }
688
689                 File artifactFile = managedRepository.toFile( artifact );
690
691                 if ( artifactFile.exists() )
692                 {
693                     Date lastModified = new Date( artifactFile.lastModified() );
694                     metadata.setLastUpdatedTimestamp( lastModified );
695                 }
696                 */
697             }
698             else
699             {
700                 throw new RepositoryMetadataException( "Unable to process snapshot version <" + latestVersion
701                     + "> reference <" + reference + ">" );
702             }
703         }
704         else
705         {
706             // Do RELEASE handling.
707             metadata.setVersion( reference.getVersion() );
708         }
709
710         // Set last updated
711         if ( lastUpdated > 0 )
712         {
713             metadata.setLastUpdatedTimestamp( toLastUpdatedDate( lastUpdated ) );
714         }
715
716         // Save the metadata model to disk.
717         RepositoryMetadataWriter.write( metadata, metadataFile );
718         checksums.update( metadataFile );
719     }
720
721     private void initConfigVariables()
722     {
723         synchronized ( this.artifactPatterns )
724         {
725             this.artifactPatterns.clear();
726
727             this.artifactPatterns.addAll( filetypes.getFileTypePatterns( FileTypes.ARTIFACTS ) );
728         }
729
730         synchronized ( proxies )
731         {
732             this.proxies.clear();
733
734             List<ProxyConnectorConfiguration> proxyConfigs = configuration.getConfiguration().getProxyConnectors();
735             for( ProxyConnectorConfiguration proxyConfig: proxyConfigs )
736             {
737                 String key = proxyConfig.getSourceRepoId();
738
739                 Set<String> remoteRepoIds = this.proxies.get( key );
740
741                 if ( remoteRepoIds == null )
742                 {
743                     remoteRepoIds = new HashSet<String>();
744                 }
745
746                 remoteRepoIds.add( proxyConfig.getTargetRepoId() );
747
748                 this.proxies.put( key, remoteRepoIds );
749             }
750         }
751     }
752
753     /**
754      * Get the first Artifact found in the provided VersionedReference location.
755      *
756      * @param managedRepository the repository to search within.
757      * @param reference         the reference to the versioned reference to search within
758      * @return the ArtifactReference to the first artifact located within the versioned reference. or null if
759      *         no artifact was found within the versioned reference.
760      * @throws IOException     if the versioned reference is invalid (example: doesn't exist, or isn't a directory)
761      * @throws LayoutException
762      */
763     public ArtifactReference getFirstArtifact( ManagedRepositoryContent managedRepository, VersionedReference reference )
764         throws LayoutException, IOException
765     {
766         String path = toPath( reference );
767
768         int idx = path.lastIndexOf( '/' );
769         if ( idx > 0 )
770         {
771             path = path.substring( 0, idx );
772         }
773
774         File repoDir = new File( managedRepository.getRepoRoot(), path );
775
776         if ( !repoDir.exists() )
777         {
778             throw new IOException( "Unable to gather the list of snapshot versions on a non-existant directory: "
779                 + repoDir.getAbsolutePath() );
780         }
781
782         if ( !repoDir.isDirectory() )
783         {
784             throw new IOException( "Unable to gather the list of snapshot versions on a non-directory: "
785                 + repoDir.getAbsolutePath() );
786         }
787
788         File repoFiles[] = repoDir.listFiles();
789         for ( int i = 0; i < repoFiles.length; i++ )
790         {
791             if ( repoFiles[i].isDirectory() )
792             {
793                 // Skip it. it's a directory.
794                 continue;
795             }
796
797             String relativePath = PathUtil.getRelative( managedRepository.getRepoRoot(), repoFiles[i] );
798
799             if ( matchesArtifactPattern( relativePath ) )
800             {
801                 ArtifactReference artifact = managedRepository.toArtifactReference( relativePath );
802
803                 return artifact;
804             }
805         }
806
807         // No artifact was found.
808         return null;
809     }
810
811     private boolean matchesArtifactPattern( String relativePath )
812     {
813         // Correct the slash pattern.
814         relativePath = relativePath.replace( '\\', '/' );
815
816         Iterator<String> it = this.artifactPatterns.iterator();
817         while ( it.hasNext() )
818         {
819             String pattern = it.next();
820
821             if ( SelectorUtils.matchPath( pattern, relativePath, false ) )
822             {
823                 // Found match
824                 return true;
825             }
826         }
827
828         // No match.
829         return false;
830     }
831 }