View Javadoc
1   package org.apache.maven.shared.dependency.graph.internal;
2   
3   /*
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
11   *
12   *  http://www.apache.org/licenses/LICENSE-2.0
13   *
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
19   * under the License.
20   */
21  
22  import org.apache.maven.artifact.Artifact;
23  import org.apache.maven.artifact.factory.ArtifactFactory;
24  import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
25  import org.apache.maven.artifact.versioning.VersionRange;
26  import org.apache.maven.project.DefaultDependencyResolutionRequest;
27  import org.apache.maven.project.DependencyResolutionException;
28  import org.apache.maven.project.DependencyResolutionRequest;
29  import org.apache.maven.project.DependencyResolutionResult;
30  import org.apache.maven.project.MavenProject;
31  import org.apache.maven.project.ProjectBuildingRequest;
32  import org.apache.maven.project.ProjectDependenciesResolver;
33  import org.apache.maven.shared.dependency.graph.DependencyGraphBuilder;
34  import org.apache.maven.shared.dependency.graph.DependencyGraphBuilderException;
35  import org.apache.maven.shared.dependency.graph.DependencyNode;
36  import org.codehaus.plexus.component.annotations.Component;
37  import org.codehaus.plexus.component.annotations.Requirement;
38  import org.codehaus.plexus.logging.AbstractLogEnabled;
39  import org.sonatype.aether.graph.Dependency;
40  import org.sonatype.aether.version.VersionConstraint;
41  
42  import java.util.ArrayList;
43  import java.util.Collection;
44  import java.util.Collections;
45  import java.util.HashSet;
46  import java.util.List;
47  import java.util.Set;
48  
49  /**
50   * Wrapper around Maven 3 dependency resolver.
51   *
52   * @see ProjectDependenciesResolver
53   * @author Hervé Boutemy
54   * @since 2.0
55   */
56  @Component( role = DependencyGraphBuilder.class, hint = "maven3" )
57  public class Maven3DependencyGraphBuilder
58      extends AbstractLogEnabled
59      implements DependencyGraphBuilder
60  {
61      @Requirement
62      private ProjectDependenciesResolver resolver;
63  
64      @Requirement
65      private ArtifactFactory factory;
66  
67      /**
68       * Builds the dependency graph for Maven 3.
69       *
70       * @param project the project
71       * @param filter artifact filter (can be <code>null</code>)
72       * @return DependencyNode containing the dependency graph.
73       * @throws DependencyGraphBuilderException if some of the dependencies could not be resolved.
74       */
75      public DependencyNode buildDependencyGraph( MavenProject project, ArtifactFilter filter )
76          throws DependencyGraphBuilderException
77      {
78          return buildDependencyGraph( project, filter, null );
79      }
80  
81      /**
82       * Builds the dependency graph for Maven 3, eventually hacking for collecting projects from
83       * reactor not yet built.
84       *
85       * @param project the project
86       * @param filter artifact filter (can be <code>null</code>)
87       * @param reactorProjects Collection of those projects contained in the reactor (can be <code>null</code>).
88       * @return DependencyNode containing the dependency graph.
89       * @throws DependencyGraphBuilderException if some of the dependencies could not be resolved.
90       */
91      public DependencyNode buildDependencyGraph( MavenProject project, ArtifactFilter filter,
92                                                  Collection<MavenProject> reactorProjects )
93          throws DependencyGraphBuilderException
94      {
95          ProjectBuildingRequest projectBuildingRequest =
96              (ProjectBuildingRequest) Invoker.invoke( project, "getProjectBuildingRequest" );
97  
98          DependencyResolutionRequest request =
99              new DefaultDependencyResolutionRequest( project, projectBuildingRequest.getRepositorySession() );
100 
101         DependencyResolutionResult result = resolveDependencies( request, reactorProjects );
102 
103         return buildDependencyNode( null, result.getDependencyGraph(), project.getArtifact(), filter );
104     }
105 
106     private DependencyResolutionResult resolveDependencies( DependencyResolutionRequest request,
107                                                             Collection<MavenProject> reactorProjects )
108         throws DependencyGraphBuilderException
109     {
110         try
111         {
112             return resolver.resolve( request );
113         }
114         catch ( DependencyResolutionException e )
115         {
116             if ( reactorProjects == null )
117             {
118                 throw new DependencyGraphBuilderException( "Could not resolve following dependencies: "
119                     + e.getResult().getUnresolvedDependencies(), e );
120             }
121 
122             // try collecting from reactor
123             return collectDependenciesFromReactor( e, reactorProjects );
124         }
125     }
126 
127     private DependencyResolutionResult collectDependenciesFromReactor( DependencyResolutionException e,
128                                                                        Collection<MavenProject> reactorProjects )
129         throws DependencyGraphBuilderException
130     {
131         DependencyResolutionResult result = e.getResult();
132 
133         List<Dependency> reactorDeps = getReactorDependencies( reactorProjects, result.getUnresolvedDependencies() );
134 
135         result.getUnresolvedDependencies().removeAll( reactorDeps );
136         Invoker.invoke( result.getResolvedDependencies(), "addAll", Collection.class, reactorDeps );
137 
138         if ( !result.getUnresolvedDependencies().isEmpty() )
139         {
140             throw new DependencyGraphBuilderException( "Could not resolve nor collect following dependencies: "
141                 + result.getUnresolvedDependencies(), e );
142         }
143 
144         return result;
145     }
146 
147     private List<org.sonatype.aether.graph.Dependency> getReactorDependencies( Collection<MavenProject> reactorProjects,
148                                                                                List<?> dependencies )
149     {
150         Set<ArtifactKey> reactorProjectsIds = new HashSet<ArtifactKey>();
151         for ( MavenProject project : reactorProjects )
152         {
153             reactorProjectsIds.add( new ArtifactKey( project ) );
154         }
155 
156         List<Dependency> reactorDeps = new ArrayList<Dependency>();
157         for ( Object untypedDependency : dependencies )
158         {
159             Dependency dependency = (Dependency) untypedDependency;
160             org.sonatype.aether.artifact.Artifact depArtifact = dependency.getArtifact();
161 
162             ArtifactKey key =
163                 new ArtifactKey( depArtifact.getGroupId(), depArtifact.getArtifactId(), depArtifact.getVersion() );
164 
165             if ( reactorProjectsIds.contains( key ) )
166             {
167                 reactorDeps.add( dependency );
168             }
169         }
170 
171         return reactorDeps;
172     }
173 
174     private Artifact getDependencyArtifact( Dependency dep )
175     {
176         org.sonatype.aether.artifact.Artifact artifact = dep.getArtifact();
177 
178         return factory.createDependencyArtifact( artifact.getGroupId(), artifact.getArtifactId(),
179                                                  VersionRange.createFromVersion( artifact.getVersion() ),
180                                                  artifact.getProperty( "type", artifact.getExtension() ),
181                                                  artifact.getClassifier(), dep.getScope(), dep.isOptional() );
182     }
183 
184     private DependencyNode buildDependencyNode( DependencyNode parent, org.sonatype.aether.graph.DependencyNode node,
185                                                 Artifact artifact, ArtifactFilter filter )
186     {
187         DefaultDependencyNode current =
188             new DefaultDependencyNode( parent, artifact,
189                                        null /* node.getPremanagedVersion() */,
190                                        null /* node.getPremanagedScope() */,
191                                        getVersionSelectedFromRange( node.getVersionConstraint() ) );
192 
193         List<DependencyNode> nodes = new ArrayList<DependencyNode>( node.getChildren().size() );
194         for ( org.sonatype.aether.graph.DependencyNode child : node.getChildren() )
195         {
196             Artifact childArtifact = getDependencyArtifact( child.getDependency() );
197 
198             if ( ( filter == null ) || filter.include( childArtifact ) )
199             {
200                 nodes.add( buildDependencyNode( current, child, childArtifact, filter ) );
201             }
202         }
203 
204         current.setChildren( Collections.unmodifiableList( nodes ) );
205 
206         return current;
207     }
208 
209     private String getVersionSelectedFromRange( VersionConstraint constraint )
210     {
211         if ( ( constraint == null ) || ( constraint.getVersion() != null ) )
212         {
213             return null;
214         }
215 
216         StringBuilder sb = new StringBuilder();
217         for ( org.sonatype.aether.version.VersionRange range : constraint.getRanges() )
218         {
219             if ( sb.length() > 0 )
220             {
221                 sb.append( ',' );
222             }
223             sb.append( range );
224         }
225 
226         return sb.toString();
227     }
228 }