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.ArrayList; 023import java.util.Arrays; 024import java.util.Collection; 025import java.util.Collections; 026import java.util.List; 027import java.util.Map; 028import java.util.Set; 029import java.util.TreeSet; 030 031import org.apache.maven.artifact.Artifact; 032import org.apache.maven.artifact.resolver.filter.ArtifactFilter; 033import org.apache.maven.artifact.resolver.filter.CumulativeScopeArtifactFilter; 034import org.apache.maven.execution.ExecutionEvent; 035import org.apache.maven.execution.MavenSession; 036import org.apache.maven.lifecycle.LifecycleExecutionException; 037import org.apache.maven.lifecycle.MissingProjectException; 038import org.apache.maven.plugin.BuildPluginManager; 039import org.apache.maven.plugin.MavenPluginManager; 040import org.apache.maven.plugin.MojoExecution; 041import org.apache.maven.plugin.MojoExecutionException; 042import org.apache.maven.plugin.MojoFailureException; 043import org.apache.maven.plugin.PluginConfigurationException; 044import org.apache.maven.plugin.PluginIncompatibleException; 045import org.apache.maven.plugin.PluginManagerException; 046import org.apache.maven.plugin.descriptor.MojoDescriptor; 047import org.apache.maven.project.MavenProject; 048import org.codehaus.plexus.component.annotations.Component; 049import org.codehaus.plexus.component.annotations.Requirement; 050import org.codehaus.plexus.util.StringUtils; 051 052/** 053 * Executes an individual mojo 054 * 055 * @since 3.0 056 * @author Jason van Zyl 057 * @author Benjamin Bentmann 058 * @author Kristian Rosenvold 059 * <p/> 060 * NOTE: This class is not part of any public api and can be changed or deleted without prior notice. 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<String>(); 085 Set<String> scopesToResolve = new TreeSet<String>(); 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 = 175 new MissingProjectException( "Goal requires a project to execute" 176 + " but there is no POM in this directory (" + 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 = 186 new IllegalStateException( "Goal requires online mode for execution" 187 + " but Maven is currently offline." ); 188 throw new LifecycleExecutionException( mojoExecution, session.getCurrentProject(), cause ); 189 } 190 else 191 { 192 eventCatapult.fire( ExecutionEvent.Type.MojoSkipped, session, mojoExecution ); 193 194 return; 195 } 196 } 197 198 List<MavenProject> forkedProjects = executeForkedExecutions( mojoExecution, session, projectIndex ); 199 200 ensureDependenciesAreResolved( mojoDescriptor, session, dependencyContext ); 201 202 eventCatapult.fire( ExecutionEvent.Type.MojoStarted, session, mojoExecution ); 203 204 try 205 { 206 try 207 { 208 pluginManager.executeMojo( session, mojoExecution ); 209 } 210 catch ( MojoFailureException e ) 211 { 212 throw new LifecycleExecutionException( mojoExecution, session.getCurrentProject(), e ); 213 } 214 catch ( MojoExecutionException e ) 215 { 216 throw new LifecycleExecutionException( mojoExecution, session.getCurrentProject(), e ); 217 } 218 catch ( PluginConfigurationException e ) 219 { 220 throw new LifecycleExecutionException( mojoExecution, session.getCurrentProject(), e ); 221 } 222 catch ( PluginManagerException e ) 223 { 224 throw new LifecycleExecutionException( mojoExecution, session.getCurrentProject(), e ); 225 } 226 227 eventCatapult.fire( ExecutionEvent.Type.MojoSucceeded, session, mojoExecution ); 228 } 229 catch ( LifecycleExecutionException e ) 230 { 231 eventCatapult.fire( ExecutionEvent.Type.MojoFailed, session, mojoExecution, e ); 232 233 throw e; 234 } 235 finally 236 { 237 for ( MavenProject forkedProject : forkedProjects ) 238 { 239 forkedProject.setExecutionProject( null ); 240 } 241 } 242 } 243 244 public void ensureDependenciesAreResolved( MojoDescriptor mojoDescriptor, MavenSession session, 245 DependencyContext dependencyContext ) 246 throws LifecycleExecutionException 247 248 { 249 MavenProject project = dependencyContext.getProject(); 250 boolean aggregating = mojoDescriptor.isAggregator(); 251 252 if ( dependencyContext.isResolutionRequiredForCurrentProject() ) 253 { 254 Collection<String> scopesToCollect = dependencyContext.getScopesToCollectForCurrentProject(); 255 Collection<String> scopesToResolve = dependencyContext.getScopesToResolveForCurrentProject(); 256 257 lifeCycleDependencyResolver.resolveProjectDependencies( project, scopesToCollect, scopesToResolve, session, 258 aggregating, Collections.<Artifact>emptySet() ); 259 260 dependencyContext.synchronizeWithProjectState(); 261 } 262 263 if ( aggregating ) 264 { 265 Collection<String> scopesToCollect = toScopes( mojoDescriptor.getDependencyCollectionRequired() ); 266 Collection<String> scopesToResolve = toScopes( mojoDescriptor.getDependencyResolutionRequired() ); 267 268 if ( dependencyContext.isResolutionRequiredForAggregatedProjects( scopesToCollect, scopesToResolve ) ) 269 { 270 for ( MavenProject aggregatedProject : session.getProjects() ) 271 { 272 if ( aggregatedProject != project ) 273 { 274 lifeCycleDependencyResolver.resolveProjectDependencies( aggregatedProject, scopesToCollect, 275 scopesToResolve, session, aggregating, 276 Collections.<Artifact>emptySet() ); 277 } 278 } 279 } 280 } 281 282 ArtifactFilter artifactFilter = getArtifactFilter( mojoDescriptor ); 283 List<MavenProject> projectsToResolve = 284 LifecycleDependencyResolver.getProjects( session.getCurrentProject(), session, 285 mojoDescriptor.isAggregator() ); 286 for ( MavenProject projectToResolve : projectsToResolve ) 287 { 288 projectToResolve.setArtifactFilter( artifactFilter ); 289 } 290 } 291 292 private ArtifactFilter getArtifactFilter( MojoDescriptor mojoDescriptor ) 293 { 294 String scopeToResolve = mojoDescriptor.getDependencyResolutionRequired(); 295 String scopeToCollect = mojoDescriptor.getDependencyCollectionRequired(); 296 297 List<String> scopes = new ArrayList<String>( 2 ); 298 if ( StringUtils.isNotEmpty( scopeToCollect ) ) 299 { 300 scopes.add( scopeToCollect ); 301 } 302 if ( StringUtils.isNotEmpty( scopeToResolve ) ) 303 { 304 scopes.add( scopeToResolve ); 305 } 306 307 if ( scopes.isEmpty() ) 308 { 309 return null; 310 } 311 else 312 { 313 return new CumulativeScopeArtifactFilter( scopes ); 314 } 315 } 316 317 public List<MavenProject> executeForkedExecutions( MojoExecution mojoExecution, MavenSession session, 318 ProjectIndex projectIndex ) 319 throws LifecycleExecutionException 320 { 321 List<MavenProject> forkedProjects = Collections.emptyList(); 322 323 Map<String, List<MojoExecution>> forkedExecutions = mojoExecution.getForkedExecutions(); 324 325 if ( !forkedExecutions.isEmpty() ) 326 { 327 eventCatapult.fire( ExecutionEvent.Type.ForkStarted, session, mojoExecution ); 328 329 MavenProject project = session.getCurrentProject(); 330 331 forkedProjects = new ArrayList<MavenProject>( forkedExecutions.size() ); 332 333 try 334 { 335 for ( Map.Entry<String, List<MojoExecution>> fork : forkedExecutions.entrySet() ) 336 { 337 String projectId = fork.getKey(); 338 339 int index = projectIndex.getIndices().get( projectId ); 340 341 MavenProject forkedProject = projectIndex.getProjects().get( projectId ); 342 343 forkedProjects.add( forkedProject ); 344 345 MavenProject executedProject = forkedProject.clone(); 346 347 forkedProject.setExecutionProject( executedProject ); 348 349 List<MojoExecution> mojoExecutions = fork.getValue(); 350 351 if ( mojoExecutions.isEmpty() ) 352 { 353 continue; 354 } 355 356 try 357 { 358 session.setCurrentProject( executedProject ); 359 session.getProjects().set( index, executedProject ); 360 projectIndex.getProjects().put( projectId, executedProject ); 361 362 eventCatapult.fire( ExecutionEvent.Type.ForkedProjectStarted, session, mojoExecution ); 363 364 execute( session, mojoExecutions, projectIndex ); 365 366 eventCatapult.fire( ExecutionEvent.Type.ForkedProjectSucceeded, session, mojoExecution ); 367 } 368 catch ( LifecycleExecutionException e ) 369 { 370 eventCatapult.fire( ExecutionEvent.Type.ForkedProjectFailed, session, mojoExecution, e ); 371 372 throw e; 373 } 374 finally 375 { 376 projectIndex.getProjects().put( projectId, forkedProject ); 377 session.getProjects().set( index, forkedProject ); 378 session.setCurrentProject( project ); 379 } 380 } 381 382 eventCatapult.fire( ExecutionEvent.Type.ForkSucceeded, session, mojoExecution ); 383 } 384 catch ( LifecycleExecutionException e ) 385 { 386 eventCatapult.fire( ExecutionEvent.Type.ForkFailed, session, mojoExecution, e ); 387 388 throw e; 389 } 390 } 391 392 return forkedProjects; 393 } 394}