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