| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.apache.maven.lifecycle.internal.builder; |
| |
| import javax.inject.Inject; |
| import javax.inject.Named; |
| import javax.inject.Singleton; |
| |
| import java.util.List; |
| import java.util.Set; |
| import java.util.stream.Collectors; |
| |
| import org.apache.maven.artifact.Artifact; |
| import org.apache.maven.execution.BuildFailure; |
| import org.apache.maven.execution.ExecutionEvent; |
| import org.apache.maven.execution.MavenExecutionRequest; |
| import org.apache.maven.execution.MavenSession; |
| import org.apache.maven.internal.MultilineMessageHelper; |
| import org.apache.maven.lifecycle.LifecycleExecutionException; |
| import org.apache.maven.lifecycle.LifecycleNotFoundException; |
| import org.apache.maven.lifecycle.LifecyclePhaseNotFoundException; |
| import org.apache.maven.lifecycle.MavenExecutionPlan; |
| import org.apache.maven.lifecycle.internal.DefaultLifecyclePluginAnalyzer; |
| import org.apache.maven.lifecycle.internal.ExecutionEventCatapult; |
| import org.apache.maven.lifecycle.internal.LifecycleDebugLogger; |
| import org.apache.maven.lifecycle.internal.LifecycleExecutionPlanCalculator; |
| import org.apache.maven.lifecycle.internal.ReactorContext; |
| import org.apache.maven.lifecycle.internal.TaskSegment; |
| import org.apache.maven.model.Plugin; |
| import org.apache.maven.plugin.InvalidPluginDescriptorException; |
| import org.apache.maven.plugin.MojoExecution; |
| import org.apache.maven.plugin.MojoNotFoundException; |
| import org.apache.maven.plugin.PluginDescriptorParsingException; |
| import org.apache.maven.plugin.PluginNotFoundException; |
| import org.apache.maven.plugin.PluginResolutionException; |
| import org.apache.maven.plugin.descriptor.MojoDescriptor; |
| import org.apache.maven.plugin.prefix.NoPluginFoundForPrefixException; |
| import org.apache.maven.plugin.version.PluginVersionResolutionException; |
| import org.apache.maven.project.MavenProject; |
| import org.codehaus.plexus.classworlds.realm.ClassRealm; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| /** |
| * Common code that is shared by the LifecycleModuleBuilder and the LifeCycleWeaveBuilder |
| * |
| * @since 3.0 |
| * Builds one or more lifecycles for a full module |
| * NOTE: This class is not part of any public api and can be changed or deleted without prior notice. |
| */ |
| @Named |
| @Singleton |
| public class BuilderCommon { |
| private final Logger logger; |
| private final LifecycleDebugLogger lifecycleDebugLogger; |
| private final LifecycleExecutionPlanCalculator lifeCycleExecutionPlanCalculator; |
| private final ExecutionEventCatapult eventCatapult; |
| |
| @Inject |
| public BuilderCommon( |
| LifecycleDebugLogger lifecycleDebugLogger, |
| LifecycleExecutionPlanCalculator lifeCycleExecutionPlanCalculator, |
| ExecutionEventCatapult eventCatapult) { |
| this.logger = LoggerFactory.getLogger(getClass()); |
| this.lifecycleDebugLogger = lifecycleDebugLogger; |
| this.lifeCycleExecutionPlanCalculator = lifeCycleExecutionPlanCalculator; |
| this.eventCatapult = eventCatapult; |
| } |
| |
| /** |
| * Ctor needed for UT. |
| */ |
| BuilderCommon( |
| LifecycleDebugLogger lifecycleDebugLogger, |
| LifecycleExecutionPlanCalculator lifeCycleExecutionPlanCalculator, |
| ExecutionEventCatapult eventCatapult, |
| Logger logger) { |
| this.lifecycleDebugLogger = lifecycleDebugLogger; |
| this.lifeCycleExecutionPlanCalculator = lifeCycleExecutionPlanCalculator; |
| this.eventCatapult = eventCatapult; |
| this.logger = logger; |
| } |
| |
| public MavenExecutionPlan resolveBuildPlan( |
| MavenSession session, MavenProject project, TaskSegment taskSegment, Set<Artifact> projectArtifacts) |
| throws PluginNotFoundException, PluginResolutionException, LifecyclePhaseNotFoundException, |
| PluginDescriptorParsingException, MojoNotFoundException, InvalidPluginDescriptorException, |
| NoPluginFoundForPrefixException, LifecycleNotFoundException, PluginVersionResolutionException, |
| LifecycleExecutionException { |
| MavenExecutionPlan executionPlan = |
| lifeCycleExecutionPlanCalculator.calculateExecutionPlan(session, project, taskSegment.getTasks()); |
| |
| lifecycleDebugLogger.debugProjectPlan(project, executionPlan); |
| |
| if (session.getRequest().getDegreeOfConcurrency() > 1 |
| && session.getProjects().size() > 1) { |
| final Set<Plugin> unsafePlugins = executionPlan.getNonThreadSafePlugins(); |
| if (!unsafePlugins.isEmpty()) { |
| for (String s : MultilineMessageHelper.format( |
| "Your build is requesting parallel execution, but this project contains the following " |
| + "plugin(s) that have goals not marked as thread-safe to support parallel execution.", |
| "While this /may/ work fine, please look for plugin updates and/or " |
| + "request plugins be made thread-safe.", |
| "If reporting an issue, report it against the plugin in question, not against Apache Maven.")) { |
| logger.warn(s); |
| } |
| if (logger.isDebugEnabled()) { |
| final Set<MojoDescriptor> unsafeGoals = executionPlan.getNonThreadSafeMojos(); |
| logger.warn("The following goals are not marked as thread-safe in " + project.getName() + ":"); |
| for (MojoDescriptor unsafeGoal : unsafeGoals) { |
| logger.warn(" " + unsafeGoal.getId()); |
| } |
| } else { |
| logger.warn("The following plugins are not marked as thread-safe in " + project.getName() + ":"); |
| for (Plugin unsafePlugin : unsafePlugins) { |
| logger.warn(" " + unsafePlugin.getId()); |
| } |
| logger.warn(""); |
| logger.warn("Enable verbose output (-X) to see precisely which goals are not marked as" |
| + " thread-safe."); |
| } |
| logger.warn(MultilineMessageHelper.separatorLine()); |
| } |
| } |
| |
| final String defaulModelId = DefaultLifecyclePluginAnalyzer.DEFAULTLIFECYCLEBINDINGS_MODELID; |
| |
| List<String> unversionedPlugins = executionPlan.getMojoExecutions().stream() |
| .map(MojoExecution::getPlugin) |
| .filter(p -> p.getLocation("version") != null |
| && p.getLocation("version").getSource() != null |
| && defaulModelId.equals( |
| p.getLocation("version").getSource().getModelId())) |
| .distinct() |
| .map(Plugin::getArtifactId) // managed by us, groupId is always o.a.m.plugins |
| .collect(Collectors.toList()); |
| |
| if (!unversionedPlugins.isEmpty()) { |
| logger.warn("Version not locked for default bindings plugins " + unversionedPlugins |
| + ", you should define versions in pluginManagement section of your " + "pom.xml or parent"); |
| } |
| |
| return executionPlan; |
| } |
| |
| public void handleBuildError( |
| final ReactorContext buildContext, |
| final MavenSession rootSession, |
| final MavenSession currentSession, |
| final MavenProject mavenProject, |
| Throwable t, |
| final long buildStartTime) { |
| // record the error and mark the project as failed |
| long buildEndTime = System.currentTimeMillis(); |
| buildContext.getResult().addException(t); |
| buildContext.getResult().addBuildSummary(new BuildFailure(mavenProject, buildEndTime - buildStartTime, t)); |
| |
| // notify listeners about "soft" project build failures only |
| if (t instanceof Exception && !(t instanceof RuntimeException)) { |
| eventCatapult.fire(ExecutionEvent.Type.ProjectFailed, currentSession, null, (Exception) t); |
| } |
| |
| // reactor failure modes |
| if (t instanceof RuntimeException || !(t instanceof Exception)) { |
| // fail fast on RuntimeExceptions, Errors and "other" Throwables |
| // assume these are system errors and further build is meaningless |
| buildContext.getReactorBuildStatus().halt(); |
| } else if (MavenExecutionRequest.REACTOR_FAIL_NEVER.equals(rootSession.getReactorFailureBehavior())) { |
| // continue the build |
| } else if (MavenExecutionRequest.REACTOR_FAIL_AT_END.equals(rootSession.getReactorFailureBehavior())) { |
| // continue the build but ban all projects that depend on the failed one |
| buildContext.getReactorBuildStatus().blackList(mavenProject); |
| } else if (MavenExecutionRequest.REACTOR_FAIL_FAST.equals(rootSession.getReactorFailureBehavior())) { |
| buildContext.getReactorBuildStatus().halt(); |
| } else { |
| logger.error("invalid reactor failure behavior " + rootSession.getReactorFailureBehavior()); |
| buildContext.getReactorBuildStatus().halt(); |
| } |
| } |
| |
| public static void attachToThread(MavenProject currentProject) { |
| ClassRealm projectRealm = currentProject.getClassRealm(); |
| if (projectRealm != null) { |
| Thread.currentThread().setContextClassLoader(projectRealm); |
| } |
| } |
| |
| // TODO I'm really wondering where this method belongs; smells like it should be on MavenProject, but for some |
| // reason it isn't ? This localization is kind-of a code smell. |
| |
| public static String getKey(MavenProject project) { |
| return project.getGroupId() + ':' + project.getArtifactId() + ':' + project.getVersion(); |
| } |
| } |