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 org.apache.maven.artifact.Artifact; 023import org.apache.maven.artifact.resolver.filter.ArtifactFilter; 024import org.apache.maven.artifact.resolver.filter.CumulativeScopeArtifactFilter; 025import org.apache.maven.execution.ExecutionEvent; 026import org.apache.maven.execution.MavenSession; 027import org.apache.maven.lifecycle.LifecycleExecutionException; 028import org.apache.maven.lifecycle.MissingProjectException; 029import org.apache.maven.plugin.BuildPluginManager; 030import org.apache.maven.plugin.MavenPluginManager; 031import org.apache.maven.plugin.MojoExecution; 032import org.apache.maven.plugin.MojoExecutionException; 033import org.apache.maven.plugin.MojoFailureException; 034import org.apache.maven.plugin.PluginConfigurationException; 035import org.apache.maven.plugin.PluginIncompatibleException; 036import org.apache.maven.plugin.PluginManagerException; 037import org.apache.maven.plugin.descriptor.MojoDescriptor; 038import org.apache.maven.project.MavenProject; 039import org.codehaus.plexus.component.annotations.Component; 040import org.codehaus.plexus.component.annotations.Requirement; 041import org.codehaus.plexus.util.StringUtils; 042 043import java.util.ArrayList; 044import java.util.Arrays; 045import java.util.Collection; 046import java.util.Collections; 047import java.util.List; 048import java.util.Map; 049import java.util.Set; 050import java.util.TreeSet; 051 052/** 053 * Executes an individual mojo 054 * 055 * @author Jason van Zyl 056 * @author Benjamin Bentmann 057 * @author Kristian Rosenvold 058 * <p/> 059 * NOTE: This class is not part of any public api and can be changed or deleted without prior notice. 060 * @since 3.0 061 */ 062@Component( role = MojoExecutor.class ) 063public class MojoExecutor 064{ 065 066 @Requirement 067 private BuildPluginManager pluginManager; 068 069 @Requirement 070 private MavenPluginManager mavenPluginManager; 071 072 @Requirement 073 private LifecycleDependencyResolver lifeCycleDependencyResolver; 074 075 @Requirement 076 private ExecutionEventCatapult eventCatapult; 077 078 public MojoExecutor() 079 { 080 } 081 082 public DependencyContext newDependencyContext( MavenSession session, List<MojoExecution> mojoExecutions ) 083 { 084 Set<String> scopesToCollect = new TreeSet<>(); 085 Set<String> scopesToResolve = new TreeSet<>(); 086 087 collectDependencyRequirements( scopesToResolve, scopesToCollect, mojoExecutions ); 088 089 return new DependencyContext( session.getCurrentProject(), scopesToCollect, scopesToResolve ); 090 } 091 092 private void collectDependencyRequirements( Set<String> scopesToResolve, Set<String> scopesToCollect, 093 Collection<MojoExecution> mojoExecutions ) 094 { 095 for ( MojoExecution mojoExecution : mojoExecutions ) 096 { 097 MojoDescriptor mojoDescriptor = mojoExecution.getMojoDescriptor(); 098 099 scopesToResolve.addAll( toScopes( mojoDescriptor.getDependencyResolutionRequired() ) ); 100 101 scopesToCollect.addAll( toScopes( mojoDescriptor.getDependencyCollectionRequired() ) ); 102 } 103 } 104 105 private Collection<String> toScopes( String classpath ) 106 { 107 if ( StringUtils.isNotEmpty( classpath ) ) 108 { 109 if ( Artifact.SCOPE_COMPILE.equals( classpath ) ) 110 { 111 return Arrays.asList( Artifact.SCOPE_COMPILE, Artifact.SCOPE_SYSTEM, Artifact.SCOPE_PROVIDED ); 112 } 113 else if ( Artifact.SCOPE_RUNTIME.equals( classpath ) ) 114 { 115 return Arrays.asList( Artifact.SCOPE_COMPILE, Artifact.SCOPE_RUNTIME ); 116 } 117 else if ( Artifact.SCOPE_COMPILE_PLUS_RUNTIME.equals( classpath ) ) 118 { 119 return Arrays.asList( Artifact.SCOPE_COMPILE, Artifact.SCOPE_SYSTEM, Artifact.SCOPE_PROVIDED, 120 Artifact.SCOPE_RUNTIME ); 121 } 122 else if ( Artifact.SCOPE_RUNTIME_PLUS_SYSTEM.equals( classpath ) ) 123 { 124 return Arrays.asList( Artifact.SCOPE_COMPILE, Artifact.SCOPE_SYSTEM, Artifact.SCOPE_RUNTIME ); 125 } 126 else if ( Artifact.SCOPE_TEST.equals( classpath ) ) 127 { 128 return Arrays.asList( Artifact.SCOPE_COMPILE, Artifact.SCOPE_SYSTEM, Artifact.SCOPE_PROVIDED, 129 Artifact.SCOPE_RUNTIME, Artifact.SCOPE_TEST ); 130 } 131 } 132 return Collections.emptyList(); 133 } 134 135 public void execute( MavenSession session, List<MojoExecution> mojoExecutions, ProjectIndex projectIndex ) 136 throws LifecycleExecutionException 137 138 { 139 DependencyContext dependencyContext = newDependencyContext( session, mojoExecutions ); 140 141 PhaseRecorder phaseRecorder = new PhaseRecorder( session.getCurrentProject() ); 142 143 for ( MojoExecution mojoExecution : mojoExecutions ) 144 { 145 execute( session, mojoExecution, projectIndex, dependencyContext, phaseRecorder ); 146 } 147 } 148 149 public void execute( MavenSession session, MojoExecution mojoExecution, ProjectIndex projectIndex, 150 DependencyContext dependencyContext, PhaseRecorder phaseRecorder ) 151 throws LifecycleExecutionException 152 { 153 execute( session, mojoExecution, projectIndex, dependencyContext ); 154 phaseRecorder.observeExecution( mojoExecution ); 155 } 156 157 private void execute( MavenSession session, MojoExecution mojoExecution, ProjectIndex projectIndex, 158 DependencyContext dependencyContext ) 159 throws LifecycleExecutionException 160 { 161 MojoDescriptor mojoDescriptor = mojoExecution.getMojoDescriptor(); 162 163 try 164 { 165 mavenPluginManager.checkRequiredMavenVersion( mojoDescriptor.getPluginDescriptor() ); 166 } 167 catch ( PluginIncompatibleException e ) 168 { 169 throw new LifecycleExecutionException( mojoExecution, session.getCurrentProject(), e ); 170 } 171 172 if ( mojoDescriptor.isProjectRequired() && !session.getRequest().isProjectPresent() ) 173 { 174 Throwable cause = new MissingProjectException( 175 "Goal requires a project to execute" + " but there is no POM in this directory (" 176 + session.getExecutionRootDirectory() + ")." 177 + " Please verify you invoked Maven from the correct directory." ); 178 throw new LifecycleExecutionException( mojoExecution, null, cause ); 179 } 180 181 if ( mojoDescriptor.isOnlineRequired() && session.isOffline() ) 182 { 183 if ( MojoExecution.Source.CLI.equals( mojoExecution.getSource() ) ) 184 { 185 Throwable cause = new IllegalStateException( 186 "Goal requires online mode for execution" + " but Maven is currently offline." ); 187 throw new LifecycleExecutionException( mojoExecution, session.getCurrentProject(), cause ); 188 } 189 else 190 { 191 eventCatapult.fire( ExecutionEvent.Type.MojoSkipped, session, mojoExecution ); 192 193 return; 194 } 195 } 196 197 List<MavenProject> forkedProjects = executeForkedExecutions( mojoExecution, session, projectIndex ); 198 199 ensureDependenciesAreResolved( mojoDescriptor, session, dependencyContext ); 200 201 eventCatapult.fire( ExecutionEvent.Type.MojoStarted, session, mojoExecution ); 202 203 try 204 { 205 try 206 { 207 pluginManager.executeMojo( session, mojoExecution ); 208 } 209 catch ( MojoFailureException | PluginManagerException | PluginConfigurationException 210 | MojoExecutionException e ) 211 { 212 throw new LifecycleExecutionException( mojoExecution, session.getCurrentProject(), e ); 213 } 214 215 eventCatapult.fire( ExecutionEvent.Type.MojoSucceeded, session, mojoExecution ); 216 } 217 catch ( LifecycleExecutionException e ) 218 { 219 eventCatapult.fire( ExecutionEvent.Type.MojoFailed, session, mojoExecution, e ); 220 221 throw e; 222 } 223 finally 224 { 225 for ( MavenProject forkedProject : forkedProjects ) 226 { 227 forkedProject.setExecutionProject( null ); 228 } 229 } 230 } 231 232 public void ensureDependenciesAreResolved( MojoDescriptor mojoDescriptor, MavenSession session, 233 DependencyContext dependencyContext ) 234 throws LifecycleExecutionException 235 236 { 237 MavenProject project = dependencyContext.getProject(); 238 boolean aggregating = mojoDescriptor.isAggregator(); 239 240 if ( dependencyContext.isResolutionRequiredForCurrentProject() ) 241 { 242 Collection<String> scopesToCollect = dependencyContext.getScopesToCollectForCurrentProject(); 243 Collection<String> scopesToResolve = dependencyContext.getScopesToResolveForCurrentProject(); 244 245 lifeCycleDependencyResolver.resolveProjectDependencies( project, scopesToCollect, scopesToResolve, session, 246 aggregating, Collections.<Artifact>emptySet() ); 247 248 dependencyContext.synchronizeWithProjectState(); 249 } 250 251 if ( aggregating ) 252 { 253 Collection<String> scopesToCollect = toScopes( mojoDescriptor.getDependencyCollectionRequired() ); 254 Collection<String> scopesToResolve = toScopes( mojoDescriptor.getDependencyResolutionRequired() ); 255 256 if ( dependencyContext.isResolutionRequiredForAggregatedProjects( scopesToCollect, scopesToResolve ) ) 257 { 258 for ( MavenProject aggregatedProject : session.getProjects() ) 259 { 260 if ( aggregatedProject != project ) 261 { 262 lifeCycleDependencyResolver.resolveProjectDependencies( aggregatedProject, scopesToCollect, 263 scopesToResolve, session, aggregating, 264 Collections.<Artifact>emptySet() ); 265 } 266 } 267 } 268 } 269 270 ArtifactFilter artifactFilter = getArtifactFilter( mojoDescriptor ); 271 List<MavenProject> projectsToResolve = 272 LifecycleDependencyResolver.getProjects( session.getCurrentProject(), session, 273 mojoDescriptor.isAggregator() ); 274 for ( MavenProject projectToResolve : projectsToResolve ) 275 { 276 projectToResolve.setArtifactFilter( artifactFilter ); 277 } 278 } 279 280 private ArtifactFilter getArtifactFilter( MojoDescriptor mojoDescriptor ) 281 { 282 String scopeToResolve = mojoDescriptor.getDependencyResolutionRequired(); 283 String scopeToCollect = mojoDescriptor.getDependencyCollectionRequired(); 284 285 List<String> scopes = new ArrayList<>( 2 ); 286 if ( StringUtils.isNotEmpty( scopeToCollect ) ) 287 { 288 scopes.add( scopeToCollect ); 289 } 290 if ( StringUtils.isNotEmpty( scopeToResolve ) ) 291 { 292 scopes.add( scopeToResolve ); 293 } 294 295 if ( scopes.isEmpty() ) 296 { 297 return null; 298 } 299 else 300 { 301 return new CumulativeScopeArtifactFilter( scopes ); 302 } 303 } 304 305 public List<MavenProject> executeForkedExecutions( MojoExecution mojoExecution, MavenSession session, 306 ProjectIndex projectIndex ) 307 throws LifecycleExecutionException 308 { 309 List<MavenProject> forkedProjects = Collections.emptyList(); 310 311 Map<String, List<MojoExecution>> forkedExecutions = mojoExecution.getForkedExecutions(); 312 313 if ( !forkedExecutions.isEmpty() ) 314 { 315 eventCatapult.fire( ExecutionEvent.Type.ForkStarted, session, mojoExecution ); 316 317 MavenProject project = session.getCurrentProject(); 318 319 forkedProjects = new ArrayList<>( forkedExecutions.size() ); 320 321 try 322 { 323 for ( Map.Entry<String, List<MojoExecution>> fork : forkedExecutions.entrySet() ) 324 { 325 String projectId = fork.getKey(); 326 327 int index = projectIndex.getIndices().get( projectId ); 328 329 MavenProject forkedProject = projectIndex.getProjects().get( projectId ); 330 331 forkedProjects.add( forkedProject ); 332 333 MavenProject executedProject = forkedProject.clone(); 334 335 forkedProject.setExecutionProject( executedProject ); 336 337 List<MojoExecution> mojoExecutions = fork.getValue(); 338 339 if ( mojoExecutions.isEmpty() ) 340 { 341 continue; 342 } 343 344 try 345 { 346 session.setCurrentProject( executedProject ); 347 session.getProjects().set( index, executedProject ); 348 projectIndex.getProjects().put( projectId, executedProject ); 349 350 eventCatapult.fire( ExecutionEvent.Type.ForkedProjectStarted, session, mojoExecution ); 351 352 execute( session, mojoExecutions, projectIndex ); 353 354 eventCatapult.fire( ExecutionEvent.Type.ForkedProjectSucceeded, session, mojoExecution ); 355 } 356 catch ( LifecycleExecutionException e ) 357 { 358 eventCatapult.fire( ExecutionEvent.Type.ForkedProjectFailed, session, mojoExecution, e ); 359 360 throw e; 361 } 362 finally 363 { 364 projectIndex.getProjects().put( projectId, forkedProject ); 365 session.getProjects().set( index, forkedProject ); 366 session.setCurrentProject( project ); 367 } 368 } 369 370 eventCatapult.fire( ExecutionEvent.Type.ForkSucceeded, session, mojoExecution ); 371 } 372 catch ( LifecycleExecutionException e ) 373 { 374 eventCatapult.fire( ExecutionEvent.Type.ForkFailed, session, mojoExecution, e ); 375 376 throw e; 377 } 378 } 379 380 return forkedProjects; 381 } 382}