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.archiva.proxy.common.WagonFactory;
31 import org.apache.commons.lang.StringUtils;
32 import org.apache.maven.archiva.common.utils.Slf4JPlexusLogger;
33 import org.apache.maven.archiva.configuration.ArchivaConfiguration;
34 import org.apache.maven.archiva.configuration.ManagedRepositoryConfiguration;
35 import org.apache.maven.archiva.configuration.NetworkProxyConfiguration;
36 import org.apache.maven.archiva.configuration.ProxyConnectorConfiguration;
37 import org.apache.maven.archiva.configuration.RemoteRepositoryConfiguration;
38 import org.apache.maven.artifact.Artifact;
39 import org.apache.maven.artifact.factory.ArtifactFactory;
40 import org.apache.maven.artifact.metadata.ArtifactMetadataRetrievalException;
41 import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
42 import org.apache.maven.artifact.metadata.ResolutionGroup;
43 import org.apache.maven.artifact.repository.ArtifactRepository;
44 import org.apache.maven.artifact.resolver.ArtifactCollector;
45 import org.apache.maven.artifact.resolver.ArtifactResolutionException;
46 import org.apache.maven.artifact.resolver.filter.AndArtifactFilter;
47 import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
48 import org.apache.maven.artifact.resolver.filter.ExcludesArtifactFilter;
49 import org.apache.maven.artifact.versioning.ArtifactVersion;
50 import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
51 import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
52 import org.apache.maven.artifact.versioning.ManagedVersionMap;
53 import org.apache.maven.artifact.versioning.VersionRange;
54 import org.apache.maven.model.Dependency;
55 import org.apache.maven.model.DependencyManagement;
56 import org.apache.maven.model.Exclusion;
57 import org.apache.maven.model.Model;
58 import org.apache.maven.model.building.DefaultModelBuilderFactory;
59 import org.apache.maven.model.building.DefaultModelBuildingRequest;
60 import org.apache.maven.model.building.ModelBuilder;
61 import org.apache.maven.model.building.ModelBuildingException;
62 import org.apache.maven.model.building.ModelBuildingRequest;
63 import org.apache.maven.model.resolution.UnresolvableModelException;
64 import org.apache.maven.shared.dependency.tree.DependencyNode;
65 import org.apache.maven.shared.dependency.tree.DependencyTreeBuilderException;
66 import org.apache.maven.shared.dependency.tree.DependencyTreeResolutionListener;
67 import org.apache.maven.shared.dependency.tree.filter.AncestorOrSelfDependencyNodeFilter;
68 import org.apache.maven.shared.dependency.tree.filter.DependencyNodeFilter;
69 import org.apache.maven.shared.dependency.tree.filter.StateDependencyNodeFilter;
70 import org.apache.maven.shared.dependency.tree.traversal.BuildingDependencyNodeVisitor;
71 import org.apache.maven.shared.dependency.tree.traversal.CollectingDependencyNodeVisitor;
72 import org.apache.maven.shared.dependency.tree.traversal.DependencyNodeVisitor;
73 import org.apache.maven.shared.dependency.tree.traversal.FilteringDependencyNodeVisitor;
74 import org.apache.maven.wagon.proxy.ProxyInfo;
75 import org.slf4j.Logger;
76 import org.slf4j.LoggerFactory;
77 import org.springframework.stereotype.Service;
80 import java.util.ArrayList;
81 import java.util.Collection;
82 import java.util.Collections;
83 import java.util.HashMap;
84 import java.util.HashSet;
85 import java.util.LinkedHashSet;
86 import java.util.List;
89 import javax.annotation.PostConstruct;
90 import javax.inject.Inject;
91 import javax.inject.Named;
94 * Default implementation of <code>DependencyTreeBuilder</code>. Customized wrapper for maven-dependency-tree to use
95 * maven-model-builder instead of maven-project. Note that the role must differ to avoid conflicting with the
96 * maven-shared implementation.
98 * plexus.component role="org.apache.archiva.dependency.tree.maven2.DependencyTreeBuilder" role-hint="maven2"
100 @Service( "dependencyTreeBuilder#maven2" )
101 public class DefaultDependencyTreeBuilder
102 implements DependencyTreeBuilder
105 private Logger log = LoggerFactory.getLogger( getClass() );
110 private ArtifactFactory factory;
115 private ArtifactCollector collector;
120 private ModelBuilder builder;
123 * TODO: can have other types, and this might eventually come through from the main request
128 private RepositorySessionFactory repositorySessionFactory;
131 * plexus.requirement role-hint="maven2"
134 @Named( value = "repositoryPathTranslator#maven2" )
135 private RepositoryPathTranslator pathTranslator;
141 @Named( value = "archivaConfiguration#default" )
142 private ArchivaConfiguration archivaConfiguration;
145 private PlexusSisuBridge plexusSisuBridge;
148 private WagonFactory wagonFactory;
151 public void initialize()
152 throws PlexusSisuBridgeException
154 factory = plexusSisuBridge.lookup( ArtifactFactory.class , "default" );
155 collector = plexusSisuBridge.lookup( ArtifactCollector.class , "default" );
158 DefaultModelBuilderFactory defaultModelBuilderFactory = new DefaultModelBuilderFactory();
159 builder = defaultModelBuilderFactory.newInstance();
162 public void buildDependencyTree( List<String> repositoryIds, String groupId, String artifactId, String version,
163 DependencyNodeVisitor nodeVisitor )
164 throws DependencyTreeBuilderException
166 DependencyTreeResolutionListener listener =
167 new DependencyTreeResolutionListener( new Slf4JPlexusLogger( getClass() ) );
169 Artifact projectArtifact = factory.createProjectArtifact( groupId, artifactId, version );
170 ManagedRepositoryConfiguration repository = findArtifactInRepositories( repositoryIds, projectArtifact );
172 if ( repository == null )
174 // metadata could not be resolved
178 File basedir = new File( repository.getLocation() );
183 // TODO: this is a workaround for a lack of proxy capability in the resolvers - replace when it can all be
184 // handled there. It doesn't cache anything locally!
185 List< RemoteRepositoryConfiguration > remoteRepositories = new ArrayList<RemoteRepositoryConfiguration>();
186 Map<String, ProxyInfo > networkProxies = new HashMap<String, ProxyInfo>();
188 Map<String, List< ProxyConnectorConfiguration >> proxyConnectorsMap = archivaConfiguration.getConfiguration().getProxyConnectorAsMap();
189 List<ProxyConnectorConfiguration> proxyConnectors = proxyConnectorsMap.get( repository.getId() );
190 if( proxyConnectors != null )
192 for( ProxyConnectorConfiguration proxyConnector : proxyConnectors )
194 remoteRepositories.add( archivaConfiguration.getConfiguration().findRemoteRepositoryById( proxyConnector.getTargetRepoId() ) );
196 NetworkProxyConfiguration networkProxyConfig = archivaConfiguration.getConfiguration().getNetworkProxiesAsMap().get(
197 proxyConnector.getProxyId() );
199 if( networkProxyConfig != null )
201 ProxyInfo proxy = new ProxyInfo();
202 proxy.setType( networkProxyConfig.getProtocol() );
203 proxy.setHost( networkProxyConfig.getHost() );
204 proxy.setPort( networkProxyConfig.getPort() );
205 proxy.setUserName( networkProxyConfig.getUsername() );
206 proxy.setPassword( networkProxyConfig.getPassword() );
208 // key/value: remote repo ID/proxy info
209 networkProxies.put( proxyConnector.getTargetRepoId(), proxy );
214 Model model = buildProject( new RepositoryModelResolver( basedir, pathTranslator, wagonFactory, remoteRepositories,
215 networkProxies, repository ), groupId, artifactId, version );
217 Map managedVersions = createManagedVersionMap( model );
219 Set<Artifact> dependencyArtifacts = createArtifacts( model, null );
221 RepositorySession repositorySession = repositorySessionFactory.createSession();
224 ArtifactMetadataSource metadataSource =
225 new MetadataArtifactMetadataSource( repositoryIds, repositorySession );
227 // Note that we don't permit going to external repositories. We don't need to pass in a local and remote
228 // since our metadata source has control over them
229 collector.collect( dependencyArtifacts, projectArtifact, managedVersions, null, null, metadataSource,
230 null, Collections.singletonList( listener ) );
232 //collector.collect( dependencyArtifacts, projectArtifact, null, Collections.<ArtifactRepository>emptyList(),
233 // metadataSource, null, Collections.singletonList( (ResolutionListener) listener ) );
236 Set<Artifact> artifacts, Artifact originatingArtifact,
237 ArtifactRepository localRepository, List<ArtifactRepository> remoteRepositories,
238 ArtifactMetadataSource source, ArtifactFilter filter,
239 List< ResolutionListener > listeners
244 repositorySession.close();
247 DependencyNode rootNode = listener.getRootNode();
249 // TODO: remove the need for this when the serializer can calculate last nodes from visitor calls only
250 DependencyNodeVisitor visitor = new BuildingDependencyNodeVisitor( nodeVisitor );
252 CollectingDependencyNodeVisitor collectingVisitor = new CollectingDependencyNodeVisitor();
253 DependencyNodeVisitor firstPassVisitor =
254 new FilteringDependencyNodeVisitor( collectingVisitor, StateDependencyNodeFilter.INCLUDED );
255 rootNode.accept( firstPassVisitor );
257 DependencyNodeFilter secondPassFilter =
258 new AncestorOrSelfDependencyNodeFilter( collectingVisitor.getNodes() );
259 visitor = new FilteringDependencyNodeVisitor( visitor, secondPassFilter );
261 rootNode.accept( visitor );
263 catch ( ArtifactResolutionException e )
265 throw new DependencyTreeBuilderException( "Cannot build project dependency tree " + e.getMessage(), e );
267 catch ( InvalidVersionSpecificationException e )
269 throw new DependencyTreeBuilderException( "Invalid dependency version for artifact " + projectArtifact );
271 catch ( ModelBuildingException e )
273 throw new DependencyTreeBuilderException( "Cannot build project dependency tree " + e.getMessage(), e );
275 catch ( UnresolvableModelException e )
277 throw new DependencyTreeBuilderException( "Cannot build project dependency tree " + e.getMessage(), e );
281 private ManagedRepositoryConfiguration findArtifactInRepositories( List<String> repositoryIds, Artifact projectArtifact )
283 for ( String repoId : repositoryIds )
285 ManagedRepositoryConfiguration repositoryConfiguration =
286 archivaConfiguration.getConfiguration().findManagedRepositoryById( repoId );
288 File repoDir = new File( repositoryConfiguration.getLocation() );
289 File file = pathTranslator.toFile( repoDir, projectArtifact.getGroupId(), projectArtifact.getArtifactId(),
290 projectArtifact.getBaseVersion(),
291 projectArtifact.getArtifactId() + "-" + projectArtifact.getVersion()
296 return repositoryConfiguration;
302 private Model buildProject( RepositoryModelResolver modelResolver, String groupId, String artifactId,
304 throws ModelBuildingException, UnresolvableModelException
306 ModelBuildingRequest req = new DefaultModelBuildingRequest();
307 req.setProcessPlugins( false );
308 req.setModelSource( modelResolver.resolveModel( groupId, artifactId, version ) );
309 req.setModelResolver( modelResolver );
310 req.setValidationLevel( ModelBuildingRequest.VALIDATION_LEVEL_MINIMAL );
312 return builder.build( req ).getEffectiveModel();
315 // from maven-project to avoid the dependency on it
316 private Set<Artifact> createArtifacts( Model model, ArtifactFilter dependencyFilter )
317 throws InvalidVersionSpecificationException
319 Collection<Dependency> dependencies = model.getDependencies();
320 Set<Artifact> projectArtifacts = new LinkedHashSet<Artifact>( dependencies.size() );
322 for ( Dependency dependency : dependencies )
324 String scope = dependency.getScope();
326 if ( StringUtils.isEmpty( scope ) )
328 scope = Artifact.SCOPE_COMPILE;
330 dependency.setScope( scope );
333 VersionRange versionRange = VersionRange.createFromVersionSpec( dependency.getVersion() );
335 factory.createDependencyArtifact( dependency.getGroupId(), dependency.getArtifactId(), versionRange,
336 dependency.getType(), dependency.getClassifier(), scope, null,
337 dependency.isOptional() );
339 if ( Artifact.SCOPE_SYSTEM.equals( scope ) )
341 artifact.setFile( new File( dependency.getSystemPath() ) );
344 ArtifactFilter artifactFilter = dependencyFilter;
346 // MNG-3769: It would be nice to be able to process relocations here,
347 // so we could have this filtering step apply to post-relocated dependencies.
348 // HOWEVER, this would require a much more invasive POM resolution process
349 // in order to look for relocations, which would make the early steps in
350 // a Maven build way too heavy.
351 if ( artifact != null && ( artifactFilter == null || artifactFilter.include( artifact ) ) )
353 if ( dependency.getExclusions() != null && !dependency.getExclusions().isEmpty() )
355 List<String> exclusions = new ArrayList<String>();
356 for ( Object o : dependency.getExclusions() )
358 Exclusion e = (Exclusion) o;
359 exclusions.add( e.getGroupId() + ":" + e.getArtifactId() );
362 ArtifactFilter newFilter = new ExcludesArtifactFilter( exclusions );
364 if ( artifactFilter != null )
366 AndArtifactFilter filter = new AndArtifactFilter();
367 filter.add( artifactFilter );
368 filter.add( newFilter );
369 artifactFilter = filter;
373 artifactFilter = newFilter;
377 artifact.setDependencyFilter( artifactFilter );
379 projectArtifacts.add( artifact );
383 return projectArtifacts;
387 // from maven-project to avoid the dependency on it
389 private Map createManagedVersionMap( Model model )
390 throws InvalidVersionSpecificationException
392 DependencyManagement dependencyManagement = model.getDependencyManagement();
394 Map<String, Artifact> map = null;
395 List<Dependency> deps;
396 if ( ( dependencyManagement != null ) && ( ( deps = dependencyManagement.getDependencies() ) != null ) && (
399 map = new ManagedVersionMap( map );
401 for ( Dependency dependency : dependencyManagement.getDependencies() )
404 VersionRange versionRange = VersionRange.createFromVersionSpec( dependency.getVersion() );
407 factory.createDependencyArtifact( dependency.getGroupId(), dependency.getArtifactId(), versionRange,
408 dependency.getType(), dependency.getClassifier(),
409 dependency.getScope(), dependency.isOptional() );
411 log.debug( "artifact {}", artifact );
413 // If the dependencyManagement section listed exclusions,
414 // add them to the managed artifacts here so that transitive
415 // dependencies will be excluded if necessary.
416 if ( ( null != dependency.getExclusions() ) && !dependency.getExclusions().isEmpty() )
418 List<String> exclusions = new ArrayList<String>();
420 for ( Exclusion exclusion : dependency.getExclusions() )
422 exclusions.add( exclusion.getGroupId() + ":" + exclusion.getArtifactId() );
424 ExcludesArtifactFilter eaf = new ExcludesArtifactFilter( exclusions );
425 artifact.setDependencyFilter( eaf );
429 artifact.setDependencyFilter( null );
431 map.put( dependency.getManagementKey(), artifact );
436 map = Collections.emptyMap();
442 private class MetadataArtifactMetadataSource
443 implements ArtifactMetadataSource
445 private final List<String> repositoryIds;
447 private final RepositorySession session;
449 private final MetadataResolver resolver;
451 public MetadataArtifactMetadataSource( List<String> repositoryIds, RepositorySession session )
453 this.repositoryIds = repositoryIds;
454 this.session = session;
455 resolver = this.session.getResolver();
458 // modified version from MavenMetadataSource to work with the simpler environment
459 public ResolutionGroup retrieve( Artifact artifact, ArtifactRepository localRepository,
460 List remoteRepositories )
461 throws ArtifactMetadataRetrievalException
463 // TODO: we removed relocation support here. This is something that might need to be generically handled
464 // throughout this module
466 Artifact pomArtifact =
467 factory.createProjectArtifact( artifact.getGroupId(), artifact.getArtifactId(), artifact.getVersion(),
468 artifact.getScope() );
470 ManagedRepositoryConfiguration repository = findArtifactInRepositories( repositoryIds, pomArtifact );
472 Model project = null;
473 if ( !Artifact.SCOPE_SYSTEM.equals( artifact.getScope() ) && repository != null )
475 File basedir = new File( repository.getLocation() );
480 buildProject( new RepositoryModelResolver( basedir, pathTranslator ), artifact.getGroupId(),
481 artifact.getArtifactId(), artifact.getVersion() );
483 catch ( ModelBuildingException e )
485 throw new ArtifactMetadataRetrievalException( e.getMessage(), e, artifact );
487 catch ( UnresolvableModelException e )
489 throw new ArtifactMetadataRetrievalException( e.getMessage(), e, artifact );
493 ResolutionGroup result;
495 if ( project == null )
497 // TODO: we could record this so that it is displayed in the dependency tree as (...) or similar
499 // if the project is null, we encountered an invalid model (read: m1 POM)
500 // we'll just return an empty resolution group.
501 // or used the inherited scope (should that be passed to the buildFromRepository method above?)
502 result = new ResolutionGroup( pomArtifact, Collections.<Artifact>emptySet(), Collections.<ArtifactRepository>emptyList() );
506 Set<Artifact> artifacts = Collections.emptySet();
507 if ( !artifact.getArtifactHandler().isIncludesDependencies() )
511 artifacts = createArtifacts( project, artifact.getDependencyFilter() );
513 catch ( InvalidVersionSpecificationException e )
515 throw new ArtifactMetadataRetrievalException( e.getMessage(), e, artifact );
519 result = new ResolutionGroup( pomArtifact, artifacts, Collections.<ArtifactRepository>emptyList() );
525 public List retrieveAvailableVersions( Artifact artifact, ArtifactRepository localRepository,
526 List remoteRepositories )
527 throws ArtifactMetadataRetrievalException
529 Set<ArtifactVersion> versions = new HashSet<ArtifactVersion>();
530 for ( String repoId : repositoryIds )
532 Collection<String> projectVersions;
535 projectVersions = resolver.resolveProjectVersions( session, repoId, artifact.getGroupId(),
536 artifact.getArtifactId() );
538 catch ( MetadataResolutionException e )
540 throw new ArtifactMetadataRetrievalException( e.getMessage(), e, artifact );
542 for ( String version : projectVersions )
544 versions.add( new DefaultArtifactVersion( version ) );
548 return new ArrayList<ArtifactVersion>( versions );
552 public List<ArtifactVersion> retrieveAvailableVersionsFromDeploymentRepository( Artifact artifact,
553 ArtifactRepository artifactRepository,
554 ArtifactRepository artifactRepository1 )
555 throws ArtifactMetadataRetrievalException
562 public ArtifactFactory getFactory()