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