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 }