1 package org.apache.archiva.dependency.tree.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
22 import org.apache.archiva.common.plexusbridge.PlexusSisuBridge;
23 import org.apache.archiva.common.plexusbridge.PlexusSisuBridgeException;
24 import org.apache.archiva.metadata.repository.MetadataResolutionException;
25 import org.apache.archiva.metadata.repository.MetadataResolver;
26 import org.apache.archiva.metadata.repository.RepositorySession;
27 import org.apache.archiva.metadata.repository.RepositorySessionFactory;
28 import org.apache.archiva.metadata.repository.storage.RepositoryPathTranslator;
29 import org.apache.archiva.metadata.repository.storage.maven2.RepositoryModelResolver;
30 import org.apache.commons.lang.StringUtils;
31 import org.apache.maven.archiva.common.utils.Slf4JPlexusLogger;
32 import org.apache.maven.archiva.configuration.ArchivaConfiguration;
33 import org.apache.maven.archiva.configuration.ManagedRepositoryConfiguration;
34 import org.apache.maven.artifact.Artifact;
35 import org.apache.maven.artifact.factory.ArtifactFactory;
36 import org.apache.maven.artifact.metadata.ArtifactMetadataRetrievalException;
37 import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
38 import org.apache.maven.artifact.metadata.ResolutionGroup;
39 import org.apache.maven.artifact.repository.ArtifactRepository;
40 import org.apache.maven.artifact.resolver.ArtifactCollector;
41 import org.apache.maven.artifact.resolver.ArtifactResolutionException;
42 import org.apache.maven.artifact.resolver.ResolutionListener;
43 import org.apache.maven.artifact.resolver.filter.AndArtifactFilter;
44 import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
45 import org.apache.maven.artifact.resolver.filter.ExcludesArtifactFilter;
46 import org.apache.maven.artifact.versioning.ArtifactVersion;
47 import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
48 import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
49 import org.apache.maven.artifact.versioning.ManagedVersionMap;
50 import org.apache.maven.artifact.versioning.VersionRange;
51 import org.apache.maven.model.Dependency;
52 import org.apache.maven.model.DependencyManagement;
53 import org.apache.maven.model.Exclusion;
54 import org.apache.maven.model.Model;
55 import org.apache.maven.model.building.DefaultModelBuilderFactory;
56 import org.apache.maven.model.building.DefaultModelBuildingRequest;
57 import org.apache.maven.model.building.ModelBuilder;
58 import org.apache.maven.model.building.ModelBuildingException;
59 import org.apache.maven.model.building.ModelBuildingRequest;
60 import org.apache.maven.model.resolution.UnresolvableModelException;
61 import org.apache.maven.repository.legacy.metadata.MetadataResolutionRequest;
62 import org.apache.maven.shared.dependency.tree.DependencyNode;
63 import org.apache.maven.shared.dependency.tree.DependencyTreeBuilderException;
64 import org.apache.maven.shared.dependency.tree.DependencyTreeResolutionListener;
65 import org.apache.maven.shared.dependency.tree.filter.AncestorOrSelfDependencyNodeFilter;
66 import org.apache.maven.shared.dependency.tree.filter.DependencyNodeFilter;
67 import org.apache.maven.shared.dependency.tree.filter.StateDependencyNodeFilter;
68 import org.apache.maven.shared.dependency.tree.traversal.BuildingDependencyNodeVisitor;
69 import org.apache.maven.shared.dependency.tree.traversal.CollectingDependencyNodeVisitor;
70 import org.apache.maven.shared.dependency.tree.traversal.DependencyNodeVisitor;
71 import org.apache.maven.shared.dependency.tree.traversal.FilteringDependencyNodeVisitor;
72 import org.slf4j.Logger;
73 import org.slf4j.LoggerFactory;
74 import org.springframework.stereotype.Service;
76 import javax.annotation.PostConstruct;
77 import javax.inject.Inject;
78 import javax.inject.Named;
80 import java.util.ArrayList;
81 import java.util.Collection;
82 import java.util.Collections;
83 import java.util.HashSet;
84 import java.util.LinkedHashSet;
85 import java.util.List;
90 * Default implementation of <code>DependencyTreeBuilder</code>. Customized wrapper for maven-dependency-tree to use
91 * maven-model-builder instead of maven-project. Note that the role must differ to avoid conflicting with the
92 * maven-shared implementation.
94 * plexus.component role="org.apache.archiva.dependency.tree.maven2.DependencyTreeBuilder" role-hint="maven2"
96 @Service( "dependencyTreeBuilder#maven2" )
97 public class DefaultDependencyTreeBuilder
98 implements DependencyTreeBuilder
101 private Logger log = LoggerFactory.getLogger( getClass() );
106 private ArtifactFactory factory;
111 private ArtifactCollector collector;
116 private ModelBuilder builder;
119 * TODO: can have other types, and this might eventually come through from the main request
124 private RepositorySessionFactory repositorySessionFactory;
127 * plexus.requirement role-hint="maven2"
130 @Named( value = "repositoryPathTranslator#maven2" )
131 private RepositoryPathTranslator pathTranslator;
137 @Named( value = "archivaConfiguration#default" )
138 private ArchivaConfiguration archivaConfiguration;
141 private PlexusSisuBridge plexusSisuBridge;
144 public void initialize()
145 throws PlexusSisuBridgeException
147 factory = plexusSisuBridge.lookup( ArtifactFactory.class , "default" );
148 collector = plexusSisuBridge.lookup( ArtifactCollector.class , "default" );
151 DefaultModelBuilderFactory defaultModelBuilderFactory = new DefaultModelBuilderFactory();
152 builder = defaultModelBuilderFactory.newInstance();
155 public void buildDependencyTree( List<String> repositoryIds, String groupId, String artifactId, String version,
156 DependencyNodeVisitor nodeVisitor )
157 throws DependencyTreeBuilderException
159 DependencyTreeResolutionListener listener =
160 new DependencyTreeResolutionListener( new Slf4JPlexusLogger( getClass() ) );
162 Artifact projectArtifact = factory.createProjectArtifact( groupId, artifactId, version );
163 File basedir = findArtifactInRepositories( repositoryIds, projectArtifact );
165 if ( basedir == null )
167 // metadata could not be resolved
174 buildProject( new RepositoryModelResolver( basedir, pathTranslator ), groupId, artifactId, version );
176 Map managedVersions = createManagedVersionMap( model );
178 Set<Artifact> dependencyArtifacts = createArtifacts( model, null );
180 RepositorySession repositorySession = repositorySessionFactory.createSession();
183 ArtifactMetadataSource metadataSource =
184 new MetadataArtifactMetadataSource( repositoryIds, repositorySession );
186 // Note that we don't permit going to external repositories. We don't need to pass in a local and remote
187 // since our metadata source has control over them
188 //collector.collect( dependencyArtifacts, projectArtifact, managedVersions, null, null, metadataSource,
189 // null, Collections.singletonList( listener ) );
191 collector.collect( dependencyArtifacts, projectArtifact, null, Collections.<ArtifactRepository>emptyList(),
192 metadataSource, null, Collections.singletonList( (ResolutionListener) listener ) );
195 Set<Artifact> artifacts, Artifact originatingArtifact,
196 ArtifactRepository localRepository, List<ArtifactRepository> remoteRepositories,
197 ArtifactMetadataSource source, ArtifactFilter filter,
198 List< ResolutionListener > listeners
203 repositorySession.close();
206 DependencyNode rootNode = listener.getRootNode();
208 // TODO: remove the need for this when the serializer can calculate last nodes from visitor calls only
209 DependencyNodeVisitor visitor = new BuildingDependencyNodeVisitor( nodeVisitor );
211 CollectingDependencyNodeVisitor collectingVisitor = new CollectingDependencyNodeVisitor();
212 DependencyNodeVisitor firstPassVisitor =
213 new FilteringDependencyNodeVisitor( collectingVisitor, StateDependencyNodeFilter.INCLUDED );
214 rootNode.accept( firstPassVisitor );
216 DependencyNodeFilter secondPassFilter =
217 new AncestorOrSelfDependencyNodeFilter( collectingVisitor.getNodes() );
218 visitor = new FilteringDependencyNodeVisitor( visitor, secondPassFilter );
220 rootNode.accept( visitor );
222 catch ( ArtifactResolutionException e )
224 throw new DependencyTreeBuilderException( "Cannot build project dependency tree " + e.getMessage(), e );
226 catch ( InvalidVersionSpecificationException e )
228 throw new DependencyTreeBuilderException( "Invalid dependency version for artifact " + projectArtifact );
230 catch ( ModelBuildingException e )
232 throw new DependencyTreeBuilderException( "Cannot build project dependency tree " + e.getMessage(), e );
234 catch ( UnresolvableModelException e )
236 throw new DependencyTreeBuilderException( "Cannot build project dependency tree " + e.getMessage(), e );
240 private File findArtifactInRepositories( List<String> repositoryIds, Artifact projectArtifact )
242 for ( String repoId : repositoryIds )
244 ManagedRepositoryConfiguration repositoryConfiguration =
245 archivaConfiguration.getConfiguration().findManagedRepositoryById( repoId );
247 File repoDir = new File( repositoryConfiguration.getLocation() );
248 File file = pathTranslator.toFile( repoDir, projectArtifact.getGroupId(), projectArtifact.getArtifactId(),
249 projectArtifact.getBaseVersion(),
250 projectArtifact.getArtifactId() + "-" + projectArtifact.getVersion()
261 private Model buildProject( RepositoryModelResolver modelResolver, String groupId, String artifactId,
263 throws ModelBuildingException, UnresolvableModelException
265 ModelBuildingRequest req = new DefaultModelBuildingRequest();
266 req.setProcessPlugins( false );
267 req.setModelSource( modelResolver.resolveModel( groupId, artifactId, version ) );
268 req.setModelResolver( modelResolver );
269 req.setValidationLevel( ModelBuildingRequest.VALIDATION_LEVEL_MINIMAL );
271 return builder.build( req ).getEffectiveModel();
274 // from maven-project to avoid the dependency on it
275 private Set<Artifact> createArtifacts( Model model, ArtifactFilter dependencyFilter )
276 throws InvalidVersionSpecificationException
278 Collection<Dependency> dependencies = model.getDependencies();
279 Set<Artifact> projectArtifacts = new LinkedHashSet<Artifact>( dependencies.size() );
281 for ( Dependency dependency : dependencies )
283 String scope = dependency.getScope();
285 if ( StringUtils.isEmpty( scope ) )
287 scope = Artifact.SCOPE_COMPILE;
289 dependency.setScope( scope );
292 VersionRange versionRange = VersionRange.createFromVersionSpec( dependency.getVersion() );
294 factory.createDependencyArtifact( dependency.getGroupId(), dependency.getArtifactId(), versionRange,
295 dependency.getType(), dependency.getClassifier(), scope, null,
296 dependency.isOptional() );
298 if ( Artifact.SCOPE_SYSTEM.equals( scope ) )
300 artifact.setFile( new File( dependency.getSystemPath() ) );
303 ArtifactFilter artifactFilter = dependencyFilter;
305 // MNG-3769: It would be nice to be able to process relocations here,
306 // so we could have this filtering step apply to post-relocated dependencies.
307 // HOWEVER, this would require a much more invasive POM resolution process
308 // in order to look for relocations, which would make the early steps in
309 // a Maven build way too heavy.
310 if ( artifact != null && ( artifactFilter == null || artifactFilter.include( artifact ) ) )
312 if ( dependency.getExclusions() != null && !dependency.getExclusions().isEmpty() )
314 List<String> exclusions = new ArrayList<String>();
315 for ( Object o : dependency.getExclusions() )
317 Exclusion e = (Exclusion) o;
318 exclusions.add( e.getGroupId() + ":" + e.getArtifactId() );
321 ArtifactFilter newFilter = new ExcludesArtifactFilter( exclusions );
323 if ( artifactFilter != null )
325 AndArtifactFilter filter = new AndArtifactFilter();
326 filter.add( artifactFilter );
327 filter.add( newFilter );
328 artifactFilter = filter;
332 artifactFilter = newFilter;
336 artifact.setDependencyFilter( artifactFilter );
338 projectArtifacts.add( artifact );
342 return projectArtifacts;
346 // from maven-project to avoid the dependency on it
348 private Map createManagedVersionMap( Model model )
349 throws InvalidVersionSpecificationException
351 DependencyManagement dependencyManagement = model.getDependencyManagement();
353 Map<String, Artifact> map = null;
354 List<Dependency> deps;
355 if ( ( dependencyManagement != null ) && ( ( deps = dependencyManagement.getDependencies() ) != null ) && (
358 map = new ManagedVersionMap( map );
360 for ( Dependency dependency : dependencyManagement.getDependencies() )
363 VersionRange versionRange = VersionRange.createFromVersionSpec( dependency.getVersion() );
366 factory.createDependencyArtifact( dependency.getGroupId(), dependency.getArtifactId(), versionRange,
367 dependency.getType(), dependency.getClassifier(),
368 dependency.getScope(), dependency.isOptional() );
370 log.debug( "artifact {}", artifact );
372 // If the dependencyManagement section listed exclusions,
373 // add them to the managed artifacts here so that transitive
374 // dependencies will be excluded if necessary.
375 if ( ( null != dependency.getExclusions() ) && !dependency.getExclusions().isEmpty() )
377 List<String> exclusions = new ArrayList<String>();
379 for ( Exclusion exclusion : dependency.getExclusions() )
381 exclusions.add( exclusion.getGroupId() + ":" + exclusion.getArtifactId() );
383 ExcludesArtifactFilter eaf = new ExcludesArtifactFilter( exclusions );
384 artifact.setDependencyFilter( eaf );
388 artifact.setDependencyFilter( null );
390 map.put( dependency.getManagementKey(), artifact );
395 map = Collections.emptyMap();
401 private class MetadataArtifactMetadataSource
402 implements ArtifactMetadataSource
404 private final List<String> repositoryIds;
406 private final RepositorySession session;
408 private final MetadataResolver resolver;
410 public MetadataArtifactMetadataSource( List<String> repositoryIds, RepositorySession session )
412 this.repositoryIds = repositoryIds;
413 this.session = session;
414 resolver = this.session.getResolver();
417 // modified version from MavenMetadataSource to work with the simpler environment
418 public ResolutionGroup retrieve( Artifact artifact, ArtifactRepository localRepository,
419 List remoteRepositories )
420 throws ArtifactMetadataRetrievalException
422 // TODO: we removed relocation support here. This is something that might need to be generically handled
423 // throughout this module
425 Artifact pomArtifact =
426 factory.createProjectArtifact( artifact.getGroupId(), artifact.getArtifactId(), artifact.getVersion(),
427 artifact.getScope() );
429 File basedir = findArtifactInRepositories( repositoryIds, pomArtifact );
431 Model project = null;
432 if ( !Artifact.SCOPE_SYSTEM.equals( artifact.getScope() ) && basedir != null )
437 buildProject( new RepositoryModelResolver( basedir, pathTranslator ), artifact.getGroupId(),
438 artifact.getArtifactId(), artifact.getVersion() );
440 catch ( ModelBuildingException e )
442 throw new ArtifactMetadataRetrievalException( e.getMessage(), e, artifact );
444 catch ( UnresolvableModelException e )
446 throw new ArtifactMetadataRetrievalException( e.getMessage(), e, artifact );
450 ResolutionGroup result;
452 if ( project == null )
454 // TODO: we could record this so that it is displayed in the dependency tree as (...) or similar
456 // if the project is null, we encountered an invalid model (read: m1 POM)
457 // we'll just return an empty resolution group.
458 // or used the inherited scope (should that be passed to the buildFromRepository method above?)
459 result = new ResolutionGroup( pomArtifact, Collections.<Artifact>emptySet(), Collections.<ArtifactRepository>emptyList() );
463 Set<Artifact> artifacts = Collections.emptySet();
464 if ( !artifact.getArtifactHandler().isIncludesDependencies() )
468 artifacts = createArtifacts( project, artifact.getDependencyFilter() );
470 catch ( InvalidVersionSpecificationException e )
472 throw new ArtifactMetadataRetrievalException( e.getMessage(), e, artifact );
476 result = new ResolutionGroup( pomArtifact, artifacts, Collections.<ArtifactRepository>emptyList() );
482 public List retrieveAvailableVersions( Artifact artifact, ArtifactRepository localRepository,
483 List remoteRepositories )
484 throws ArtifactMetadataRetrievalException
486 Set<ArtifactVersion> versions = new HashSet<ArtifactVersion>();
487 for ( String repoId : repositoryIds )
489 Collection<String> projectVersions;
492 projectVersions = resolver.resolveProjectVersions( session, repoId, artifact.getGroupId(),
493 artifact.getArtifactId() );
495 catch ( MetadataResolutionException e )
497 throw new ArtifactMetadataRetrievalException( e.getMessage(), e, artifact );
499 for ( String version : projectVersions )
501 versions.add( new DefaultArtifactVersion( version ) );
505 return new ArrayList<ArtifactVersion>( versions );
508 public ResolutionGroup retrieve( MetadataResolutionRequest metadataResolutionRequest )
509 throws ArtifactMetadataRetrievalException
515 public List<ArtifactVersion> retrieveAvailableVersions( MetadataResolutionRequest metadataResolutionRequest )
516 throws ArtifactMetadataRetrievalException
522 public List<ArtifactVersion> retrieveAvailableVersionsFromDeploymentRepository( Artifact artifact,
523 ArtifactRepository artifactRepository,
524 ArtifactRepository artifactRepository1 )
525 throws ArtifactMetadataRetrievalException
532 public ArtifactFactory getFactory()