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