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