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 javax.inject.Inject; 032import javax.inject.Named; 033 034import org.apache.maven.RepositoryUtils; 035import org.apache.maven.artifact.Artifact; 036import org.apache.maven.artifact.ArtifactUtils; 037import org.apache.maven.eventspy.internal.EventSpyDispatcher; 038import org.apache.maven.execution.MavenSession; 039import org.apache.maven.lifecycle.LifecycleExecutionException; 040import org.apache.maven.project.DefaultDependencyResolutionRequest; 041import org.apache.maven.project.DependencyResolutionException; 042import org.apache.maven.project.DependencyResolutionResult; 043import org.apache.maven.project.MavenProject; 044import org.apache.maven.project.ProjectDependenciesResolver; 045import org.apache.maven.project.artifact.InvalidDependencyVersionException; 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@Named 064public class LifecycleDependencyResolver 065{ 066 067 @Inject 068 private ProjectDependenciesResolver dependenciesResolver; 069 070 @Inject 071 private Logger logger; 072 073 @Inject 074 private ProjectArtifactFactory artifactFactory; 075 076 @Inject 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( artifactFactory.createArtifacts( project ) ); 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<>(); 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<>(); 175 } 176 177 scopesToCollect = new HashSet<>( 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(), 207 result.getUnresolvedDependencies() ) ) 208 { 209 logger.warn( "The following dependencies could not be resolved at this point of the build" 210 + " but seem to be part of the reactor:" ); 211 212 for ( Dependency dependency : result.getUnresolvedDependencies() ) 213 { 214 logger.warn( "o " + dependency ); 215 } 216 217 logger.warn( "Try running the build up to the lifecycle phase \"package\"" ); 218 } 219 else 220 { 221 throw new LifecycleExecutionException( null, project, e ); 222 } 223 } 224 225 eventSpyDispatcher.onEvent( result ); 226 227 Set<Artifact> artifacts = new LinkedHashSet<>(); 228 if ( result.getDependencyGraph() != null && !result.getDependencyGraph().getChildren().isEmpty() ) 229 { 230 RepositoryUtils.toArtifacts( artifacts, result.getDependencyGraph().getChildren(), 231 Collections.singletonList( project.getArtifact().getId() ), collectionFilter ); 232 } 233 return artifacts; 234 } 235 236 private boolean areAllDependenciesInReactor( Collection<MavenProject> projects, 237 Collection<Dependency> dependencies ) 238 { 239 Set<String> projectKeys = getReactorProjectKeys( projects ); 240 241 for ( Dependency dependency : dependencies ) 242 { 243 org.eclipse.aether.artifact.Artifact a = dependency.getArtifact(); 244 String key = ArtifactUtils.key( a.getGroupId(), a.getArtifactId(), a.getVersion() ); 245 if ( !projectKeys.contains( key ) ) 246 { 247 return false; 248 } 249 } 250 251 return true; 252 } 253 254 private Set<String> getReactorProjectKeys( Collection<MavenProject> projects ) 255 { 256 Set<String> projectKeys = new HashSet<>( projects.size() * 2 ); 257 for ( MavenProject project : projects ) 258 { 259 String key = ArtifactUtils.key( project.getGroupId(), project.getArtifactId(), project.getVersion() ); 260 projectKeys.add( key ); 261 } 262 return projectKeys; 263 } 264 265 private Collection<String> negate( Collection<String> scopes ) 266 { 267 Collection<String> result = new HashSet<>(); 268 Collections.addAll( result, "system", "compile", "provided", "runtime", "test" ); 269 270 for ( String scope : scopes ) 271 { 272 if ( "compile".equals( scope ) ) 273 { 274 result.remove( "compile" ); 275 result.remove( "system" ); 276 result.remove( "provided" ); 277 } 278 else if ( "runtime".equals( scope ) ) 279 { 280 result.remove( "compile" ); 281 result.remove( "runtime" ); 282 } 283 else if ( "compile+runtime".equals( scope ) ) 284 { 285 result.remove( "compile" ); 286 result.remove( "system" ); 287 result.remove( "provided" ); 288 result.remove( "runtime" ); 289 } 290 else if ( "runtime+system".equals( scope ) ) 291 { 292 result.remove( "compile" ); 293 result.remove( "system" ); 294 result.remove( "runtime" ); 295 } 296 else if ( "test".equals( scope ) ) 297 { 298 result.clear(); 299 } 300 } 301 302 return result; 303 } 304 305 private static class ReactorDependencyFilter 306 implements DependencyFilter 307 { 308 309 private Set<String> keys = new HashSet<>(); 310 311 public ReactorDependencyFilter( Collection<Artifact> artifacts ) 312 { 313 for ( Artifact artifact : artifacts ) 314 { 315 String key = ArtifactUtils.key( artifact ); 316 keys.add( key ); 317 } 318 } 319 320 public boolean accept( DependencyNode node, List<DependencyNode> parents ) 321 { 322 Dependency dependency = node.getDependency(); 323 if ( dependency != null ) 324 { 325 org.eclipse.aether.artifact.Artifact a = dependency.getArtifact(); 326 String key = ArtifactUtils.key( a.getGroupId(), a.getArtifactId(), a.getVersion() ); 327 return !keys.contains( key ); 328 } 329 return false; 330 } 331 332 } 333 334}