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