1 package org.apache.archiva.dependency.tree.maven2;
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
22 import org.apache.archiva.admin.model.RepositoryAdminException;
23 import org.apache.archiva.admin.model.beans.ManagedRepository;
24 import org.apache.archiva.admin.model.beans.NetworkProxy;
25 import org.apache.archiva.admin.model.beans.ProxyConnector;
26 import org.apache.archiva.admin.model.beans.RemoteRepository;
27 import org.apache.archiva.admin.model.managed.ManagedRepositoryAdmin;
28 import org.apache.archiva.admin.model.networkproxy.NetworkProxyAdmin;
29 import org.apache.archiva.admin.model.proxyconnector.ProxyConnectorAdmin;
30 import org.apache.archiva.admin.model.remote.RemoteRepositoryAdmin;
31 import org.apache.archiva.common.plexusbridge.PlexusSisuBridge;
32 import org.apache.archiva.common.plexusbridge.PlexusSisuBridgeException;
33 import org.apache.archiva.metadata.repository.storage.RepositoryPathTranslator;
34 import org.apache.archiva.metadata.repository.storage.maven2.RepositoryModelResolver;
35 import org.apache.archiva.proxy.common.WagonFactory;
36 import org.apache.maven.RepositoryUtils;
37 import org.apache.maven.artifact.Artifact;
38 import org.apache.maven.artifact.factory.ArtifactFactory;
39 import org.apache.maven.artifact.versioning.VersionRange;
40 import org.apache.maven.model.DependencyManagement;
41 import org.apache.maven.model.Exclusion;
42 import org.apache.maven.model.Model;
43 import org.apache.maven.model.building.DefaultModelBuilderFactory;
44 import org.apache.maven.model.building.DefaultModelBuildingRequest;
45 import org.apache.maven.model.building.ModelBuilder;
46 import org.apache.maven.model.building.ModelBuildingException;
47 import org.apache.maven.model.building.ModelBuildingRequest;
48 import org.apache.maven.model.resolution.UnresolvableModelException;
49 import org.apache.maven.project.DefaultDependencyResolutionRequest;
50 import org.apache.maven.project.DefaultProjectBuildingRequest;
51 import org.apache.maven.project.DependencyResolutionException;
52 import org.apache.maven.project.DependencyResolutionResult;
53 import org.apache.maven.project.MavenProject;
54 import org.apache.maven.repository.internal.DefaultArtifactDescriptorReader;
55 import org.apache.maven.repository.internal.DefaultVersionRangeResolver;
56 import org.apache.maven.repository.internal.DefaultVersionResolver;
57 import org.apache.maven.repository.internal.MavenRepositorySystemSession;
58 import org.codehaus.plexus.util.StringUtils;
59 import org.slf4j.Logger;
60 import org.slf4j.LoggerFactory;
61 import org.sonatype.aether.RepositorySystem;
62 import org.sonatype.aether.RepositorySystemSession;
63 import org.sonatype.aether.RequestTrace;
64 import org.sonatype.aether.artifact.ArtifactType;
65 import org.sonatype.aether.artifact.ArtifactTypeRegistry;
66 import org.sonatype.aether.collection.CollectRequest;
67 import org.sonatype.aether.collection.CollectResult;
68 import org.sonatype.aether.collection.DependencyCollectionException;
69 import org.sonatype.aether.connector.file.FileRepositoryConnectorFactory;
70 import org.sonatype.aether.graph.Dependency;
71 import org.sonatype.aether.graph.DependencyFilter;
72 import org.sonatype.aether.graph.DependencyVisitor;
73 import org.sonatype.aether.impl.ArtifactDescriptorReader;
74 import org.sonatype.aether.impl.VersionRangeResolver;
75 import org.sonatype.aether.impl.VersionResolver;
76 import org.sonatype.aether.impl.internal.DefaultServiceLocator;
77 import org.sonatype.aether.impl.internal.SimpleLocalRepositoryManager;
78 import org.sonatype.aether.repository.LocalRepository;
79 import org.sonatype.aether.resolution.DependencyRequest;
80 import org.sonatype.aether.spi.connector.ArtifactDownload;
81 import org.sonatype.aether.spi.connector.ArtifactUpload;
82 import org.sonatype.aether.spi.connector.MetadataDownload;
83 import org.sonatype.aether.spi.connector.MetadataUpload;
84 import org.sonatype.aether.spi.connector.RepositoryConnector;
85 import org.sonatype.aether.spi.connector.RepositoryConnectorFactory;
86 import org.sonatype.aether.transfer.NoRepositoryConnectorException;
87 import org.sonatype.aether.util.DefaultRepositorySystemSession;
88 import org.sonatype.aether.util.DefaultRequestTrace;
89 import org.sonatype.aether.util.artifact.ArtifacIdUtils;
90 import org.sonatype.aether.util.artifact.DefaultArtifact;
91 import org.sonatype.aether.util.artifact.JavaScopes;
92 import org.sonatype.aether.version.VersionConstraint;
93 import org.springframework.stereotype.Service;
95 import javax.annotation.PostConstruct;
96 import javax.inject.Inject;
97 import javax.inject.Named;
99 import java.util.ArrayList;
100 import java.util.Collection;
101 import java.util.Collections;
102 import java.util.HashMap;
103 import java.util.IdentityHashMap;
104 import java.util.List;
105 import java.util.Map;
108 * @author Olivier Lamy
110 @Service( "dependencyTreeBuilder#maven3" )
111 public class Maven3DependencyTreeBuilder
113 private Logger log = LoggerFactory.getLogger( getClass() );
116 private PlexusSisuBridge plexusSisuBridge;
119 @Named( value = "repositoryPathTranslator#maven2" )
120 private RepositoryPathTranslator pathTranslator;
123 private WagonFactory wagonFactory;
126 private ManagedRepositoryAdmin managedRepositoryAdmin;
129 private ProxyConnectorAdmin proxyConnectorAdmin;
132 private NetworkProxyAdmin networkProxyAdmin;
135 private RemoteRepositoryAdmin remoteRepositoryAdmin;
137 private ArtifactFactory factory;
139 private ModelBuilder builder;
142 private RepositorySystem repoSystem;
145 public void initialize()
146 throws PlexusSisuBridgeException
148 factory = plexusSisuBridge.lookup( ArtifactFactory.class, "default" );
150 repoSystem = plexusSisuBridge.lookup( RepositorySystem.class );
151 DefaultModelBuilderFactory defaultModelBuilderFactory = new DefaultModelBuilderFactory();
152 builder = defaultModelBuilderFactory.newInstance();
155 public DependencyResolutionResult buildDependencyTree( List<String> repositoryIds, String groupId,
156 String artifactId, String version,
157 DependencyVisitor dependencyVisitor )
160 Artifact projectArtifact = factory.createProjectArtifact( groupId, artifactId, version );
161 ManagedRepository repository = null;
164 repository = findArtifactInRepositories( repositoryIds, projectArtifact );
166 catch ( RepositoryAdminException e )
168 // FIXME better exception
169 throw new Exception( "Cannot build project dependency tree " + e.getMessage(), e );
172 if ( repository == null )
174 // metadata could not be resolved
175 return new DefaultDependencyResolutionResult();
179 // TODO: this is a workaround for a lack of proxy capability in the resolvers - replace when it can all be
180 // handled there. It doesn't cache anything locally!
181 List<RemoteRepository> remoteRepositories = new ArrayList<RemoteRepository>();
182 Map<String, NetworkProxy> networkProxies = new HashMap<String, NetworkProxy>();
184 Map<String, List<ProxyConnector>> proxyConnectorsMap = proxyConnectorAdmin.getProxyConnectorAsMap();
185 List<ProxyConnector> proxyConnectors = proxyConnectorsMap.get( repository.getId() );
186 if ( proxyConnectors != null )
188 for ( ProxyConnector proxyConnector : proxyConnectors )
190 remoteRepositories.add( remoteRepositoryAdmin.getRemoteRepository( proxyConnector.getTargetRepoId() ) );
192 NetworkProxy networkProxyConfig = networkProxyAdmin.getNetworkProxy( proxyConnector.getProxyId() );
194 if ( networkProxyConfig != null )
196 // key/value: remote repo ID/proxy info
197 networkProxies.put( proxyConnector.getTargetRepoId(), networkProxyConfig );
202 Model model = buildProject(
203 new RepositoryModelResolver( repository, pathTranslator, wagonFactory, remoteRepositories, networkProxies,
204 repository ), groupId, artifactId, version );
206 MavenProject project = new MavenProject( model );
208 DefaultRepositorySystemSession repositorySystemSession = new DefaultRepositorySystemSession();
210 // FIXME take care of relative path for getLocation
211 repositorySystemSession.setLocalRepositoryManager(
212 new SimpleLocalRepositoryManager( new File( repository.getLocation() ) ) );
214 DefaultProjectBuildingRequest projectBuildingRequest = new DefaultProjectBuildingRequest();
216 project.setProjectBuildingRequest( projectBuildingRequest );
218 projectBuildingRequest.setRepositorySession( repositorySystemSession );
220 DefaultDependencyResolutionRequest request =
221 new DefaultDependencyResolutionRequest( project, projectBuildingRequest.getRepositorySession() );
223 //DependencyFilter dependencyFilter
224 //request.setResolutionFilter( )
226 //DependencyResolutionResult result = projectDependenciesResolver.resolve( request );
228 //DependencyNode dependencyNode = buildDependencyNode( null, result.getDependencyGraph(), projectArtifact, null );
229 /*DependencyNode dependencyNode = dependencyGraphBuilder.buildDependencyGraph( project, new ArtifactFilter()
231 public boolean include( Artifact artifact )
237 DependencyResolutionResult resolutionResult = resolve( request );
239 log.debug( "dependency graph build" );
241 // FIXME take care of relative path
242 test( repository.getLocation(), groupId, artifactId, version, dependencyVisitor );
244 return resolutionResult;
247 private DependencyResolutionResult resolve( DefaultDependencyResolutionRequest request )
248 throws DependencyResolutionException
251 RequestTrace trace = DefaultRequestTrace.newChild( null, request );
253 DefaultDependencyResolutionResult result = new DefaultDependencyResolutionResult();
255 MavenProject project = request.getMavenProject();
256 RepositorySystemSession session = request.getRepositorySession();
257 DependencyFilter filter = request.getResolutionFilter();
259 ArtifactTypeRegistry stereotypes = session.getArtifactTypeRegistry();
261 CollectRequest collect = new CollectRequest();
262 collect.setRequestContext( "project" );
263 collect.setRepositories( project.getRemoteProjectRepositories() );
265 if ( project.getDependencyArtifacts() == null )
267 for ( org.apache.maven.model.Dependency dependency : project.getDependencies() )
269 if ( StringUtils.isEmpty( dependency.getGroupId() ) || StringUtils.isEmpty( dependency.getArtifactId() )
270 || StringUtils.isEmpty( dependency.getVersion() ) )
272 // guard against case where best-effort resolution for invalid models is requested
275 collect.addDependency( RepositoryUtils.toDependency( dependency, stereotypes ) );
280 Map<String, org.apache.maven.model.Dependency> dependencies =
281 new HashMap<String, org.apache.maven.model.Dependency>();
282 for ( org.apache.maven.model.Dependency dependency : project.getDependencies() )
284 String classifier = dependency.getClassifier();
285 if ( classifier == null )
287 ArtifactType type = stereotypes.get( dependency.getType() );
290 classifier = type.getClassifier();
293 String key = ArtifacIdUtils.toVersionlessId( dependency.getGroupId(), dependency.getArtifactId(),
294 dependency.getType(), classifier );
295 dependencies.put( key, dependency );
297 for ( Artifact artifact : project.getDependencyArtifacts() )
299 String key = artifact.getDependencyConflictId();
300 org.apache.maven.model.Dependency dependency = dependencies.get( key );
301 Collection<Exclusion> exclusions = dependency != null ? dependency.getExclusions() : null;
302 org.sonatype.aether.graph.Dependency dep = RepositoryUtils.toDependency( artifact, exclusions );
303 if ( !JavaScopes.SYSTEM.equals( dep.getScope() ) && dep.getArtifact().getFile() != null )
305 // enable re-resolution
306 org.sonatype.aether.artifact.Artifact art = dep.getArtifact();
307 art = art.setFile( null ).setVersion( art.getBaseVersion() );
308 dep = dep.setArtifact( art );
310 collect.addDependency( dep );
314 DependencyManagement depMngt = project.getDependencyManagement();
315 if ( depMngt != null )
317 for ( org.apache.maven.model.Dependency dependency : depMngt.getDependencies() )
319 collect.addManagedDependency( RepositoryUtils.toDependency( dependency, stereotypes ) );
323 collect.setRoot( new org.sonatype.aether.graph.Dependency(
324 new org.sonatype.aether.util.artifact.DefaultArtifact( project.getGroupId(), project.getArtifactId(), null,
325 project.getVersion() ), "compile" ) );
327 DependencyRequest depRequest = new DependencyRequest( collect, filter );
328 depRequest.setTrace( trace );
330 org.sonatype.aether.graph.DependencyNode node;
333 collect.setTrace( DefaultRequestTrace.newChild( trace, depRequest ) );
334 node = repoSystem.collectDependencies( session, collect ).getRoot();
335 result.setDependencyGraph( node );
337 catch ( DependencyCollectionException e )
339 result.setDependencyGraph( e.getResult().getRoot() );
340 result.setCollectionErrors( e.getResult().getExceptions() );
342 throw new DependencyResolutionException( result,
343 "Could not resolve dependencies for project " + project.getId()
344 + ": " + e.getMessage(), e );
347 depRequest.setRoot( node );
352 private void test( String localRepoDir, String groupId, String artifactId, String version,
353 DependencyVisitor dependencyVisitor )
356 RepositorySystem system = newRepositorySystem();
358 RepositorySystemSession session = newRepositorySystemSession( system, localRepoDir );
360 org.sonatype.aether.artifact.Artifact artifact =
361 new DefaultArtifact( groupId + ":" + artifactId + ":" + version );
363 //RemoteRepository repo = Booter.newCentralRepository();
365 CollectRequest collectRequest = new CollectRequest();
366 collectRequest.setRoot( new Dependency( artifact, "" ) );
367 //collectRequest.addRepository( repo );
371 CollectResult collectResult = system.collectDependencies( session, collectRequest );
372 collectResult.getRoot().accept( dependencyVisitor );
375 catch ( DependencyCollectionException e )
377 log.error( e.getMessage(), e );
383 public static class MyFileRepositoryConnectorFactory
384 extends FileRepositoryConnectorFactory
387 public MyFileRepositoryConnectorFactory()
392 public RepositoryConnector newInstance( RepositorySystemSession session,
393 org.sonatype.aether.repository.RemoteRepository repository )
394 throws NoRepositoryConnectorException
399 return super.newInstance( session, repository );
401 catch ( NoRepositoryConnectorException e )
406 return new RepositoryConnector()
409 private Logger log = LoggerFactory.getLogger( getClass() );
411 public void get( Collection<? extends ArtifactDownload> artifactDownloads,
412 Collection<? extends MetadataDownload> metadataDownloads )
417 public void put( Collection<? extends ArtifactUpload> artifactUploads,
418 Collection<? extends MetadataUpload> metadataUploads )
425 log.debug( "close" );
431 public static RepositorySystem newRepositorySystem()
433 DefaultServiceLocator locator = new DefaultServiceLocator();
434 locator.addService( RepositoryConnectorFactory.class,
435 MyFileRepositoryConnectorFactory.class );// FileRepositoryConnectorFactory.class );
436 locator.addService( VersionResolver.class, DefaultVersionResolver.class );
437 locator.addService( VersionRangeResolver.class, DefaultVersionRangeResolver.class );
438 locator.addService( ArtifactDescriptorReader.class, DefaultArtifactDescriptorReader.class );
439 //locator.addService( RepositoryConnectorFactory.class, WagonRepositoryConnectorFactory.class );
440 //locator.setServices( WagonProvider.class, );
442 return locator.getService( RepositorySystem.class );
445 public static RepositorySystemSession newRepositorySystemSession( RepositorySystem system, String localRepoDir )
447 MavenRepositorySystemSession session = new MavenRepositorySystemSession();
449 LocalRepository localRepo = new LocalRepository( localRepoDir );
450 session.setLocalRepositoryManager( system.newLocalRepositoryManager( localRepo ) );
452 //session.setTransferListener( new ConsoleTransferListener() );
453 //session.setRepositoryListener( new ConsoleRepositoryListener() );
458 private String getVersionSelectedFromRange( VersionConstraint constraint )
460 if ( ( constraint == null ) || ( constraint.getVersion() != null ) )
465 StringBuilder sb = new StringBuilder();
466 for ( org.sonatype.aether.version.VersionRange range : constraint.getRanges() )
468 if ( sb.length() > 0 )
475 return sb.toString();
478 private Artifact getDependencyArtifact( Dependency dep )
480 org.sonatype.aether.artifact.Artifact artifact = dep.getArtifact();
482 return factory.createDependencyArtifact( artifact.getGroupId(), artifact.getArtifactId(),
483 VersionRange.createFromVersion( artifact.getVersion() ),
484 artifact.getExtension(), artifact.getClassifier(), dep.getScope(),
488 private Model buildProject( RepositoryModelResolver modelResolver, String groupId, String artifactId,
490 throws ModelBuildingException, UnresolvableModelException
492 DefaultModelBuildingRequest req = new DefaultModelBuildingRequest();
493 req.setProcessPlugins( false );
494 req.setModelSource( modelResolver.resolveModel( groupId, artifactId, version ) );
495 req.setModelResolver( modelResolver );
496 req.setValidationLevel( ModelBuildingRequest.VALIDATION_LEVEL_MINIMAL );
497 //MRM-1607. olamy this will resolve jdk profiles on the current running archiva jvm
498 req.setSystemProperties( System.getProperties() );
500 return builder.build( req ).getEffectiveModel();
503 private ManagedRepository findArtifactInRepositories( List<String> repositoryIds, Artifact projectArtifact )
504 throws RepositoryAdminException
506 for ( String repoId : repositoryIds )
508 ManagedRepository managedRepository = managedRepositoryAdmin.getManagedRepository( repoId );
510 File repoDir = new File( managedRepository.getLocation() );
511 File file = pathTranslator.toFile( repoDir, projectArtifact.getGroupId(), projectArtifact.getArtifactId(),
512 projectArtifact.getBaseVersion(),
513 projectArtifact.getArtifactId() + "-" + projectArtifact.getVersion()
518 return managedRepository;
524 public static class DefaultDependencyResolutionResult
525 implements DependencyResolutionResult
528 private org.sonatype.aether.graph.DependencyNode root;
530 private List<Dependency> dependencies = new ArrayList<Dependency>();
532 private List<Dependency> resolvedDependencies = new ArrayList<Dependency>();
534 private List<Dependency> unresolvedDependencies = new ArrayList<Dependency>();
536 private List<Exception> collectionErrors = new ArrayList<Exception>();
538 private Map<Dependency, List<Exception>> resolutionErrors = new IdentityHashMap<Dependency, List<Exception>>();
540 public org.sonatype.aether.graph.DependencyNode getDependencyGraph()
545 public void setDependencyGraph( org.sonatype.aether.graph.DependencyNode root )
550 public List<Dependency> getDependencies()
555 public List<Dependency> getResolvedDependencies()
557 return resolvedDependencies;
560 public void addResolvedDependency( Dependency dependency )
562 dependencies.add( dependency );
563 resolvedDependencies.add( dependency );
566 public List<Dependency> getUnresolvedDependencies()
568 return unresolvedDependencies;
571 public List<Exception> getCollectionErrors()
573 return collectionErrors;
576 public void setCollectionErrors( List<Exception> exceptions )
578 if ( exceptions != null )
580 this.collectionErrors = exceptions;
584 this.collectionErrors = new ArrayList<Exception>();
588 public List<Exception> getResolutionErrors( Dependency dependency )
590 List<Exception> errors = resolutionErrors.get( dependency );
591 return ( errors != null ) ? errors : Collections.<Exception>emptyList();
594 public void setResolutionErrors( Dependency dependency, List<Exception> errors )
596 dependencies.add( dependency );
597 unresolvedDependencies.add( dependency );
598 resolutionErrors.put( dependency, errors );