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