]> source.dussan.org Git - archiva.git/blob
02bf6af68002ac097dcba5de21ced233cb3e1e66
[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 java.io.File;
23 import java.io.FilenameFilter;
24 import java.io.IOException;
25 import java.util.ArrayList;
26 import java.util.Arrays;
27 import java.util.Collection;
28 import java.util.Collections;
29 import java.util.Date;
30 import java.util.List;
31
32 import org.apache.archiva.checksum.ChecksumAlgorithm;
33 import org.apache.archiva.checksum.ChecksummedFile;
34 import org.apache.archiva.metadata.model.ArtifactMetadata;
35 import org.apache.archiva.metadata.model.ProjectMetadata;
36 import org.apache.archiva.metadata.model.ProjectVersionMetadata;
37 import org.apache.archiva.metadata.model.ProjectVersionReference;
38 import org.apache.archiva.metadata.repository.MetadataRepository;
39 import org.apache.archiva.metadata.repository.MetadataResolverException;
40 import org.apache.archiva.metadata.repository.filter.AllFilter;
41 import org.apache.archiva.metadata.repository.filter.Filter;
42 import org.apache.archiva.metadata.repository.storage.RepositoryPathTranslator;
43 import org.apache.archiva.metadata.repository.storage.StorageMetadataResolver;
44 import org.apache.archiva.reports.RepositoryProblemFacet;
45 import org.apache.maven.archiva.common.utils.VersionUtil;
46 import org.apache.maven.archiva.configuration.ArchivaConfiguration;
47 import org.apache.maven.archiva.configuration.ManagedRepositoryConfiguration;
48 import org.apache.maven.archiva.xml.XMLException;
49 import org.apache.maven.model.CiManagement;
50 import org.apache.maven.model.Dependency;
51 import org.apache.maven.model.IssueManagement;
52 import org.apache.maven.model.License;
53 import org.apache.maven.model.MailingList;
54 import org.apache.maven.model.Model;
55 import org.apache.maven.model.Organization;
56 import org.apache.maven.model.Scm;
57 import org.apache.maven.model.building.DefaultModelBuildingRequest;
58 import org.apache.maven.model.building.ModelBuilder;
59 import org.apache.maven.model.building.ModelBuildingException;
60 import org.apache.maven.model.building.ModelBuildingRequest;
61 import org.slf4j.Logger;
62 import org.slf4j.LoggerFactory;
63
64 /**
65  * @plexus.component role="org.apache.archiva.metadata.repository.storage.StorageMetadataResolver" role-hint="maven2"
66  */
67 public class Maven2RepositoryMetadataResolver
68     implements StorageMetadataResolver
69 {
70     /**
71      * @plexus.requirement
72      */
73     private ModelBuilder builder;
74
75     /**
76      * @plexus.requirement
77      */
78     private ArchivaConfiguration archivaConfiguration;
79
80     /**
81      * @plexus.requirement role-hint="maven2"
82      */
83     private RepositoryPathTranslator pathTranslator;
84
85     /**
86      * @plexus.requirement
87      */
88     private MetadataRepository metadataRepository;
89
90     private final static Logger log = LoggerFactory.getLogger( Maven2RepositoryMetadataResolver.class );
91
92     private static final String METADATA_FILENAME = "maven-metadata.xml";
93
94     private static final Filter<String> ALL = new AllFilter<String>();
95
96     public ProjectMetadata getProject( String repoId, String namespace, String projectId )
97     {
98         // TODO: could natively implement the "shared model" concept from the browse action to avoid needing it there?
99         return null;
100     }
101
102     public ProjectVersionMetadata getProjectVersion( String repoId, String namespace, String projectId,
103                                                      String projectVersion )
104         throws MetadataResolverException
105     {
106         ManagedRepositoryConfiguration repositoryConfiguration =
107             archivaConfiguration.getConfiguration().findManagedRepositoryById( repoId );
108
109         String artifactVersion = projectVersion;
110
111         File basedir = new File( repositoryConfiguration.getLocation() );
112         if ( VersionUtil.isSnapshot( projectVersion ) )
113         {
114             File metadataFile =
115                 pathTranslator.toFile( basedir, namespace, projectId, projectVersion, METADATA_FILENAME );
116             try
117             {
118                 MavenRepositoryMetadata metadata = MavenRepositoryMetadataReader.read( metadataFile );
119
120                 // re-adjust to timestamp if present, otherwise retain the original -SNAPSHOT filename
121                 MavenRepositoryMetadata.Snapshot snapshotVersion = metadata.getSnapshotVersion();
122                 if ( snapshotVersion != null )
123                 {
124                     artifactVersion =
125                         artifactVersion.substring( 0, artifactVersion.length() - 8 ); // remove SNAPSHOT from end
126                     artifactVersion =
127                         artifactVersion + snapshotVersion.getTimestamp() + "-" + snapshotVersion.getBuildNumber();
128                 }
129             }
130             catch ( XMLException e )
131             {
132                 // unable to parse metadata - log it, and continue with the version as the original SNAPSHOT version
133                 log.warn( "Invalid metadata: " + metadataFile + " - " + e.getMessage() );
134             }
135         }
136
137         String id = projectId + "-" + artifactVersion + ".pom";
138         File file = pathTranslator.toFile( basedir, namespace, projectId, projectVersion, id );
139
140         if ( !file.exists() )
141         {
142             // TODO: an event mechanism would remove coupling to the problem reporting plugin
143             RepositoryProblemFacet problem = new RepositoryProblemFacet();
144             problem.setProblem( "missing-pom" );
145             problem.setMessage( "The artifact's POM file '" + file + "' was missing" );
146             problem.setProject( projectId );
147             problem.setNamespace( namespace );
148             problem.setRepositoryId( repoId );
149             problem.setVersion( projectVersion );
150
151             metadataRepository.addMetadataFacet( repoId, problem );
152
153             // metadata could not be resolved
154             return null;
155         }
156
157         ModelBuildingRequest req = new DefaultModelBuildingRequest();
158         req.setProcessPlugins( false );
159         req.setPomFile( file );
160         req.setModelResolver( new RepositoryModelResolver( basedir, pathTranslator ) );
161         req.setValidationLevel( ModelBuildingRequest.VALIDATION_LEVEL_MINIMAL );
162
163         Model model;
164         try
165         {
166             model = builder.build( req ).getEffectiveModel();
167         }
168         catch ( ModelBuildingException e )
169         {
170             // TODO: an event mechanism would remove coupling to the problem reporting plugin
171             RepositoryProblemFacet problem = new RepositoryProblemFacet();
172             problem.setProblem( "invalid-pom" );
173             problem.setMessage( "The artifact's POM file '" + file + "' was invalid: " + e.getMessage() );
174             problem.setProject( projectId );
175             problem.setNamespace( namespace );
176             problem.setRepositoryId( repoId );
177             problem.setVersion( projectVersion );
178
179             metadataRepository.addMetadataFacet( repoId, problem );
180
181             // metadata could not be resolved
182             return null;
183         }
184
185         ProjectVersionMetadata metadata = new ProjectVersionMetadata();
186         metadata.setCiManagement( convertCiManagement( model.getCiManagement() ) );
187         metadata.setDescription( model.getDescription() );
188         metadata.setId( projectVersion );
189         metadata.setIssueManagement( convertIssueManagement( model.getIssueManagement() ) );
190         metadata.setLicenses( convertLicenses( model.getLicenses() ) );
191         metadata.setMailingLists( convertMailingLists( model.getMailingLists() ) );
192         metadata.setDependencies( convertDependencies( model.getDependencies() ) );
193         metadata.setName( model.getName() );
194         metadata.setOrganization( convertOrganization( model.getOrganization() ) );
195         metadata.setScm( convertScm( model.getScm() ) );
196         metadata.setUrl( model.getUrl() );
197
198         MavenProjectFacet facet = new MavenProjectFacet();
199         facet.setGroupId( model.getGroupId() != null ? model.getGroupId() : model.getParent().getGroupId() );
200         facet.setArtifactId( model.getArtifactId() );
201         facet.setPackaging( model.getPackaging() );
202         if ( model.getParent() != null )
203         {
204             MavenProjectParent parent = new MavenProjectParent();
205             parent.setGroupId( model.getParent().getGroupId() );
206             parent.setArtifactId( model.getParent().getArtifactId() );
207             parent.setVersion( model.getParent().getVersion() );
208             facet.setParent( parent );
209         }
210         metadata.addFacet( facet );
211
212         return metadata;
213     }
214
215     private List<org.apache.archiva.metadata.model.Dependency> convertDependencies( List<Dependency> dependencies )
216     {
217         List<org.apache.archiva.metadata.model.Dependency> l =
218             new ArrayList<org.apache.archiva.metadata.model.Dependency>();
219         for ( Dependency dependency : dependencies )
220         {
221             org.apache.archiva.metadata.model.Dependency newDependency =
222                 new org.apache.archiva.metadata.model.Dependency();
223             newDependency.setArtifactId( dependency.getArtifactId() );
224             newDependency.setClassifier( dependency.getClassifier() );
225             newDependency.setGroupId( dependency.getGroupId() );
226             newDependency.setOptional( dependency.isOptional() );
227             newDependency.setScope( dependency.getScope() );
228             newDependency.setSystemPath( dependency.getSystemPath() );
229             newDependency.setType( dependency.getType() );
230             newDependency.setVersion( dependency.getVersion() );
231             l.add( newDependency );
232         }
233         return l;
234     }
235
236     private org.apache.archiva.metadata.model.Scm convertScm( Scm scm )
237     {
238         org.apache.archiva.metadata.model.Scm newScm = null;
239         if ( scm != null )
240         {
241             newScm = new org.apache.archiva.metadata.model.Scm();
242             newScm.setConnection( scm.getConnection() );
243             newScm.setDeveloperConnection( scm.getDeveloperConnection() );
244             newScm.setUrl( scm.getUrl() );
245         }
246         return newScm;
247     }
248
249     private org.apache.archiva.metadata.model.Organization convertOrganization( Organization organization )
250     {
251         org.apache.archiva.metadata.model.Organization org = null;
252         if ( organization != null )
253         {
254             org = new org.apache.archiva.metadata.model.Organization();
255             org.setName( organization.getName() );
256             org.setUrl( organization.getUrl() );
257         }
258         return org;
259     }
260
261     private List<org.apache.archiva.metadata.model.License> convertLicenses( List<License> licenses )
262     {
263         List<org.apache.archiva.metadata.model.License> l = new ArrayList<org.apache.archiva.metadata.model.License>();
264         for ( License license : licenses )
265         {
266             org.apache.archiva.metadata.model.License newLicense = new org.apache.archiva.metadata.model.License();
267             newLicense.setName( license.getName() );
268             newLicense.setUrl( license.getUrl() );
269             l.add( newLicense );
270         }
271         return l;
272     }
273
274     private List<org.apache.archiva.metadata.model.MailingList> convertMailingLists( List<MailingList> mailingLists )
275     {
276         List<org.apache.archiva.metadata.model.MailingList> l =
277             new ArrayList<org.apache.archiva.metadata.model.MailingList>();
278         for ( MailingList mailingList : mailingLists )
279         {
280             org.apache.archiva.metadata.model.MailingList newMailingList =
281                 new org.apache.archiva.metadata.model.MailingList();
282             newMailingList.setName( mailingList.getName() );
283             newMailingList.setMainArchiveUrl( mailingList.getArchive() );
284             newMailingList.setPostAddress( mailingList.getPost() );
285             newMailingList.setSubscribeAddress( mailingList.getSubscribe() );
286             newMailingList.setUnsubscribeAddress( mailingList.getUnsubscribe() );
287             newMailingList.setOtherArchives( mailingList.getOtherArchives() );
288             l.add( newMailingList );
289         }
290         return l;
291     }
292
293     private org.apache.archiva.metadata.model.IssueManagement convertIssueManagement( IssueManagement issueManagement )
294     {
295         org.apache.archiva.metadata.model.IssueManagement im = null;
296         if ( issueManagement != null )
297         {
298             im = new org.apache.archiva.metadata.model.IssueManagement();
299             im.setSystem( issueManagement.getSystem() );
300             im.setUrl( issueManagement.getUrl() );
301         }
302         return im;
303     }
304
305     private org.apache.archiva.metadata.model.CiManagement convertCiManagement( CiManagement ciManagement )
306     {
307         org.apache.archiva.metadata.model.CiManagement ci = null;
308         if ( ciManagement != null )
309         {
310             ci = new org.apache.archiva.metadata.model.CiManagement();
311             ci.setSystem( ciManagement.getSystem() );
312             ci.setUrl( ciManagement.getUrl() );
313         }
314         return ci;
315     }
316
317     public Collection<String> getArtifactVersions( String repoId, String namespace, String projectId,
318                                                    String projectVersion )
319     {
320         // TODO: useful, but not implemented yet, not called from DefaultMetadataResolver
321         throw new UnsupportedOperationException();
322     }
323
324     public Collection<ProjectVersionReference> getProjectReferences( String repoId, String namespace, String projectId,
325                                                                      String projectVersion )
326     {
327         // Can't be determined on a Maven 2 repository
328         throw new UnsupportedOperationException();
329     }
330
331     public Collection<String> getRootNamespaces( String repoId )
332     {
333         return getRootNamespaces( repoId, ALL );
334     }
335
336     public Collection<String> getRootNamespaces( String repoId, Filter<String> filter )
337     {
338         File dir = getRepositoryBasedir( repoId );
339
340         String[] files = dir.list( new DirectoryFilter( filter ) );
341         return files != null ? Arrays.asList( files ) : Collections.<String>emptyList();
342     }
343
344     private File getRepositoryBasedir( String repoId )
345     {
346         ManagedRepositoryConfiguration repositoryConfiguration =
347             archivaConfiguration.getConfiguration().findManagedRepositoryById( repoId );
348
349         return new File( repositoryConfiguration.getLocation() );
350     }
351
352     public Collection<String> getNamespaces( String repoId, String namespace )
353     {
354         return getNamespaces( repoId, namespace, ALL );
355     }
356
357     public Collection<String> getNamespaces( String repoId, String namespace, Filter<String> filter )
358     {
359         File dir = pathTranslator.toFile( getRepositoryBasedir( repoId ), namespace );
360
361         // scan all the directories which are potential namespaces. Any directories known to be projects are excluded
362         Collection<String> namespaces = new ArrayList<String>();
363         File[] files = dir.listFiles( new DirectoryFilter( filter ) );
364         if ( files != null )
365         {
366             for ( File file : files )
367             {
368                 if ( !isProject( file, filter ) )
369                 {
370                     namespaces.add( file.getName() );
371                 }
372             }
373         }
374         return namespaces;
375     }
376
377     public Collection<String> getProjects( String repoId, String namespace )
378     {
379         return getProjects( repoId, namespace, ALL );
380     }
381
382     public Collection<String> getProjects( String repoId, String namespace, Filter<String> filter )
383     {
384         File dir = pathTranslator.toFile( getRepositoryBasedir( repoId ), namespace );
385
386         // scan all directories in the namespace, and only include those that are known to be projects
387         Collection<String> projects = new ArrayList<String>();
388         File[] files = dir.listFiles( new DirectoryFilter( filter ) );
389         if ( files != null )
390         {
391             for ( File file : files )
392             {
393                 if ( isProject( file, filter ) )
394                 {
395                     projects.add( file.getName() );
396                 }
397             }
398         }
399         return projects;
400     }
401
402     public Collection<String> getProjectVersions( String repoId, String namespace, String projectId )
403     {
404         return getProjectVersions( repoId, namespace, projectId, ALL );
405     }
406
407     public Collection<ArtifactMetadata> getArtifacts( String repoId, String namespace, String projectId,
408                                                       String projectVersion )
409     {
410         return getArtifacts( repoId, namespace, projectId, projectVersion, ALL );
411     }
412
413     public Collection<String> getProjectVersions( String repoId, String namespace, String projectId,
414                                                   Filter<String> filter )
415     {
416         File dir = pathTranslator.toFile( getRepositoryBasedir( repoId ), namespace, projectId );
417
418         // all directories in a project directory can be considered a version
419         String[] files = dir.list( new DirectoryFilter( filter ) );
420         return files != null ? Arrays.asList( files ) : Collections.<String>emptyList();
421     }
422
423     public Collection<ArtifactMetadata> getArtifacts( String repoId, String namespace, String projectId,
424                                                       String projectVersion, Filter<String> filter )
425     {
426         File dir = pathTranslator.toFile( getRepositoryBasedir( repoId ), namespace, projectId, projectVersion );
427
428         // all files that are not metadata and not a checksum / signature are considered artifacts
429         File[] files = dir.listFiles( new ArtifactDirectoryFilter( filter ) );
430
431         List<ArtifactMetadata> artifacts = new ArrayList<ArtifactMetadata>();
432         if ( files != null )
433         {
434             for ( File file : files )
435             {
436                 ArtifactMetadata metadata = new ArtifactMetadata();
437                 metadata.setId( file.getName() );
438                 metadata.setProject( projectId );
439                 metadata.setNamespace( namespace );
440                 metadata.setRepositoryId( repoId );
441                 metadata.setWhenGathered( new Date() );
442                 metadata.setFileLastModified( file.lastModified() );
443                 ChecksummedFile checksummedFile = new ChecksummedFile( file );
444                 try
445                 {
446                     metadata.setMd5( checksummedFile.calculateChecksum( ChecksumAlgorithm.MD5 ) );
447                 }
448                 catch ( IOException e )
449                 {
450                     log.error( "Unable to checksum file " + file + ": " + e.getMessage() );
451                 }
452                 try
453                 {
454                     metadata.setSha1( checksummedFile.calculateChecksum( ChecksumAlgorithm.SHA1 ) );
455                 }
456                 catch ( IOException e )
457                 {
458                     log.error( "Unable to checksum file " + file + ": " + e.getMessage() );
459                 }
460                 metadata.setSize( file.length() );
461                 metadata.setVersion( projectVersion );
462                 artifacts.add( metadata );
463             }
464         }
465         return artifacts;
466     }
467
468     private boolean isProject( File dir, Filter<String> filter )
469     {
470         // scan directories for a valid project version subdirectory, meaning this must be a project directory
471         File[] files = dir.listFiles( new DirectoryFilter( filter ) );
472         if ( files != null )
473         {
474             for ( File file : files )
475             {
476                 if ( isProjectVersion( file ) )
477                 {
478                     return true;
479                 }
480             }
481         }
482
483         // if a metadata file is present, check if this is the "artifactId" directory, marking it as a project
484         MavenRepositoryMetadata metadata = readMetadata( dir );
485         if ( metadata != null && dir.getName().equals( metadata.getArtifactId() ) )
486         {
487             return true;
488         }
489
490         return false;
491     }
492
493     private boolean isProjectVersion( File dir )
494     {
495         final String artifactId = dir.getParentFile().getName();
496         final String projectVersion = dir.getName();
497
498         // check if there is a POM artifact file to ensure it is a version directory
499         File[] files;
500         if ( VersionUtil.isSnapshot( projectVersion ) )
501         {
502             files = dir.listFiles( new FilenameFilter()
503             {
504                 public boolean accept( File dir, String name )
505                 {
506                     if ( name.startsWith( artifactId + "-" ) && name.endsWith( ".pom" ) )
507                     {
508                         String v = name.substring( artifactId.length() + 1, name.length() - 4 );
509                         v = VersionUtil.getBaseVersion( v );
510                         if ( v.equals( projectVersion ) )
511                         {
512                             return true;
513                         }
514                     }
515                     return false;
516                 }
517             } );
518         }
519         else
520         {
521             final String pomFile = artifactId + "-" + projectVersion + ".pom";
522             files = dir.listFiles( new FilenameFilter()
523             {
524                 public boolean accept( File dir, String name )
525                 {
526                     return pomFile.equals( name );
527                 }
528             } );
529         }
530         if ( files != null && files.length > 0 )
531         {
532             return true;
533         }
534
535         // if a metadata file is present, check if this is the "version" directory, marking it as a project version
536         MavenRepositoryMetadata metadata = readMetadata( dir );
537         if ( metadata != null && projectVersion.equals( metadata.getVersion() ) )
538         {
539             return true;
540         }
541
542         return false;
543     }
544
545     private MavenRepositoryMetadata readMetadata( File directory )
546     {
547         MavenRepositoryMetadata metadata = null;
548         File metadataFile = new File( directory, METADATA_FILENAME );
549         if ( metadataFile.exists() )
550         {
551             try
552             {
553                 metadata = MavenRepositoryMetadataReader.read( metadataFile );
554             }
555             catch ( XMLException e )
556             {
557                 // ignore missing or invalid metadata
558             }
559         }
560         return metadata;
561     }
562
563     public void setConfiguration( ArchivaConfiguration configuration )
564     {
565         this.archivaConfiguration = configuration;
566     }
567
568     private static class DirectoryFilter
569         implements FilenameFilter
570     {
571         private final Filter<String> filter;
572
573         public DirectoryFilter( Filter<String> filter )
574         {
575             this.filter = filter;
576         }
577
578         public boolean accept( File dir, String name )
579         {
580             if ( !filter.accept( name ) )
581             {
582                 return false;
583             }
584             else if ( name.startsWith( "." ) )
585             {
586                 return false;
587             }
588             else if ( !new File( dir, name ).isDirectory() )
589             {
590                 return false;
591             }
592             return true;
593         }
594     }
595
596     private class ArtifactDirectoryFilter
597         implements FilenameFilter
598     {
599         private final Filter<String> filter;
600
601         public ArtifactDirectoryFilter( Filter<String> filter )
602         {
603             this.filter = filter;
604         }
605
606         public boolean accept( File dir, String name )
607         {
608             // TODO compare to logic in maven-repository-layer
609             if ( !filter.accept( name ) )
610             {
611                 return false;
612             }
613             else if ( name.startsWith( "." ) )
614             {
615                 return false;
616             }
617             else if ( name.endsWith( ".md5" ) || name.endsWith( ".sha1" ) || name.endsWith( ".asc" ) )
618             {
619                 return false;
620             }
621             else if ( name.equals( METADATA_FILENAME ) )
622             {
623                 return false;
624             }
625             else if ( new File( dir, name ).isDirectory() )
626             {
627                 return false;
628             }
629             return true;
630         }
631     }
632 }