package org.apache.maven.lifecycle.internal; /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import javax.inject.Inject; import javax.inject.Named; import org.apache.maven.RepositoryUtils; import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.ArtifactUtils; import org.apache.maven.eventspy.internal.EventSpyDispatcher; import org.apache.maven.execution.MavenSession; import org.apache.maven.lifecycle.LifecycleExecutionException; import org.apache.maven.project.DefaultDependencyResolutionRequest; import org.apache.maven.project.DependencyResolutionException; import org.apache.maven.project.DependencyResolutionResult; import org.apache.maven.project.MavenProject; import org.apache.maven.project.ProjectDependenciesResolver; import org.apache.maven.project.artifact.InvalidDependencyVersionException; import org.codehaus.plexus.logging.Logger; import org.eclipse.aether.graph.Dependency; import org.eclipse.aether.graph.DependencyFilter; import org.eclipse.aether.graph.DependencyNode; import org.eclipse.aether.util.filter.AndDependencyFilter; import org.eclipse.aether.util.filter.ScopeDependencyFilter; /** * Resolves dependencies for the artifacts in context of the lifecycle build * * @since 3.0 * @author Benjamin Bentmann * @author Jason van Zyl * @author Kristian Rosenvold (extracted class) *

* NOTE: This class is not part of any public api and can be changed or deleted without prior notice. */ @Named public class LifecycleDependencyResolver { @Inject private ProjectDependenciesResolver dependenciesResolver; @Inject private Logger logger; @Inject private ProjectArtifactFactory artifactFactory; @Inject private EventSpyDispatcher eventSpyDispatcher; public LifecycleDependencyResolver() { } public LifecycleDependencyResolver( ProjectDependenciesResolver projectDependenciesResolver, Logger logger ) { this.dependenciesResolver = projectDependenciesResolver; this.logger = logger; } public static List getProjects( MavenProject project, MavenSession session, boolean aggregator ) { if ( aggregator ) { return session.getProjects(); } else { return Collections.singletonList( project ); } } public void resolveProjectDependencies( MavenProject project, Collection scopesToCollect, Collection scopesToResolve, MavenSession session, boolean aggregating, Set projectArtifacts ) throws LifecycleExecutionException { ClassLoader tccl = Thread.currentThread().getContextClassLoader(); try { ClassLoader projectRealm = project.getClassRealm(); if ( projectRealm != null && projectRealm != tccl ) { Thread.currentThread().setContextClassLoader( projectRealm ); } if ( project.getDependencyArtifacts() == null ) { try { project.setDependencyArtifacts( artifactFactory.createArtifacts( project ) ); } catch ( InvalidDependencyVersionException e ) { throw new LifecycleExecutionException( e ); } } Set artifacts = getDependencies( project, scopesToCollect, scopesToResolve, session, aggregating, projectArtifacts ); project.setResolvedArtifacts( artifacts ); Map map = new HashMap(); for ( Artifact artifact : artifacts ) { map.put( artifact.getDependencyConflictId(), artifact ); } for ( Artifact artifact : project.getDependencyArtifacts() ) { if ( artifact.getFile() == null ) { Artifact resolved = map.get( artifact.getDependencyConflictId() ); if ( resolved != null ) { artifact.setFile( resolved.getFile() ); artifact.setDependencyTrail( resolved.getDependencyTrail() ); artifact.setResolvedVersion( resolved.getVersion() ); artifact.setResolved( true ); } } } } finally { Thread.currentThread().setContextClassLoader( tccl ); } } private Set getDependencies( MavenProject project, Collection scopesToCollect, Collection scopesToResolve, MavenSession session, boolean aggregating, Set projectArtifacts ) throws LifecycleExecutionException { if ( scopesToCollect == null ) { scopesToCollect = Collections.emptySet(); } if ( scopesToResolve == null ) { scopesToResolve = Collections.emptySet(); } if ( scopesToCollect.isEmpty() && scopesToResolve.isEmpty() ) { return new LinkedHashSet(); } scopesToCollect = new HashSet( scopesToCollect ); scopesToCollect.addAll( scopesToResolve ); DependencyFilter collectionFilter = new ScopeDependencyFilter( null, negate( scopesToCollect ) ); DependencyFilter resolutionFilter = new ScopeDependencyFilter( null, negate( scopesToResolve ) ); resolutionFilter = AndDependencyFilter.newInstance( collectionFilter, resolutionFilter ); resolutionFilter = AndDependencyFilter.newInstance( resolutionFilter, new ReactorDependencyFilter( projectArtifacts ) ); DependencyResolutionResult result; try { DefaultDependencyResolutionRequest request = new DefaultDependencyResolutionRequest( project, session.getRepositorySession() ); request.setResolutionFilter( resolutionFilter ); eventSpyDispatcher.onEvent( request ); result = dependenciesResolver.resolve( request ); } catch ( DependencyResolutionException e ) { result = e.getResult(); /* * MNG-2277, the check below compensates for our bad plugin support where we ended up with aggregator * plugins that require dependency resolution although they usually run in phases of the build where project * artifacts haven't been assembled yet. The prime example of this is "mvn release:prepare". */ if ( aggregating && areAllDependenciesInReactor( session.getProjects(), result.getUnresolvedDependencies() ) ) { logger.warn( "The following dependencies could not be resolved at this point of the build" + " but seem to be part of the reactor:" ); for ( Dependency dependency : result.getUnresolvedDependencies() ) { logger.warn( "o " + dependency ); } logger.warn( "Try running the build up to the lifecycle phase \"package\"" ); } else { throw new LifecycleExecutionException( null, project, e ); } } eventSpyDispatcher.onEvent( result ); Set artifacts = new LinkedHashSet(); if ( result.getDependencyGraph() != null && !result.getDependencyGraph().getChildren().isEmpty() ) { RepositoryUtils.toArtifacts( artifacts, result.getDependencyGraph().getChildren(), Collections.singletonList( project.getArtifact().getId() ), collectionFilter ); } return artifacts; } private boolean areAllDependenciesInReactor( Collection projects, Collection dependencies ) { Set projectKeys = getReactorProjectKeys( projects ); for ( Dependency dependency : dependencies ) { org.eclipse.aether.artifact.Artifact a = dependency.getArtifact(); String key = ArtifactUtils.key( a.getGroupId(), a.getArtifactId(), a.getVersion() ); if ( !projectKeys.contains( key ) ) { return false; } } return true; } private Set getReactorProjectKeys( Collection projects ) { Set projectKeys = new HashSet( projects.size() * 2 ); for ( MavenProject project : projects ) { String key = ArtifactUtils.key( project.getGroupId(), project.getArtifactId(), project.getVersion() ); projectKeys.add( key ); } return projectKeys; } private Collection negate( Collection scopes ) { Collection result = new HashSet(); Collections.addAll( result, "system", "compile", "provided", "runtime", "test" ); for ( String scope : scopes ) { if ( "compile".equals( scope ) ) { result.remove( "compile" ); result.remove( "system" ); result.remove( "provided" ); } else if ( "runtime".equals( scope ) ) { result.remove( "compile" ); result.remove( "runtime" ); } else if ( "compile+runtime".equals( scope ) ) { result.remove( "compile" ); result.remove( "system" ); result.remove( "provided" ); result.remove( "runtime" ); } else if ( "runtime+system".equals( scope ) ) { result.remove( "compile" ); result.remove( "system" ); result.remove( "runtime" ); } else if ( "test".equals( scope ) ) { result.clear(); } } return result; } private static class ReactorDependencyFilter implements DependencyFilter { private Set keys = new HashSet(); public ReactorDependencyFilter( Collection artifacts ) { for ( Artifact artifact : artifacts ) { String key = ArtifactUtils.key( artifact ); keys.add( key ); } } public boolean accept( DependencyNode node, List parents ) { Dependency dependency = node.getDependency(); if ( dependency != null ) { org.eclipse.aether.artifact.Artifact a = dependency.getArtifact(); String key = ArtifactUtils.key( a.getGroupId(), a.getArtifactId(), a.getVersion() ); return !keys.contains( key ); } return false; } } }