001 package org.apache.maven.lifecycle.internal;
002
003 /*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements. See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership. The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License. You may obtain a copy of the License at
011 *
012 * http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied. See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022 import java.util.Collection;
023 import java.util.Collections;
024 import java.util.HashMap;
025 import java.util.HashSet;
026 import java.util.LinkedHashSet;
027 import java.util.List;
028 import java.util.Map;
029 import java.util.Set;
030
031 import org.apache.maven.RepositoryUtils;
032 import org.apache.maven.artifact.Artifact;
033 import org.apache.maven.artifact.ArtifactUtils;
034 import org.apache.maven.artifact.factory.ArtifactFactory;
035 import org.apache.maven.eventspy.internal.EventSpyDispatcher;
036 import org.apache.maven.execution.MavenSession;
037 import org.apache.maven.lifecycle.LifecycleExecutionException;
038 import org.apache.maven.project.DefaultDependencyResolutionRequest;
039 import org.apache.maven.project.DependencyResolutionException;
040 import org.apache.maven.project.DependencyResolutionResult;
041 import org.apache.maven.project.MavenProject;
042 import org.apache.maven.project.ProjectDependenciesResolver;
043 import org.apache.maven.project.artifact.InvalidDependencyVersionException;
044 import org.codehaus.plexus.component.annotations.Component;
045 import org.codehaus.plexus.component.annotations.Requirement;
046 import org.codehaus.plexus.logging.Logger;
047 import org.eclipse.aether.graph.Dependency;
048 import org.eclipse.aether.graph.DependencyFilter;
049 import org.eclipse.aether.graph.DependencyNode;
050 import org.eclipse.aether.util.filter.AndDependencyFilter;
051 import org.eclipse.aether.util.filter.ScopeDependencyFilter;
052
053 /**
054 * Resolves dependencies for the artifacts in context of the lifecycle build
055 *
056 * @since 3.0
057 * @author Benjamin Bentmann
058 * @author Jason van Zyl
059 * @author Kristian Rosenvold (extracted class)
060 * <p/>
061 * NOTE: This class is not part of any public api and can be changed or deleted without prior notice.
062 */
063 @Component( role = LifecycleDependencyResolver.class )
064 public class LifecycleDependencyResolver
065 {
066
067 @Requirement
068 private ProjectDependenciesResolver dependenciesResolver;
069
070 @Requirement
071 private Logger logger;
072
073 @Requirement
074 private ArtifactFactory artifactFactory;
075
076 @Requirement
077 private EventSpyDispatcher eventSpyDispatcher;
078
079 public LifecycleDependencyResolver()
080 {
081 }
082
083 public LifecycleDependencyResolver( ProjectDependenciesResolver projectDependenciesResolver, Logger logger )
084 {
085 this.dependenciesResolver = projectDependenciesResolver;
086 this.logger = logger;
087 }
088
089 public static List<MavenProject> getProjects( MavenProject project, MavenSession session, boolean aggregator )
090 {
091 if ( aggregator )
092 {
093 return session.getProjects();
094 }
095 else
096 {
097 return Collections.singletonList( project );
098 }
099 }
100
101 public void resolveProjectDependencies( MavenProject project, Collection<String> scopesToCollect,
102 Collection<String> scopesToResolve, MavenSession session,
103 boolean aggregating, Set<Artifact> projectArtifacts )
104 throws LifecycleExecutionException
105 {
106 ClassLoader tccl = Thread.currentThread().getContextClassLoader();
107 try
108 {
109 ClassLoader projectRealm = project.getClassRealm();
110 if ( projectRealm != null && projectRealm != tccl )
111 {
112 Thread.currentThread().setContextClassLoader( projectRealm );
113 }
114
115 if ( project.getDependencyArtifacts() == null )
116 {
117 try
118 {
119 project.setDependencyArtifacts( project.createArtifacts( artifactFactory, null, null ) );
120 }
121 catch ( InvalidDependencyVersionException e )
122 {
123 throw new LifecycleExecutionException( e );
124 }
125 }
126
127 Set<Artifact> artifacts =
128 getDependencies( project, scopesToCollect, scopesToResolve, session, aggregating, projectArtifacts );
129
130 project.setResolvedArtifacts( artifacts );
131
132 Map<String, Artifact> map = new HashMap<String, Artifact>();
133 for ( Artifact artifact : artifacts )
134 {
135 map.put( artifact.getDependencyConflictId(), artifact );
136 }
137 for ( Artifact artifact : project.getDependencyArtifacts() )
138 {
139 if ( artifact.getFile() == null )
140 {
141 Artifact resolved = map.get( artifact.getDependencyConflictId() );
142 if ( resolved != null )
143 {
144 artifact.setFile( resolved.getFile() );
145 artifact.setDependencyTrail( resolved.getDependencyTrail() );
146 artifact.setResolvedVersion( resolved.getVersion() );
147 artifact.setResolved( true );
148 }
149 }
150 }
151 }
152 finally
153 {
154 Thread.currentThread().setContextClassLoader( tccl );
155 }
156 }
157
158 private Set<Artifact> getDependencies( MavenProject project, Collection<String> scopesToCollect,
159 Collection<String> scopesToResolve, MavenSession session,
160 boolean aggregating, Set<Artifact> projectArtifacts )
161 throws LifecycleExecutionException
162 {
163 if ( scopesToCollect == null )
164 {
165 scopesToCollect = Collections.emptySet();
166 }
167 if ( scopesToResolve == null )
168 {
169 scopesToResolve = Collections.emptySet();
170 }
171
172 if ( scopesToCollect.isEmpty() && scopesToResolve.isEmpty() )
173 {
174 return new LinkedHashSet<Artifact>();
175 }
176
177 scopesToCollect = new HashSet<String>( scopesToCollect );
178 scopesToCollect.addAll( scopesToResolve );
179
180 DependencyFilter collectionFilter = new ScopeDependencyFilter( null, negate( scopesToCollect ) );
181 DependencyFilter resolutionFilter = new ScopeDependencyFilter( null, negate( scopesToResolve ) );
182 resolutionFilter = AndDependencyFilter.newInstance( collectionFilter, resolutionFilter );
183 resolutionFilter =
184 AndDependencyFilter.newInstance( resolutionFilter, new ReactorDependencyFilter( projectArtifacts ) );
185
186 DependencyResolutionResult result;
187 try
188 {
189 DefaultDependencyResolutionRequest request =
190 new DefaultDependencyResolutionRequest( project, session.getRepositorySession() );
191 request.setResolutionFilter( resolutionFilter );
192
193 eventSpyDispatcher.onEvent( request );
194
195 result = dependenciesResolver.resolve( request );
196 }
197 catch ( DependencyResolutionException e )
198 {
199 result = e.getResult();
200
201 /*
202 * MNG-2277, the check below compensates for our bad plugin support where we ended up with aggregator
203 * plugins that require dependency resolution although they usually run in phases of the build where project
204 * artifacts haven't been assembled yet. The prime example of this is "mvn release:prepare".
205 */
206 if ( aggregating && areAllDependenciesInReactor( session.getProjects(), result.getUnresolvedDependencies() ) )
207 {
208 logger.warn( "The following dependencies could not be resolved at this point of the build"
209 + " but seem to be part of the reactor:" );
210
211 for ( Dependency dependency : result.getUnresolvedDependencies() )
212 {
213 logger.warn( "o " + dependency );
214 }
215
216 logger.warn( "Try running the build up to the lifecycle phase \"package\"" );
217 }
218 else
219 {
220 throw new LifecycleExecutionException( null, project, e );
221 }
222 }
223
224 eventSpyDispatcher.onEvent( result );
225
226 Set<Artifact> artifacts = new LinkedHashSet<Artifact>();
227 if ( result.getDependencyGraph() != null && !result.getDependencyGraph().getChildren().isEmpty() )
228 {
229 RepositoryUtils.toArtifacts( artifacts, result.getDependencyGraph().getChildren(),
230 Collections.singletonList( project.getArtifact().getId() ), collectionFilter );
231 }
232 return artifacts;
233 }
234
235 private boolean areAllDependenciesInReactor( Collection<MavenProject> projects, Collection<Dependency> dependencies )
236 {
237 Set<String> projectKeys = getReactorProjectKeys( projects );
238
239 for ( Dependency dependency : dependencies )
240 {
241 org.eclipse.aether.artifact.Artifact a = dependency.getArtifact();
242 String key = ArtifactUtils.key( a.getGroupId(), a.getArtifactId(), a.getVersion() );
243 if ( !projectKeys.contains( key ) )
244 {
245 return false;
246 }
247 }
248
249 return true;
250 }
251
252 private Set<String> getReactorProjectKeys( Collection<MavenProject> projects )
253 {
254 Set<String> projectKeys = new HashSet<String>( projects.size() * 2 );
255 for ( MavenProject project : projects )
256 {
257 String key = ArtifactUtils.key( project.getGroupId(), project.getArtifactId(), project.getVersion() );
258 projectKeys.add( key );
259 }
260 return projectKeys;
261 }
262
263 private Collection<String> negate( Collection<String> scopes )
264 {
265 Collection<String> result = new HashSet<String>();
266 Collections.addAll( result, "system", "compile", "provided", "runtime", "test" );
267
268 for ( String scope : scopes )
269 {
270 if ( "compile".equals( scope ) )
271 {
272 result.remove( "compile" );
273 result.remove( "system" );
274 result.remove( "provided" );
275 }
276 else if ( "runtime".equals( scope ) )
277 {
278 result.remove( "compile" );
279 result.remove( "runtime" );
280 }
281 else if ( "compile+runtime".equals( scope ) )
282 {
283 result.remove( "compile" );
284 result.remove( "system" );
285 result.remove( "provided" );
286 result.remove( "runtime" );
287 }
288 else if ( "runtime+system".equals( scope ) )
289 {
290 result.remove( "compile" );
291 result.remove( "system" );
292 result.remove( "runtime" );
293 }
294 else if ( "test".equals( scope ) )
295 {
296 result.clear();
297 }
298 }
299
300 return result;
301 }
302
303 private static class ReactorDependencyFilter
304 implements DependencyFilter
305 {
306
307 private Set<String> keys = new HashSet<String>();
308
309 public ReactorDependencyFilter( Collection<Artifact> artifacts )
310 {
311 for ( Artifact artifact : artifacts )
312 {
313 String key = ArtifactUtils.key( artifact );
314 keys.add( key );
315 }
316 }
317
318 public boolean accept( DependencyNode node, List<DependencyNode> parents )
319 {
320 Dependency dependency = node.getDependency();
321 if ( dependency != null )
322 {
323 org.eclipse.aether.artifact.Artifact a = dependency.getArtifact();
324 String key = ArtifactUtils.key( a.getGroupId(), a.getArtifactId(), a.getVersion() );
325 return !keys.contains( key );
326 }
327 return false;
328 }
329
330 }
331
332 }