1 package org.apache.archiva.metadata.repository.storage.maven2;
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
12 * http://www.apache.org/licenses/LICENSE-2.0
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
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;
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;
65 * @plexus.component role="org.apache.archiva.metadata.repository.storage.StorageMetadataResolver" role-hint="maven2"
67 public class Maven2RepositoryMetadataResolver
68 implements StorageMetadataResolver
73 private ModelBuilder builder;
78 private ArchivaConfiguration archivaConfiguration;
81 * @plexus.requirement role-hint="maven2"
83 private RepositoryPathTranslator pathTranslator;
88 private MetadataRepository metadataRepository;
90 private final static Logger log = LoggerFactory.getLogger( Maven2RepositoryMetadataResolver.class );
92 private static final String METADATA_FILENAME = "maven-metadata.xml";
94 private static final Filter<String> ALL = new AllFilter<String>();
96 public ProjectMetadata getProject( String repoId, String namespace, String projectId )
98 // TODO: could natively implement the "shared model" concept from the browse action to avoid needing it there?
102 public ProjectVersionMetadata getProjectVersion( String repoId, String namespace, String projectId,
103 String projectVersion )
104 throws MetadataResolverException
106 ManagedRepositoryConfiguration repositoryConfiguration =
107 archivaConfiguration.getConfiguration().findManagedRepositoryById( repoId );
109 String artifactVersion = projectVersion;
111 File basedir = new File( repositoryConfiguration.getLocation() );
112 if ( VersionUtil.isSnapshot( projectVersion ) )
115 pathTranslator.toFile( basedir, namespace, projectId, projectVersion, METADATA_FILENAME );
118 MavenRepositoryMetadata metadata = MavenRepositoryMetadataReader.read( metadataFile );
120 // re-adjust to timestamp if present, otherwise retain the original -SNAPSHOT filename
121 MavenRepositoryMetadata.Snapshot snapshotVersion = metadata.getSnapshotVersion();
122 if ( snapshotVersion != null )
125 artifactVersion.substring( 0, artifactVersion.length() - 8 ); // remove SNAPSHOT from end
127 artifactVersion + snapshotVersion.getTimestamp() + "-" + snapshotVersion.getBuildNumber();
130 catch ( XMLException e )
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() );
137 String id = projectId + "-" + artifactVersion + ".pom";
138 File file = pathTranslator.toFile( basedir, namespace, projectId, projectVersion, id );
140 if ( !file.exists() )
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 );
151 metadataRepository.addMetadataFacet( repoId, problem );
153 // metadata could not be resolved
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 );
166 model = builder.build( req ).getEffectiveModel();
168 catch ( ModelBuildingException e )
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 );
179 metadataRepository.addMetadataFacet( repoId, problem );
181 // metadata could not be resolved
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() );
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 )
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 );
210 metadata.addFacet( facet );
215 private List<org.apache.archiva.metadata.model.Dependency> convertDependencies( List<Dependency> dependencies )
217 List<org.apache.archiva.metadata.model.Dependency> l =
218 new ArrayList<org.apache.archiva.metadata.model.Dependency>();
219 for ( Dependency dependency : dependencies )
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 );
236 private org.apache.archiva.metadata.model.Scm convertScm( Scm scm )
238 org.apache.archiva.metadata.model.Scm newScm = null;
241 newScm = new org.apache.archiva.metadata.model.Scm();
242 newScm.setConnection( scm.getConnection() );
243 newScm.setDeveloperConnection( scm.getDeveloperConnection() );
244 newScm.setUrl( scm.getUrl() );
249 private org.apache.archiva.metadata.model.Organization convertOrganization( Organization organization )
251 org.apache.archiva.metadata.model.Organization org = null;
252 if ( organization != null )
254 org = new org.apache.archiva.metadata.model.Organization();
255 org.setName( organization.getName() );
256 org.setUrl( organization.getUrl() );
261 private List<org.apache.archiva.metadata.model.License> convertLicenses( List<License> licenses )
263 List<org.apache.archiva.metadata.model.License> l = new ArrayList<org.apache.archiva.metadata.model.License>();
264 for ( License license : licenses )
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() );
274 private List<org.apache.archiva.metadata.model.MailingList> convertMailingLists( List<MailingList> mailingLists )
276 List<org.apache.archiva.metadata.model.MailingList> l =
277 new ArrayList<org.apache.archiva.metadata.model.MailingList>();
278 for ( MailingList mailingList : mailingLists )
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 );
293 private org.apache.archiva.metadata.model.IssueManagement convertIssueManagement( IssueManagement issueManagement )
295 org.apache.archiva.metadata.model.IssueManagement im = null;
296 if ( issueManagement != null )
298 im = new org.apache.archiva.metadata.model.IssueManagement();
299 im.setSystem( issueManagement.getSystem() );
300 im.setUrl( issueManagement.getUrl() );
305 private org.apache.archiva.metadata.model.CiManagement convertCiManagement( CiManagement ciManagement )
307 org.apache.archiva.metadata.model.CiManagement ci = null;
308 if ( ciManagement != null )
310 ci = new org.apache.archiva.metadata.model.CiManagement();
311 ci.setSystem( ciManagement.getSystem() );
312 ci.setUrl( ciManagement.getUrl() );
317 public Collection<String> getArtifactVersions( String repoId, String namespace, String projectId,
318 String projectVersion )
320 // TODO: useful, but not implemented yet, not called from DefaultMetadataResolver
321 throw new UnsupportedOperationException();
324 public Collection<ProjectVersionReference> getProjectReferences( String repoId, String namespace, String projectId,
325 String projectVersion )
327 // Can't be determined on a Maven 2 repository
328 throw new UnsupportedOperationException();
331 public Collection<String> getRootNamespaces( String repoId )
333 return getRootNamespaces( repoId, ALL );
336 public Collection<String> getRootNamespaces( String repoId, Filter<String> filter )
338 File dir = getRepositoryBasedir( repoId );
340 String[] files = dir.list( new DirectoryFilter( filter ) );
341 return files != null ? Arrays.asList( files ) : Collections.<String>emptyList();
344 private File getRepositoryBasedir( String repoId )
346 ManagedRepositoryConfiguration repositoryConfiguration =
347 archivaConfiguration.getConfiguration().findManagedRepositoryById( repoId );
349 return new File( repositoryConfiguration.getLocation() );
352 public Collection<String> getNamespaces( String repoId, String namespace )
354 return getNamespaces( repoId, namespace, ALL );
357 public Collection<String> getNamespaces( String repoId, String namespace, Filter<String> filter )
359 File dir = pathTranslator.toFile( getRepositoryBasedir( repoId ), namespace );
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 ) );
366 for ( File file : files )
368 if ( !isProject( file, filter ) )
370 namespaces.add( file.getName() );
377 public Collection<String> getProjects( String repoId, String namespace )
379 return getProjects( repoId, namespace, ALL );
382 public Collection<String> getProjects( String repoId, String namespace, Filter<String> filter )
384 File dir = pathTranslator.toFile( getRepositoryBasedir( repoId ), namespace );
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 ) );
391 for ( File file : files )
393 if ( isProject( file, filter ) )
395 projects.add( file.getName() );
402 public Collection<String> getProjectVersions( String repoId, String namespace, String projectId )
404 return getProjectVersions( repoId, namespace, projectId, ALL );
407 public Collection<ArtifactMetadata> getArtifacts( String repoId, String namespace, String projectId,
408 String projectVersion )
410 return getArtifacts( repoId, namespace, projectId, projectVersion, ALL );
413 public Collection<String> getProjectVersions( String repoId, String namespace, String projectId,
414 Filter<String> filter )
416 File dir = pathTranslator.toFile( getRepositoryBasedir( repoId ), namespace, projectId );
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();
423 public Collection<ArtifactMetadata> getArtifacts( String repoId, String namespace, String projectId,
424 String projectVersion, Filter<String> filter )
426 File dir = pathTranslator.toFile( getRepositoryBasedir( repoId ), namespace, projectId, projectVersion );
428 // all files that are not metadata and not a checksum / signature are considered artifacts
429 File[] files = dir.listFiles( new ArtifactDirectoryFilter( filter ) );
431 List<ArtifactMetadata> artifacts = new ArrayList<ArtifactMetadata>();
434 for ( File file : files )
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 );
446 metadata.setMd5( checksummedFile.calculateChecksum( ChecksumAlgorithm.MD5 ) );
448 catch ( IOException e )
450 log.error( "Unable to checksum file " + file + ": " + e.getMessage() );
454 metadata.setSha1( checksummedFile.calculateChecksum( ChecksumAlgorithm.SHA1 ) );
456 catch ( IOException e )
458 log.error( "Unable to checksum file " + file + ": " + e.getMessage() );
460 metadata.setSize( file.length() );
461 metadata.setVersion( projectVersion );
462 artifacts.add( metadata );
468 private boolean isProject( File dir, Filter<String> filter )
470 // scan directories for a valid project version subdirectory, meaning this must be a project directory
471 File[] files = dir.listFiles( new DirectoryFilter( filter ) );
474 for ( File file : files )
476 if ( isProjectVersion( file ) )
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() ) )
493 private boolean isProjectVersion( File dir )
495 final String artifactId = dir.getParentFile().getName();
496 final String projectVersion = dir.getName();
498 // check if there is a POM artifact file to ensure it is a version directory
500 if ( VersionUtil.isSnapshot( projectVersion ) )
502 files = dir.listFiles( new FilenameFilter()
504 public boolean accept( File dir, String name )
506 if ( name.startsWith( artifactId + "-" ) && name.endsWith( ".pom" ) )
508 String v = name.substring( artifactId.length() + 1, name.length() - 4 );
509 v = VersionUtil.getBaseVersion( v );
510 if ( v.equals( projectVersion ) )
521 final String pomFile = artifactId + "-" + projectVersion + ".pom";
522 files = dir.listFiles( new FilenameFilter()
524 public boolean accept( File dir, String name )
526 return pomFile.equals( name );
530 if ( files != null && files.length > 0 )
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() ) )
545 private MavenRepositoryMetadata readMetadata( File directory )
547 MavenRepositoryMetadata metadata = null;
548 File metadataFile = new File( directory, METADATA_FILENAME );
549 if ( metadataFile.exists() )
553 metadata = MavenRepositoryMetadataReader.read( metadataFile );
555 catch ( XMLException e )
557 // ignore missing or invalid metadata
563 public void setConfiguration( ArchivaConfiguration configuration )
565 this.archivaConfiguration = configuration;
568 private static class DirectoryFilter
569 implements FilenameFilter
571 private final Filter<String> filter;
573 public DirectoryFilter( Filter<String> filter )
575 this.filter = filter;
578 public boolean accept( File dir, String name )
580 if ( !filter.accept( name ) )
584 else if ( name.startsWith( "." ) )
588 else if ( !new File( dir, name ).isDirectory() )
596 private class ArtifactDirectoryFilter
597 implements FilenameFilter
599 private final Filter<String> filter;
601 public ArtifactDirectoryFilter( Filter<String> filter )
603 this.filter = filter;
606 public boolean accept( File dir, String name )
608 // TODO compare to logic in maven-repository-layer
609 if ( !filter.accept( name ) )
613 else if ( name.startsWith( "." ) )
617 else if ( name.endsWith( ".md5" ) || name.endsWith( ".sha1" ) || name.endsWith( ".asc" ) )
621 else if ( name.equals( METADATA_FILENAME ) )
625 else if ( new File( dir, name ).isDirectory() )