]> source.dussan.org Git - archiva.git/blob
3ad07b8ff2279cf88f57975254bdcf393ff97f12
[archiva.git] /
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     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 }