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 @Service( "dependencyTreeBuilder#maven2" )
99 public class DefaultDependencyTreeBuilder
100 implements DependencyTreeBuilder
103 private Logger log = LoggerFactory.getLogger( getClass() );
108 private ArtifactFactory factory;
113 private ArtifactCollector collector;
118 private ModelBuilder builder;
121 * TODO: can have other types, and this might eventually come through from the main request
126 private RepositorySessionFactory repositorySessionFactory;
129 * plexus.requirement role-hint="maven2"
132 @Named( value = "repositoryPathTranslator#maven2" )
133 private RepositoryPathTranslator pathTranslator;
139 @Named( value = "archivaConfiguration#default" )
140 private ArchivaConfiguration archivaConfiguration;
143 private PlexusSisuBridge plexusSisuBridge;
146 private WagonFactory wagonFactory;
149 public void initialize()
150 throws PlexusSisuBridgeException
152 factory = plexusSisuBridge.lookup( ArtifactFactory.class , "default" );
153 collector = plexusSisuBridge.lookup( ArtifactCollector.class , "default" );
156 DefaultModelBuilderFactory defaultModelBuilderFactory = new DefaultModelBuilderFactory();
157 builder = defaultModelBuilderFactory.newInstance();
160 public void buildDependencyTree( List<String> repositoryIds, String groupId, String artifactId, String version,
161 DependencyNodeVisitor nodeVisitor )
162 throws DependencyTreeBuilderException
164 DependencyTreeResolutionListener listener =
165 new DependencyTreeResolutionListener( new Slf4JPlexusLogger( getClass() ) );
167 Artifact projectArtifact = factory.createProjectArtifact( groupId, artifactId, version );
168 ManagedRepositoryConfiguration repository = findArtifactInRepositories( repositoryIds, projectArtifact );
170 if ( repository == null )
172 // metadata could not be resolved
176 File basedir = new File( repository.getLocation() );
181 // TODO: this is a workaround for a lack of proxy capability in the resolvers - replace when it can all be
182 // handled there. It doesn't cache anything locally!
183 List< RemoteRepositoryConfiguration > remoteRepositories = new ArrayList<RemoteRepositoryConfiguration>();
184 Map<String, ProxyInfo > networkProxies = new HashMap<String, ProxyInfo>();
186 Map<String, List< ProxyConnectorConfiguration >> proxyConnectorsMap = archivaConfiguration.getConfiguration().getProxyConnectorAsMap();
187 List<ProxyConnectorConfiguration> proxyConnectors = proxyConnectorsMap.get( repository.getId() );
188 if( proxyConnectors != null )
190 for( ProxyConnectorConfiguration proxyConnector : proxyConnectors )
192 remoteRepositories.add( archivaConfiguration.getConfiguration().findRemoteRepositoryById( proxyConnector.getTargetRepoId() ) );
194 NetworkProxyConfiguration networkProxyConfig = archivaConfiguration.getConfiguration().getNetworkProxiesAsMap().get(
195 proxyConnector.getProxyId() );
197 if( networkProxyConfig != null )
199 ProxyInfo proxy = new ProxyInfo();
200 proxy.setType( networkProxyConfig.getProtocol() );
201 proxy.setHost( networkProxyConfig.getHost() );
202 proxy.setPort( networkProxyConfig.getPort() );
203 proxy.setUserName( networkProxyConfig.getUsername() );
204 proxy.setPassword( networkProxyConfig.getPassword() );
206 // key/value: remote repo ID/proxy info
207 networkProxies.put( proxyConnector.getTargetRepoId(), proxy );
212 Model model = buildProject( new RepositoryModelResolver( basedir, pathTranslator, wagonFactory, remoteRepositories,
213 networkProxies, repository ), groupId, artifactId, version );
215 Map managedVersions = createManagedVersionMap( model );
217 Set<Artifact> dependencyArtifacts = createArtifacts( model, null );
219 RepositorySession repositorySession = repositorySessionFactory.createSession();
222 ArtifactMetadataSource metadataSource =
223 new MetadataArtifactMetadataSource( repositoryIds, repositorySession );
225 // Note that we don't permit going to external repositories. We don't need to pass in a local and remote
226 // since our metadata source has control over them
227 collector.collect( dependencyArtifacts, projectArtifact, managedVersions, null, null, metadataSource,
228 null, Collections.singletonList( listener ) );
230 //collector.collect( dependencyArtifacts, projectArtifact, null, Collections.<ArtifactRepository>emptyList(),
231 // metadataSource, null, Collections.singletonList( (ResolutionListener) listener ) );
234 Set<Artifact> artifacts, Artifact originatingArtifact,
235 ArtifactRepository localRepository, List<ArtifactRepository> remoteRepositories,
236 ArtifactMetadataSource source, ArtifactFilter filter,
237 List< ResolutionListener > listeners
242 repositorySession.close();
245 DependencyNode rootNode = listener.getRootNode();
247 // TODO: remove the need for this when the serializer can calculate last nodes from visitor calls only
248 DependencyNodeVisitor visitor = new BuildingDependencyNodeVisitor( nodeVisitor );
250 CollectingDependencyNodeVisitor collectingVisitor = new CollectingDependencyNodeVisitor();
251 DependencyNodeVisitor firstPassVisitor =
252 new FilteringDependencyNodeVisitor( collectingVisitor, StateDependencyNodeFilter.INCLUDED );
253 rootNode.accept( firstPassVisitor );
255 DependencyNodeFilter secondPassFilter =
256 new AncestorOrSelfDependencyNodeFilter( collectingVisitor.getNodes() );
257 visitor = new FilteringDependencyNodeVisitor( visitor, secondPassFilter );
259 rootNode.accept( visitor );
261 catch ( ArtifactResolutionException e )
263 throw new DependencyTreeBuilderException( "Cannot build project dependency tree " + e.getMessage(), e );
265 catch ( InvalidVersionSpecificationException e )
267 throw new DependencyTreeBuilderException( "Invalid dependency version for artifact " + projectArtifact );
269 catch ( ModelBuildingException e )
271 throw new DependencyTreeBuilderException( "Cannot build project dependency tree " + e.getMessage(), e );
273 catch ( UnresolvableModelException e )
275 throw new DependencyTreeBuilderException( "Cannot build project dependency tree " + e.getMessage(), e );
279 private ManagedRepositoryConfiguration findArtifactInRepositories( List<String> repositoryIds, Artifact projectArtifact )
281 for ( String repoId : repositoryIds )
283 ManagedRepositoryConfiguration repositoryConfiguration =
284 archivaConfiguration.getConfiguration().findManagedRepositoryById( repoId );
286 File repoDir = new File( repositoryConfiguration.getLocation() );
287 File file = pathTranslator.toFile( repoDir, projectArtifact.getGroupId(), projectArtifact.getArtifactId(),
288 projectArtifact.getBaseVersion(),
289 projectArtifact.getArtifactId() + "-" + projectArtifact.getVersion()
294 return repositoryConfiguration;
300 private Model buildProject( RepositoryModelResolver modelResolver, String groupId, String artifactId,
302 throws ModelBuildingException, UnresolvableModelException
304 ModelBuildingRequest req = new DefaultModelBuildingRequest();
305 req.setProcessPlugins( false );
306 req.setModelSource( modelResolver.resolveModel( groupId, artifactId, version ) );
307 req.setModelResolver( modelResolver );
308 req.setValidationLevel( ModelBuildingRequest.VALIDATION_LEVEL_MINIMAL );
310 return builder.build( req ).getEffectiveModel();
313 // from maven-project to avoid the dependency on it
314 private Set<Artifact> createArtifacts( Model model, ArtifactFilter dependencyFilter )
315 throws InvalidVersionSpecificationException
317 Collection<Dependency> dependencies = model.getDependencies();
318 Set<Artifact> projectArtifacts = new LinkedHashSet<Artifact>( dependencies.size() );
320 for ( Dependency dependency : dependencies )
322 String scope = dependency.getScope();
324 if ( StringUtils.isEmpty( scope ) )
326 scope = Artifact.SCOPE_COMPILE;
328 dependency.setScope( scope );
331 VersionRange versionRange = VersionRange.createFromVersionSpec( dependency.getVersion() );
333 factory.createDependencyArtifact( dependency.getGroupId(), dependency.getArtifactId(), versionRange,
334 dependency.getType(), dependency.getClassifier(), scope, null,
335 dependency.isOptional() );
337 if ( Artifact.SCOPE_SYSTEM.equals( scope ) )
339 artifact.setFile( new File( dependency.getSystemPath() ) );
342 ArtifactFilter artifactFilter = dependencyFilter;
344 // MNG-3769: It would be nice to be able to process relocations here,
345 // so we could have this filtering step apply to post-relocated dependencies.
346 // HOWEVER, this would require a much more invasive POM resolution process
347 // in order to look for relocations, which would make the early steps in
348 // a Maven build way too heavy.
349 if ( artifact != null && ( artifactFilter == null || artifactFilter.include( artifact ) ) )
351 if ( dependency.getExclusions() != null && !dependency.getExclusions().isEmpty() )
353 List<String> exclusions = new ArrayList<String>();
354 for ( Object o : dependency.getExclusions() )
356 Exclusion e = (Exclusion) o;
357 exclusions.add( e.getGroupId() + ":" + e.getArtifactId() );
360 ArtifactFilter newFilter = new ExcludesArtifactFilter( exclusions );
362 if ( artifactFilter != null )
364 AndArtifactFilter filter = new AndArtifactFilter();
365 filter.add( artifactFilter );
366 filter.add( newFilter );
367 artifactFilter = filter;
371 artifactFilter = newFilter;
375 artifact.setDependencyFilter( artifactFilter );
377 projectArtifacts.add( artifact );
381 return projectArtifacts;
385 // from maven-project to avoid the dependency on it
387 private Map createManagedVersionMap( Model model )
388 throws InvalidVersionSpecificationException
390 DependencyManagement dependencyManagement = model.getDependencyManagement();
392 Map<String, Artifact> map = null;
393 List<Dependency> deps;
394 if ( ( dependencyManagement != null ) && ( ( deps = dependencyManagement.getDependencies() ) != null ) && (
397 map = new ManagedVersionMap( map );
399 for ( Dependency dependency : dependencyManagement.getDependencies() )
402 VersionRange versionRange = VersionRange.createFromVersionSpec( dependency.getVersion() );
405 factory.createDependencyArtifact( dependency.getGroupId(), dependency.getArtifactId(), versionRange,
406 dependency.getType(), dependency.getClassifier(),
407 dependency.getScope(), dependency.isOptional() );
409 log.debug( "artifact {}", artifact );
411 // If the dependencyManagement section listed exclusions,
412 // add them to the managed artifacts here so that transitive
413 // dependencies will be excluded if necessary.
414 if ( ( null != dependency.getExclusions() ) && !dependency.getExclusions().isEmpty() )
416 List<String> exclusions = new ArrayList<String>();
418 for ( Exclusion exclusion : dependency.getExclusions() )
420 exclusions.add( exclusion.getGroupId() + ":" + exclusion.getArtifactId() );
422 ExcludesArtifactFilter eaf = new ExcludesArtifactFilter( exclusions );
423 artifact.setDependencyFilter( eaf );
427 artifact.setDependencyFilter( null );
429 map.put( dependency.getManagementKey(), artifact );
434 map = Collections.emptyMap();
440 private class MetadataArtifactMetadataSource
441 implements ArtifactMetadataSource
443 private final List<String> repositoryIds;
445 private final RepositorySession session;
447 private final MetadataResolver resolver;
449 public MetadataArtifactMetadataSource( List<String> repositoryIds, RepositorySession session )
451 this.repositoryIds = repositoryIds;
452 this.session = session;
453 resolver = this.session.getResolver();
456 // modified version from MavenMetadataSource to work with the simpler environment
457 public ResolutionGroup retrieve( Artifact artifact, ArtifactRepository localRepository,
458 List remoteRepositories )
459 throws ArtifactMetadataRetrievalException
461 // TODO: we removed relocation support here. This is something that might need to be generically handled
462 // throughout this module
464 Artifact pomArtifact =
465 factory.createProjectArtifact( artifact.getGroupId(), artifact.getArtifactId(), artifact.getVersion(),
466 artifact.getScope() );
468 ManagedRepositoryConfiguration repository = findArtifactInRepositories( repositoryIds, pomArtifact );
470 Model project = null;
471 if ( !Artifact.SCOPE_SYSTEM.equals( artifact.getScope() ) && repository != null )
473 File basedir = new File( repository.getLocation() );
478 buildProject( new RepositoryModelResolver( basedir, pathTranslator ), artifact.getGroupId(),
479 artifact.getArtifactId(), artifact.getVersion() );
481 catch ( ModelBuildingException e )
483 throw new ArtifactMetadataRetrievalException( e.getMessage(), e, artifact );
485 catch ( UnresolvableModelException e )
487 throw new ArtifactMetadataRetrievalException( e.getMessage(), e, artifact );
491 ResolutionGroup result;
493 if ( project == null )
495 // TODO: we could record this so that it is displayed in the dependency tree as (...) or similar
497 // if the project is null, we encountered an invalid model (read: m1 POM)
498 // we'll just return an empty resolution group.
499 // or used the inherited scope (should that be passed to the buildFromRepository method above?)
500 result = new ResolutionGroup( pomArtifact, Collections.<Artifact>emptySet(), Collections.<ArtifactRepository>emptyList() );
504 Set<Artifact> artifacts = Collections.emptySet();
505 if ( !artifact.getArtifactHandler().isIncludesDependencies() )
509 artifacts = createArtifacts( project, artifact.getDependencyFilter() );
511 catch ( InvalidVersionSpecificationException e )
513 throw new ArtifactMetadataRetrievalException( e.getMessage(), e, artifact );
517 result = new ResolutionGroup( pomArtifact, artifacts, Collections.<ArtifactRepository>emptyList() );
523 public List retrieveAvailableVersions( Artifact artifact, ArtifactRepository localRepository,
524 List remoteRepositories )
525 throws ArtifactMetadataRetrievalException
527 Set<ArtifactVersion> versions = new HashSet<ArtifactVersion>();
528 for ( String repoId : repositoryIds )
530 Collection<String> projectVersions;
533 projectVersions = resolver.resolveProjectVersions( session, repoId, artifact.getGroupId(),
534 artifact.getArtifactId() );
536 catch ( MetadataResolutionException e )
538 throw new ArtifactMetadataRetrievalException( e.getMessage(), e, artifact );
540 for ( String version : projectVersions )
542 versions.add( new DefaultArtifactVersion( version ) );
546 return new ArrayList<ArtifactVersion>( versions );
550 public List<ArtifactVersion> retrieveAvailableVersionsFromDeploymentRepository( Artifact artifact,
551 ArtifactRepository artifactRepository,
552 ArtifactRepository artifactRepository1 )
553 throws ArtifactMetadataRetrievalException
560 public ArtifactFactory getFactory()