001package 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 022import java.util.Collection; 023import java.util.Collections; 024import java.util.HashMap; 025import java.util.HashSet; 026import java.util.LinkedHashSet; 027import java.util.List; 028import java.util.Map; 029import java.util.Set; 030 031import org.apache.maven.RepositoryUtils; 032import org.apache.maven.artifact.Artifact; 033import org.apache.maven.artifact.ArtifactUtils; 034import org.apache.maven.artifact.factory.ArtifactFactory; 035import org.apache.maven.eventspy.internal.EventSpyDispatcher; 036import org.apache.maven.execution.MavenSession; 037import org.apache.maven.lifecycle.LifecycleExecutionException; 038import org.apache.maven.project.DefaultDependencyResolutionRequest; 039import org.apache.maven.project.DependencyResolutionException; 040import org.apache.maven.project.DependencyResolutionResult; 041import org.apache.maven.project.MavenProject; 042import org.apache.maven.project.ProjectDependenciesResolver; 043import org.apache.maven.project.artifact.InvalidDependencyVersionException; 044import org.codehaus.plexus.component.annotations.Component; 045import org.codehaus.plexus.component.annotations.Requirement; 046import org.codehaus.plexus.logging.Logger; 047import org.eclipse.aether.graph.Dependency; 048import org.eclipse.aether.graph.DependencyFilter; 049import org.eclipse.aether.graph.DependencyNode; 050import org.eclipse.aether.util.filter.AndDependencyFilter; 051import 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 ) 064public 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}