View Javadoc
1   package org.apache.maven.lifecycle.internal.builder;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *  http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import org.apache.maven.artifact.Artifact;
23  import org.apache.maven.execution.BuildFailure;
24  import org.apache.maven.execution.ExecutionEvent;
25  import org.apache.maven.execution.MavenExecutionRequest;
26  import org.apache.maven.execution.MavenSession;
27  import org.apache.maven.internal.MultilineMessageHelper;
28  import org.apache.maven.lifecycle.LifecycleExecutionException;
29  import org.apache.maven.lifecycle.LifecycleNotFoundException;
30  import org.apache.maven.lifecycle.LifecyclePhaseNotFoundException;
31  import org.apache.maven.lifecycle.MavenExecutionPlan;
32  import org.apache.maven.lifecycle.internal.ExecutionEventCatapult;
33  import org.apache.maven.lifecycle.internal.LifecycleDebugLogger;
34  import org.apache.maven.lifecycle.internal.LifecycleExecutionPlanCalculator;
35  import org.apache.maven.lifecycle.internal.ReactorContext;
36  import org.apache.maven.lifecycle.internal.TaskSegment;
37  import org.apache.maven.model.Plugin;
38  import org.apache.maven.plugin.InvalidPluginDescriptorException;
39  import org.apache.maven.plugin.MojoNotFoundException;
40  import org.apache.maven.plugin.PluginDescriptorParsingException;
41  import org.apache.maven.plugin.PluginNotFoundException;
42  import org.apache.maven.plugin.PluginResolutionException;
43  import org.apache.maven.plugin.descriptor.MojoDescriptor;
44  import org.apache.maven.plugin.prefix.NoPluginFoundForPrefixException;
45  import org.apache.maven.plugin.version.PluginVersionResolutionException;
46  import org.apache.maven.project.MavenProject;
47  import org.codehaus.plexus.classworlds.realm.ClassRealm;
48  import org.codehaus.plexus.component.annotations.Component;
49  import org.codehaus.plexus.component.annotations.Requirement;
50  import org.codehaus.plexus.logging.Logger;
51  
52  import java.util.Set;
53  
54  /**
55   * Common code that is shared by the LifecycleModuleBuilder and the LifeCycleWeaveBuilder
56   *
57   * @since 3.0
58   * @author Kristian Rosenvold
59   *         Builds one or more lifecycles for a full module
60   *         NOTE: This class is not part of any public api and can be changed or deleted without prior notice.
61   */
62  @Component( role = BuilderCommon.class )
63  public class BuilderCommon
64  {
65      @Requirement
66      private LifecycleDebugLogger lifecycleDebugLogger;
67  
68      @Requirement
69      private LifecycleExecutionPlanCalculator lifeCycleExecutionPlanCalculator;
70  
71      @Requirement
72      private ExecutionEventCatapult eventCatapult;
73  
74      @Requirement
75      private Logger logger;
76  
77  
78      public BuilderCommon()
79      {
80      }
81  
82      public BuilderCommon( LifecycleDebugLogger lifecycleDebugLogger,
83                            LifecycleExecutionPlanCalculator lifeCycleExecutionPlanCalculator, Logger logger )
84      {
85          this.lifecycleDebugLogger = lifecycleDebugLogger;
86          this.lifeCycleExecutionPlanCalculator = lifeCycleExecutionPlanCalculator;
87          this.logger = logger;
88      }
89  
90      public MavenExecutionPlan resolveBuildPlan( MavenSession session, MavenProject project, TaskSegment taskSegment,
91                                                  Set<Artifact> projectArtifacts )
92          throws PluginNotFoundException, PluginResolutionException, LifecyclePhaseNotFoundException,
93          PluginDescriptorParsingException, MojoNotFoundException, InvalidPluginDescriptorException,
94          NoPluginFoundForPrefixException, LifecycleNotFoundException, PluginVersionResolutionException,
95          LifecycleExecutionException
96      {
97          MavenExecutionPlan executionPlan =
98              lifeCycleExecutionPlanCalculator.calculateExecutionPlan( session, project, taskSegment.getTasks() );
99  
100         lifecycleDebugLogger.debugProjectPlan( project, executionPlan );
101 
102         if ( session.getRequest().getDegreeOfConcurrency() > 1 && session.getProjects().size() > 1 )
103         {
104             final Set<Plugin> unsafePlugins = executionPlan.getNonThreadSafePlugins();
105             if ( !unsafePlugins.isEmpty() )
106             {
107                 for ( String s : MultilineMessageHelper.format(
108                         "Your build is requesting parallel execution, but this project contains the following "
109                                 + "plugin(s) that have goals not marked as thread-safe to support parallel execution.",
110                         "While this /may/ work fine, please look for plugin updates and/or "
111                                 + "request plugins be made thread-safe.",
112                         "If reporting an issue, report it against the plugin in question, not against Apache Maven." ) )
113                 {
114                     logger.warn( s );
115                 }
116                 if ( logger.isDebugEnabled() )
117                 {
118                     final Set<MojoDescriptor> unsafeGoals = executionPlan.getNonThreadSafeMojos();
119                     logger.warn( "The following goals are not marked as thread-safe in " + project.getName() + ":" );
120                     for ( MojoDescriptor unsafeGoal : unsafeGoals )
121                     {
122                         logger.warn( "  " + unsafeGoal.getId() );
123                     }
124                 }
125                 else
126                 {
127                     logger.warn( "The following plugins are not marked as thread-safe in " + project.getName() + ":" );
128                     for ( Plugin unsafePlugin : unsafePlugins )
129                     {
130                         logger.warn( "  " + unsafePlugin.getId() );
131                     }
132                     logger.warn( "" );
133                     logger.warn( "Enable debug to see precisely which goals are not marked as thread-safe." );
134                 }
135                 logger.warn( MultilineMessageHelper.separatorLine() );
136             }
137         }
138 
139         return executionPlan;
140     }
141 
142     public void handleBuildError( final ReactorContext buildContext, final MavenSession rootSession,
143                                   final MavenSession currentSession, final MavenProject mavenProject, Throwable t,
144                                   final long buildStartTime )
145     {
146         // record the error and mark the project as failed
147         long buildEndTime = System.currentTimeMillis();
148         buildContext.getResult().addException( t );
149         buildContext.getResult().addBuildSummary( new BuildFailure( mavenProject, buildEndTime - buildStartTime, t ) );
150 
151         // notify listeners about "soft" project build failures only
152         if ( t instanceof Exception && !( t instanceof RuntimeException ) )
153         {
154             eventCatapult.fire( ExecutionEvent.Type.ProjectFailed, currentSession, null, (Exception) t );
155         }
156 
157         // reactor failure modes
158         if ( t instanceof RuntimeException || !( t instanceof Exception ) )
159         {
160             // fail fast on RuntimeExceptions, Errors and "other" Throwables
161             // assume these are system errors and further build is meaningless
162             buildContext.getReactorBuildStatus().halt();
163         }
164         else if ( MavenExecutionRequest.REACTOR_FAIL_NEVER.equals( rootSession.getReactorFailureBehavior() ) )
165         {
166             // continue the build
167         }
168         else if ( MavenExecutionRequest.REACTOR_FAIL_AT_END.equals( rootSession.getReactorFailureBehavior() ) )
169         {
170             // continue the build but ban all projects that depend on the failed one
171             buildContext.getReactorBuildStatus().blackList( mavenProject );
172         }
173         else if ( MavenExecutionRequest.REACTOR_FAIL_FAST.equals( rootSession.getReactorFailureBehavior() ) )
174         {
175             buildContext.getReactorBuildStatus().halt();
176         }
177         else
178         {
179             logger.error( "invalid reactor failure behavior " + rootSession.getReactorFailureBehavior() );
180             buildContext.getReactorBuildStatus().halt();
181         }
182     }
183 
184     public static void attachToThread( MavenProject currentProject )
185     {
186         ClassRealm projectRealm = currentProject.getClassRealm();
187         if ( projectRealm != null )
188         {
189             Thread.currentThread().setContextClassLoader( projectRealm );
190         }
191     }
192 
193     // TODO I'm really wondering where this method belongs; smells like it should be on MavenProject, but for some
194     // reason it isn't ? This localization is kind-of a code smell.
195 
196     public static String getKey( MavenProject project )
197     {
198         return project.getGroupId() + ':' + project.getArtifactId() + ':' + project.getVersion();
199     }
200 
201 }