]> source.dussan.org Git - archiva.git/blob
fdf57b9cc40427252f1abbe5c2bb2dc40f452e9c
[archiva.git] /
1 package org.apache.archiva.metadata.repository.storage.maven2;
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.metadata.model.ArtifactMetadata;
25 import org.apache.archiva.metadata.model.ProjectMetadata;
26 import org.apache.archiva.metadata.model.ProjectVersionMetadata;
27 import org.apache.archiva.metadata.repository.filter.Filter;
28 import org.apache.archiva.metadata.repository.storage.RepositoryPathTranslator;
29 import org.apache.archiva.metadata.repository.storage.RepositoryStorage;
30 import org.apache.archiva.metadata.repository.storage.RepositoryStorageMetadataInvalidException;
31 import org.apache.archiva.metadata.repository.storage.RepositoryStorageMetadataNotFoundException;
32 import org.apache.archiva.proxy.common.WagonFactory;
33 import org.apache.archiva.reports.RepositoryProblemFacet;
34 import org.apache.archiva.common.utils.VersionUtil;
35 import org.apache.archiva.configuration.ArchivaConfiguration;
36 import org.apache.archiva.configuration.ManagedRepositoryConfiguration;
37 import org.apache.archiva.configuration.NetworkProxyConfiguration;
38 import org.apache.archiva.configuration.ProxyConnectorConfiguration;
39 import org.apache.archiva.configuration.RemoteRepositoryConfiguration;
40 import org.apache.archiva.xml.XMLException;
41 import org.apache.maven.model.CiManagement;
42 import org.apache.maven.model.Dependency;
43 import org.apache.maven.model.IssueManagement;
44 import org.apache.maven.model.License;
45 import org.apache.maven.model.MailingList;
46 import org.apache.maven.model.Model;
47 import org.apache.maven.model.Organization;
48 import org.apache.maven.model.Scm;
49 import org.apache.maven.model.building.DefaultModelBuilderFactory;
50 import org.apache.maven.model.building.DefaultModelBuildingRequest;
51 import org.apache.maven.model.building.ModelBuilder;
52 import org.apache.maven.model.building.ModelBuildingException;
53 import org.apache.maven.model.building.ModelBuildingRequest;
54 import org.apache.maven.model.building.ModelProblem;
55 import org.apache.maven.wagon.proxy.ProxyInfo;
56 import org.slf4j.Logger;
57 import org.slf4j.LoggerFactory;
58 import org.springframework.stereotype.Service;
59
60 import java.io.File;
61 import java.io.FileNotFoundException;
62 import java.io.FilenameFilter;
63 import java.io.IOException;
64 import java.util.ArrayList;
65 import java.util.Arrays;
66 import java.util.Collection;
67 import java.util.Collections;
68 import java.util.Date;
69 import java.util.HashMap;
70 import java.util.List;
71 import java.util.Map;
72 import javax.annotation.PostConstruct;
73 import javax.inject.Inject;
74 import javax.inject.Named;
75
76 /**
77  * Maven 2 repository format storage implementation. This class currently takes parameters to indicate the repository to
78  * deal with rather than being instantiated per-repository.
79  * FIXME: instantiate one per repository and allocate permanently from a factory (which can be obtained within the session).
80  * TODO: finish Maven 1 implementation to prove this API
81  * <p/>
82  * The session is passed in as an argument to obtain any necessary resources, rather than the class being instantiated
83  * within the session in the context of a single managed repository's resolution needs.
84  * <p/>
85  */
86 @Service( "repositoryStorage#maven2" )
87 public class Maven2RepositoryStorage
88     implements RepositoryStorage
89 {
90     /**
91      *
92      */
93     private ModelBuilder builder;
94
95     /**
96      *
97      */
98     @Inject
99     @Named( value = "archivaConfiguration#default" )
100     private ArchivaConfiguration archivaConfiguration;
101
102     /**
103      *
104      */
105     @Inject
106     @Named( value = "repositoryPathTranslator#maven2" )
107     private RepositoryPathTranslator pathTranslator;
108
109     @Inject
110     private WagonFactory wagonFactory;
111
112     private final static Logger log = LoggerFactory.getLogger( Maven2RepositoryStorage.class );
113
114     private static final String METADATA_FILENAME = "maven-metadata.xml";
115
116
117     @PostConstruct
118     public void initialize()
119     {
120         DefaultModelBuilderFactory defaultModelBuilderFactory = new DefaultModelBuilderFactory();
121         builder = defaultModelBuilderFactory.newInstance();
122     }
123
124     public ProjectMetadata readProjectMetadata( String repoId, String namespace, String projectId )
125     {
126         // TODO: could natively implement the "shared model" concept from the browse action to avoid needing it there?
127         return null;
128     }
129
130     public ProjectVersionMetadata readProjectVersionMetadata( String repoId, String namespace, String projectId,
131                                                               String projectVersion )
132         throws RepositoryStorageMetadataNotFoundException, RepositoryStorageMetadataInvalidException
133     {
134         ManagedRepositoryConfiguration repositoryConfiguration =
135             archivaConfiguration.getConfiguration().findManagedRepositoryById( repoId );
136
137         String artifactVersion = projectVersion;
138
139         File basedir = new File( repositoryConfiguration.getLocation() );
140         if ( VersionUtil.isSnapshot( projectVersion ) )
141         {
142             File metadataFile =
143                 pathTranslator.toFile( basedir, namespace, projectId, projectVersion, METADATA_FILENAME );
144             try
145             {
146                 MavenRepositoryMetadata metadata = MavenRepositoryMetadataReader.read( metadataFile );
147
148                 // re-adjust to timestamp if present, otherwise retain the original -SNAPSHOT filename
149                 MavenRepositoryMetadata.Snapshot snapshotVersion = metadata.getSnapshotVersion();
150                 if ( snapshotVersion != null )
151                 {
152                     artifactVersion =
153                         artifactVersion.substring( 0, artifactVersion.length() - 8 ); // remove SNAPSHOT from end
154                     artifactVersion =
155                         artifactVersion + snapshotVersion.getTimestamp() + "-" + snapshotVersion.getBuildNumber();
156                 }
157             }
158             catch ( XMLException e )
159             {
160                 // unable to parse metadata - log it, and continue with the version as the original SNAPSHOT version
161                 log.warn( "Invalid metadata: " + metadataFile + " - " + e.getMessage() );
162             }
163         }
164
165         // TODO: won't work well with some other layouts, might need to convert artifact parts to ID by path translator
166         String id = projectId + "-" + artifactVersion + ".pom";
167         File file = pathTranslator.toFile( basedir, namespace, projectId, projectVersion, id );
168
169         if ( !file.exists() )
170         {
171             // metadata could not be resolved
172             throw new RepositoryStorageMetadataNotFoundException(
173                 "The artifact's POM file '" + file.getAbsolutePath() + "' was missing" );
174         }
175
176         // TODO: this is a workaround until we can properly resolve using proxies as well - this doesn't cache
177         //       anything locally!
178         List<RemoteRepositoryConfiguration> remoteRepositories = new ArrayList<RemoteRepositoryConfiguration>();
179         Map<String, ProxyInfo> networkProxies = new HashMap<String, ProxyInfo>();
180
181         Map<String, List<ProxyConnectorConfiguration>> proxyConnectorsMap = archivaConfiguration.getConfiguration().getProxyConnectorAsMap();
182         List<ProxyConnectorConfiguration> proxyConnectors = proxyConnectorsMap.get( repoId );
183         if( proxyConnectors != null )
184         {
185             for( ProxyConnectorConfiguration proxyConnector : proxyConnectors )
186             {
187                 RemoteRepositoryConfiguration remoteRepoConfig = archivaConfiguration.getConfiguration().findRemoteRepositoryById(
188                     proxyConnector.getTargetRepoId() );
189
190                 if( remoteRepoConfig != null )
191                 {
192                     remoteRepositories.add( remoteRepoConfig );
193
194                     NetworkProxyConfiguration networkProxyConfig = archivaConfiguration.getConfiguration().getNetworkProxiesAsMap().get(
195                         proxyConnector.getProxyId() );
196
197                     if( networkProxyConfig != null )
198                     {
199                         ProxyInfo proxy = new ProxyInfo();
200                         proxy.setType( networkProxyConfig.getProtocol() );
201                         proxy.setHost( networkProxyConfig.getHost() );
202                         proxy.setPort( networkProxyConfig.getPort() );
203                         proxy.setUserName( networkProxyConfig.getUsername() );
204                         proxy.setPassword( networkProxyConfig.getPassword() );
205
206                         // key/value: remote repo ID/proxy info
207                         networkProxies.put( proxyConnector.getTargetRepoId(), proxy );
208                     }
209                 }
210             }
211         }
212
213         ModelBuildingRequest req = new DefaultModelBuildingRequest();
214         req.setProcessPlugins( false );
215         req.setPomFile( file );
216
217         // MRM-1411
218         req.setModelResolver( new RepositoryModelResolver( basedir, pathTranslator, wagonFactory, remoteRepositories,
219                                                            networkProxies, repositoryConfiguration ) );
220         req.setValidationLevel( ModelBuildingRequest.VALIDATION_LEVEL_MINIMAL );
221
222         Model model;
223         try
224         {
225             model = builder.build( req ).getEffectiveModel();
226         }
227         catch ( ModelBuildingException e )
228         {
229             String msg = "The artifact's POM file '" + file + "' was invalid: " + e.getMessage();
230
231             List<ModelProblem> modelProblems = e.getProblems();
232             for( ModelProblem problem : modelProblems )
233             {
234                 // MRM-1411, related to MRM-1335
235                 // this means that the problem was that the parent wasn't resolved!
236                 if( problem.getException() instanceof FileNotFoundException && e.getModelId() != null &&
237                     !e.getModelId().equals( problem.getModelId() ) )
238                 {
239                     log.warn( "The artifact's parent POM file '" + file + "' cannot be resolved. " +
240                         "Using defaults for project version metadata.." );
241
242                     ProjectVersionMetadata metadata = new ProjectVersionMetadata();
243                     metadata.setId( projectVersion );
244
245                     MavenProjectFacet facet = new MavenProjectFacet();
246                     facet.setGroupId( namespace );
247                     facet.setArtifactId( projectId );
248                     facet.setPackaging( "jar" );
249                     metadata.addFacet( facet );
250
251                     String errMsg = "Error in resolving artifact's parent POM file. " + problem.getException().getMessage();
252                     RepositoryProblemFacet repoProblemFacet = new RepositoryProblemFacet();
253                     repoProblemFacet.setRepositoryId( repoId );
254                     repoProblemFacet.setId( repoId );
255                     repoProblemFacet.setMessage( errMsg );
256                     repoProblemFacet.setProblem( errMsg );
257                     repoProblemFacet.setProject( projectId );
258                     repoProblemFacet.setVersion( projectVersion );
259                     repoProblemFacet.setNamespace( namespace );
260                     
261                     metadata.addFacet( repoProblemFacet );
262                     
263                     return metadata;
264                 }
265             }
266
267             throw new RepositoryStorageMetadataInvalidException( "invalid-pom", msg, e );
268         }
269
270         // Check if the POM is in the correct location
271         boolean correctGroupId = namespace.equals( model.getGroupId() );
272         boolean correctArtifactId = projectId.equals( model.getArtifactId() );
273         boolean correctVersion = projectVersion.equals( model.getVersion() );
274         if ( !correctGroupId || !correctArtifactId || !correctVersion )
275         {
276             StringBuilder message = new StringBuilder( "Incorrect POM coordinates in '" + file + "':" );
277             if ( !correctGroupId )
278             {
279                 message.append( "\nIncorrect group ID: " ).append( model.getGroupId() );
280             }
281             if ( !correctArtifactId )
282             {
283                 message.append( "\nIncorrect artifact ID: " ).append( model.getArtifactId() );
284             }
285             if ( !correctVersion )
286             {
287                 message.append( "\nIncorrect version: " ).append( model.getVersion() );
288             }
289
290             throw new RepositoryStorageMetadataInvalidException( "mislocated-pom", message.toString() );
291         }
292
293         ProjectVersionMetadata metadata = new ProjectVersionMetadata();
294         metadata.setCiManagement( convertCiManagement( model.getCiManagement() ) );
295         metadata.setDescription( model.getDescription() );
296         metadata.setId( projectVersion );
297         metadata.setIssueManagement( convertIssueManagement( model.getIssueManagement() ) );
298         metadata.setLicenses( convertLicenses( model.getLicenses() ) );
299         metadata.setMailingLists( convertMailingLists( model.getMailingLists() ) );
300         metadata.setDependencies( convertDependencies( model.getDependencies() ) );
301         metadata.setName( model.getName() );
302         metadata.setOrganization( convertOrganization( model.getOrganization() ) );
303         metadata.setScm( convertScm( model.getScm() ) );
304         metadata.setUrl( model.getUrl() );
305
306         MavenProjectFacet facet = new MavenProjectFacet();
307         facet.setGroupId( model.getGroupId() != null ? model.getGroupId() : model.getParent().getGroupId() );
308         facet.setArtifactId( model.getArtifactId() );
309         facet.setPackaging( model.getPackaging() );
310         if ( model.getParent() != null )
311         {
312             MavenProjectParent parent = new MavenProjectParent();
313             parent.setGroupId( model.getParent().getGroupId() );
314             parent.setArtifactId( model.getParent().getArtifactId() );
315             parent.setVersion( model.getParent().getVersion() );
316             facet.setParent( parent );
317         }
318         metadata.addFacet( facet );
319
320         return metadata;
321     }
322
323     public void setWagonFactory( WagonFactory wagonFactory )
324     {
325         this.wagonFactory = wagonFactory;
326     }
327
328     private List<org.apache.archiva.metadata.model.Dependency> convertDependencies( List<Dependency> dependencies )
329     {
330         List<org.apache.archiva.metadata.model.Dependency> l =
331             new ArrayList<org.apache.archiva.metadata.model.Dependency>();
332         for ( Dependency dependency : dependencies )
333         {
334             org.apache.archiva.metadata.model.Dependency newDependency =
335                 new org.apache.archiva.metadata.model.Dependency();
336             newDependency.setArtifactId( dependency.getArtifactId() );
337             newDependency.setClassifier( dependency.getClassifier() );
338             newDependency.setGroupId( dependency.getGroupId() );
339             newDependency.setOptional( dependency.isOptional() );
340             newDependency.setScope( dependency.getScope() );
341             newDependency.setSystemPath( dependency.getSystemPath() );
342             newDependency.setType( dependency.getType() );
343             newDependency.setVersion( dependency.getVersion() );
344             l.add( newDependency );
345         }
346         return l;
347     }
348
349     private org.apache.archiva.metadata.model.Scm convertScm( Scm scm )
350     {
351         org.apache.archiva.metadata.model.Scm newScm = null;
352         if ( scm != null )
353         {
354             newScm = new org.apache.archiva.metadata.model.Scm();
355             newScm.setConnection( scm.getConnection() );
356             newScm.setDeveloperConnection( scm.getDeveloperConnection() );
357             newScm.setUrl( scm.getUrl() );
358         }
359         return newScm;
360     }
361
362     private org.apache.archiva.metadata.model.Organization convertOrganization( Organization organization )
363     {
364         org.apache.archiva.metadata.model.Organization org = null;
365         if ( organization != null )
366         {
367             org = new org.apache.archiva.metadata.model.Organization();
368             org.setName( organization.getName() );
369             org.setUrl( organization.getUrl() );
370         }
371         return org;
372     }
373
374     private List<org.apache.archiva.metadata.model.License> convertLicenses( List<License> licenses )
375     {
376         List<org.apache.archiva.metadata.model.License> l = new ArrayList<org.apache.archiva.metadata.model.License>();
377         for ( License license : licenses )
378         {
379             org.apache.archiva.metadata.model.License newLicense = new org.apache.archiva.metadata.model.License();
380             newLicense.setName( license.getName() );
381             newLicense.setUrl( license.getUrl() );
382             l.add( newLicense );
383         }
384         return l;
385     }
386
387     private List<org.apache.archiva.metadata.model.MailingList> convertMailingLists( List<MailingList> mailingLists )
388     {
389         List<org.apache.archiva.metadata.model.MailingList> l =
390             new ArrayList<org.apache.archiva.metadata.model.MailingList>();
391         for ( MailingList mailingList : mailingLists )
392         {
393             org.apache.archiva.metadata.model.MailingList newMailingList =
394                 new org.apache.archiva.metadata.model.MailingList();
395             newMailingList.setName( mailingList.getName() );
396             newMailingList.setMainArchiveUrl( mailingList.getArchive() );
397             newMailingList.setPostAddress( mailingList.getPost() );
398             newMailingList.setSubscribeAddress( mailingList.getSubscribe() );
399             newMailingList.setUnsubscribeAddress( mailingList.getUnsubscribe() );
400             newMailingList.setOtherArchives( mailingList.getOtherArchives() );
401             l.add( newMailingList );
402         }
403         return l;
404     }
405
406     private org.apache.archiva.metadata.model.IssueManagement convertIssueManagement( IssueManagement issueManagement )
407     {
408         org.apache.archiva.metadata.model.IssueManagement im = null;
409         if ( issueManagement != null )
410         {
411             im = new org.apache.archiva.metadata.model.IssueManagement();
412             im.setSystem( issueManagement.getSystem() );
413             im.setUrl( issueManagement.getUrl() );
414         }
415         return im;
416     }
417
418     private org.apache.archiva.metadata.model.CiManagement convertCiManagement( CiManagement ciManagement )
419     {
420         org.apache.archiva.metadata.model.CiManagement ci = null;
421         if ( ciManagement != null )
422         {
423             ci = new org.apache.archiva.metadata.model.CiManagement();
424             ci.setSystem( ciManagement.getSystem() );
425             ci.setUrl( ciManagement.getUrl() );
426         }
427         return ci;
428     }
429
430     public Collection<String> listRootNamespaces( String repoId, Filter<String> filter )
431     {
432         File dir = getRepositoryBasedir( repoId );
433
434         return getSortedFiles( dir, filter );
435     }
436
437     private static Collection<String> getSortedFiles( File dir, Filter<String> filter )
438     {
439         List<String> fileNames;
440         String[] files = dir.list( new DirectoryFilter( filter ) );
441         if ( files != null )
442         {
443             fileNames = new ArrayList<String>( Arrays.asList( files ) );
444             Collections.sort( fileNames );
445         }
446         else
447         {
448             fileNames = Collections.emptyList();
449         }
450         return fileNames;
451     }
452
453     private File getRepositoryBasedir( String repoId )
454     {
455         ManagedRepositoryConfiguration repositoryConfiguration =
456             archivaConfiguration.getConfiguration().findManagedRepositoryById( repoId );
457
458         return new File( repositoryConfiguration.getLocation() );
459     }
460
461     public Collection<String> listNamespaces( String repoId, String namespace, Filter<String> filter )
462     {
463         File dir = pathTranslator.toFile( getRepositoryBasedir( repoId ), namespace );
464
465         // scan all the directories which are potential namespaces. Any directories known to be projects are excluded
466         List<String> namespaces = new ArrayList<String>();
467         File[] files = dir.listFiles( new DirectoryFilter( filter ) );
468         if ( files != null )
469         {
470             for ( File file : files )
471             {
472                 if ( !isProject( file, filter ) )
473                 {
474                     namespaces.add( file.getName() );
475                 }
476             }
477         }
478         Collections.sort( namespaces );
479         return namespaces;
480     }
481
482     public Collection<String> listProjects( String repoId, String namespace, Filter<String> filter )
483     {
484         File dir = pathTranslator.toFile( getRepositoryBasedir( repoId ), namespace );
485
486         // scan all directories in the namespace, and only include those that are known to be projects
487         List<String> projects = new ArrayList<String>();
488         File[] files = dir.listFiles( new DirectoryFilter( filter ) );
489         if ( files != null )
490         {
491             for ( File file : files )
492             {
493                 if ( isProject( file, filter ) )
494                 {
495                     projects.add( file.getName() );
496                 }
497             }
498         }
499         Collections.sort( projects );
500         return projects;
501     }
502
503     public Collection<String> listProjectVersions( String repoId, String namespace, String projectId,
504                                                    Filter<String> filter )
505     {
506         File dir = pathTranslator.toFile( getRepositoryBasedir( repoId ), namespace, projectId );
507
508         // all directories in a project directory can be considered a version
509         return getSortedFiles( dir, filter );
510     }
511
512     public Collection<ArtifactMetadata> readArtifactsMetadata( String repoId, String namespace, String projectId,
513                                                                String projectVersion, Filter<String> filter )
514     {
515         File dir = pathTranslator.toFile( getRepositoryBasedir( repoId ), namespace, projectId, projectVersion );
516
517         // all files that are not metadata and not a checksum / signature are considered artifacts
518         File[] files = dir.listFiles( new ArtifactDirectoryFilter( filter ) );
519
520         List<ArtifactMetadata> artifacts = new ArrayList<ArtifactMetadata>();
521         if ( files != null )
522         {
523             for ( File file : files )
524             {
525                 ArtifactMetadata metadata = getArtifactFromFile( repoId, namespace, projectId, projectVersion, file );
526                 artifacts.add( metadata );
527             }
528         }
529         return artifacts;
530     }
531
532     public ArtifactMetadata readArtifactMetadataFromPath( String repoId, String path )
533     {
534         ArtifactMetadata metadata = pathTranslator.getArtifactForPath( repoId, path );
535
536         populateArtifactMetadataFromFile( metadata, new File( getRepositoryBasedir( repoId ), path ) );
537
538         return metadata;
539     }
540
541     private ArtifactMetadata getArtifactFromFile( String repoId, String namespace, String projectId,
542                                                   String projectVersion, File file )
543     {
544         ArtifactMetadata metadata =
545             pathTranslator.getArtifactFromId( repoId, namespace, projectId, projectVersion, file.getName() );
546
547         populateArtifactMetadataFromFile( metadata, file );
548
549         return metadata;
550     }
551
552     private static void populateArtifactMetadataFromFile( ArtifactMetadata metadata, File file )
553     {
554         metadata.setWhenGathered( new Date() );
555         metadata.setFileLastModified( file.lastModified() );
556         ChecksummedFile checksummedFile = new ChecksummedFile( file );
557         try
558         {
559             metadata.setMd5( checksummedFile.calculateChecksum( ChecksumAlgorithm.MD5 ) );
560         }
561         catch ( IOException e )
562         {
563             log.error( "Unable to checksum file " + file + ": " + e.getMessage() );
564         }
565         try
566         {
567             metadata.setSha1( checksummedFile.calculateChecksum( ChecksumAlgorithm.SHA1 ) );
568         }
569         catch ( IOException e )
570         {
571             log.error( "Unable to checksum file " + file + ": " + e.getMessage() );
572         }
573         metadata.setSize( file.length() );
574     }
575
576     private boolean isProject( File dir, Filter<String> filter )
577     {
578         // scan directories for a valid project version subdirectory, meaning this must be a project directory
579         File[] files = dir.listFiles( new DirectoryFilter( filter ) );
580         if ( files != null )
581         {
582             for ( File file : files )
583             {
584                 if ( isProjectVersion( file ) )
585                 {
586                     return true;
587                 }
588             }
589         }
590
591         // if a metadata file is present, check if this is the "artifactId" directory, marking it as a project
592         MavenRepositoryMetadata metadata = readMetadata( dir );
593         if ( metadata != null && dir.getName().equals( metadata.getArtifactId() ) )
594         {
595             return true;
596         }
597
598         return false;
599     }
600
601     private boolean isProjectVersion( File dir )
602     {
603         final String artifactId = dir.getParentFile().getName();
604         final String projectVersion = dir.getName();
605
606         // check if there is a POM artifact file to ensure it is a version directory
607         File[] files;
608         if ( VersionUtil.isSnapshot( projectVersion ) )
609         {
610             files = dir.listFiles( new FilenameFilter()
611             {
612                 public boolean accept( File dir, String name )
613                 {
614                     if ( name.startsWith( artifactId + "-" ) && name.endsWith( ".pom" ) )
615                     {
616                         String v = name.substring( artifactId.length() + 1, name.length() - 4 );
617                         v = VersionUtil.getBaseVersion( v );
618                         if ( v.equals( projectVersion ) )
619                         {
620                             return true;
621                         }
622                     }
623                     return false;
624                 }
625             } );
626         }
627         else
628         {
629             final String pomFile = artifactId + "-" + projectVersion + ".pom";
630             files = dir.listFiles( new FilenameFilter()
631             {
632                 public boolean accept( File dir, String name )
633                 {
634                     return pomFile.equals( name );
635                 }
636             } );
637         }
638         if ( files != null && files.length > 0 )
639         {
640             return true;
641         }
642
643         // if a metadata file is present, check if this is the "version" directory, marking it as a project version
644         MavenRepositoryMetadata metadata = readMetadata( dir );
645         if ( metadata != null && projectVersion.equals( metadata.getVersion() ) )
646         {
647             return true;
648         }
649
650         return false;
651     }
652
653     private MavenRepositoryMetadata readMetadata( File directory )
654     {
655         MavenRepositoryMetadata metadata = null;
656         File metadataFile = new File( directory, METADATA_FILENAME );
657         if ( metadataFile.exists() )
658         {
659             try
660             {
661                 metadata = MavenRepositoryMetadataReader.read( metadataFile );
662             }
663             catch ( XMLException e )
664             {
665                 // ignore missing or invalid metadata
666             }
667         }
668         return metadata;
669     }
670
671     private static class DirectoryFilter
672         implements FilenameFilter
673     {
674         private final Filter<String> filter;
675
676         public DirectoryFilter( Filter<String> filter )
677         {
678             this.filter = filter;
679         }
680
681         public boolean accept( File dir, String name )
682         {
683             if ( !filter.accept( name ) )
684             {
685                 return false;
686             }
687             else if ( name.startsWith( "." ) )
688             {
689                 return false;
690             }
691             else if ( !new File( dir, name ).isDirectory() )
692             {
693                 return false;
694             }
695             return true;
696         }
697     }
698
699     private class ArtifactDirectoryFilter
700         implements FilenameFilter
701     {
702         private final Filter<String> filter;
703
704         public ArtifactDirectoryFilter( Filter<String> filter )
705         {
706             this.filter = filter;
707         }
708
709         public boolean accept( File dir, String name )
710         {
711             // TODO compare to logic in maven-repository-layer
712             if ( !filter.accept( name ) )
713             {
714                 return false;
715             }
716             else if ( name.startsWith( "." ) )
717             {
718                 return false;
719             }
720             else if ( name.endsWith( ".md5" ) || name.endsWith( ".sha1" ) || name.endsWith( ".asc" ) )
721             {
722                 return false;
723             }
724             else if ( name.equals( METADATA_FILENAME ) )
725             {
726                 return false;
727             }
728             else if ( new File( dir, name ).isDirectory() )
729             {
730                 return false;
731             }
732             return true;
733         }
734     }
735 }