IDEA-217889 and IDEA-196522 - fix maven "resume from" action

GitOrigin-RevId: 8fcc0470552334b7b3f838ebc502ae7e13854bb6
This commit is contained in:
Alexander Bubenchikov
2019-08-01 16:03:35 +03:00
committed by intellij-monorepo-bot
parent c9f91db207
commit 78faca17f9
7 changed files with 286 additions and 132 deletions
@@ -3,13 +3,16 @@ package org.jetbrains.maven.server;
import org.apache.maven.eventspy.AbstractEventSpy;
import org.apache.maven.execution.ExecutionEvent;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.plugin.MojoExecution;
import org.apache.maven.project.DependencyResolutionRequest;
import org.apache.maven.project.DependencyResolutionResult;
import org.apache.maven.project.MavenProject;
import org.eclipse.aether.RepositoryEvent;
import javax.inject.Named;
import javax.inject.Singleton;
import java.util.ArrayList;
import java.util.List;
import static org.jetbrains.maven.server.EventInfoPrinter.printMavenEventInfo;
@@ -86,7 +89,30 @@ public class IntellijMavenSpy extends AbstractEventSpy {
errMessage);
}
else {
printMavenEventInfo(event.getType(), "id", projectId);
if (event.getType() == ExecutionEvent.Type.SessionStarted) {
printSessionStartedEventAndReactorData(event, projectId);
}
else {
printMavenEventInfo(event.getType(), "id", projectId);
}
}
}
private static void printSessionStartedEventAndReactorData(ExecutionEvent event, String projectId) {
MavenSession session = event.getSession();
if (session != null) {
List<MavenProject> projectsInReactor = session.getProjects();
if (projectsInReactor == null) {
projectsInReactor = new ArrayList<MavenProject>();
}
StringBuilder builder = new StringBuilder();
for (MavenProject project : projectsInReactor) {
builder.append(project.getGroupId()).append(":").append(project.getArtifactId()).append("&&");
}
printMavenEventInfo(ExecutionEvent.Type.SessionStarted, "id", projectId, "projects", builder.toString());
}
else {
printMavenEventInfo(ExecutionEvent.Type.SessionStarted, "id", projectId, "projects", "");
}
}
}
@@ -21,6 +21,7 @@ import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.maven.execution.MavenResumeAction;
import org.jetbrains.idea.maven.externalSystemIntegration.output.MavenLogOutputParser;
import org.jetbrains.idea.maven.externalSystemIntegration.output.MavenOutputParserProvider;
import org.jetbrains.idea.maven.externalSystemIntegration.output.MavenParsingContext;
import org.jetbrains.idea.maven.project.MavenConsoleImpl;
import org.jetbrains.idea.maven.utils.MavenUtil;
@@ -75,7 +76,8 @@ public class MavenBuildEventProcessor implements AnsiEscapeDecoder.ColoredTextAc
.withExecutionFilters(MavenConsoleImpl.getMavenConsoleFilters(myProject));
if (executionEnvironment != null && processHandler != null) {
startEvent
.withRestartAction(new MavenResumeAction(processHandler, DefaultJavaProgramRunner.getInstance(), executionEnvironment));
.withRestartAction(
new MavenResumeAction(processHandler, DefaultJavaProgramRunner.getInstance(), executionEnvironment, getParsingContext()));
}
myBuildProgressListener.onEvent(myDescriptor.getId(), startEvent);
@@ -92,6 +94,10 @@ public class MavenBuildEventProcessor implements AnsiEscapeDecoder.ColoredTextAc
}
}
public MavenParsingContext getParsingContext() {
return myParser.getParsingContext();
}
@Override
public void coloredTextAvailable(@NotNull String text, @NotNull Key outputType) {
onTextAvailable(text, ProcessOutputType.isStderr(outputType));
@@ -35,10 +35,15 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Key;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.text.VersionComparatorUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.maven.externalSystemIntegration.output.MavenParsingContext;
import org.jetbrains.idea.maven.project.MavenGeneralSettings;
import org.jetbrains.idea.maven.project.MavenProject;
import org.jetbrains.idea.maven.project.MavenProjectsManager;
import org.jetbrains.idea.maven.server.MavenServerManager;
import org.jetbrains.idea.maven.utils.MavenUtil;
import java.util.ArrayList;
import java.util.List;
@@ -63,6 +68,7 @@ public class MavenResumeAction extends AnAction {
private final ProgramRunner myRunner;
private final ExecutionEnvironment myEnvironment;
private final MavenParsingContext myContext;
private int myState = STATE_INITIAL;
@@ -74,116 +80,39 @@ public class MavenResumeAction extends AnAction {
private String myResumeModuleId;
private String myMavenVersion;
public MavenResumeAction(ProcessHandler processHandler,
ProgramRunner runner,
ExecutionEnvironment environment) {
super("Resume build from specified module", null, AllIcons.RunConfigurations.RerunFailedTests);
ExecutionEnvironment environment,
MavenParsingContext context) {
super("Resume Build From Specified Module", null, AllIcons.RunConfigurations.RerunFailedTests);
myRunner = runner;
myEnvironment = environment;
myContext = context;
final MavenRunConfiguration runConfiguration = (MavenRunConfiguration)environment.getRunProfile();
myMavenVersion = getMavenVersion(runConfiguration);
getTemplatePresentation().setEnabled(false);
processHandler.addProcessListener(new ProcessAdapter() {
@Override
public void processTerminated(@NotNull ProcessEvent event) {
if (myState == STATE_WTF) return;
if (VersionComparatorUtil.compare(myMavenVersion, "3.5.3") < 0 || context == null) {
processHandler.addProcessListener(new LegacyMavenResumeProcessAdapter(runConfiguration));
}
else {
processHandler.addProcessListener(new MavenSpyResumeProcessAdapter(runConfiguration));
}
}
if (event.getExitCode() == 0 && myBuildingProjectIndex != myMavenProjectNames.size()) {
log(String.format("Build was success, but not all project was build. Project build order: %s, build index: %d",
myMavenProjectNames,
myBuildingProjectIndex ));
}
if (event.getExitCode() == 1 && myBuildingProjectIndex > 0) {
if (myBuildingProjectIndex == 1 && !hasResumeFromParameter(runConfiguration)) {
return;
}
myResumeFromModuleName = myMavenProjectNames.get(myBuildingProjectIndex - 1);
MavenProject mavenProject = findProjectByName(myResumeFromModuleName);
if (mavenProject != null) {
myResumeModuleId = mavenProject.getMavenId().getGroupId() + ':' + mavenProject.getMavenId().getArtifactId();
}
}
}
@Override
public void onTextAvailable(@NotNull ProcessEvent event, @NotNull Key outputType) {
if (outputType != ProcessOutputTypes.STDOUT) return;
String text = event.getText().trim();
if (text.isEmpty()) return;
String textWithoutInfo = "";
if (text.startsWith("[INFO] ")) {
textWithoutInfo = text.substring("[INFO] ".length()).trim();
}
switch (myState) {
case STATE_INITIAL: // initial state.
if (textWithoutInfo.equals("Reactor build order:")) {
myState = STATE_READING_PROJECT_LIST_OLD_MAVEN;
}
else if (textWithoutInfo.equals("Reactor Build Order:")) {
myState = STATE_READING_PROJECT_LIST;
}
break;
case STATE_READING_PROJECT_LIST:
if (textWithoutInfo.equals("------------------------------------------------------------------------")) {
myState = STATE_WAIT_FOR_BUILD;
}
else if (textWithoutInfo.length() > 0) {
myMavenProjectNames.add(textWithoutInfo);
} else if (!myMavenProjectNames.isEmpty()) {
myState = STATE_WAIT_FOR______;
}
break;
case STATE_READING_PROJECT_LIST_OLD_MAVEN:
if (textWithoutInfo.length() > 0) {
if (text.startsWith("[INFO] ")) {
myMavenProjectNames.add(textWithoutInfo);
}
else {
myState = STATE_WAIT_FOR_BUILD;
}
}
break;
case STATE_WAIT_FOR_BUILD:
if (textWithoutInfo.startsWith("Building ")) {
String projectName = textWithoutInfo.substring("Building ".length());
if (myBuildingProjectIndex >= myMavenProjectNames.size() ||
!projectName.startsWith(myMavenProjectNames.get(myBuildingProjectIndex))) {
myState = STATE_WTF;
log(String.format("Invalid project building order. Defined order: %s, error index: %d, invalid line: %s",
myMavenProjectNames, myBuildingProjectIndex, text));
break;
}
myBuildingProjectIndex++;
}
myState = STATE_WAIT_FOR______;
break;
case STATE_WAIT_FOR______:
if (textWithoutInfo.equals("------------------------------------------------------------------------")) {
myState = STATE_WAIT_FOR_BUILD;
}
break;
case STATE_WTF:
break;
default:
throw new IllegalStateException();
}
}
});
private static String getMavenVersion(MavenRunConfiguration runConfiguration) {
MavenGeneralSettings generalSettings = runConfiguration.getGeneralSettings();
if (generalSettings == null) {
return MavenServerManager.getInstance().getCurrentMavenVersion();
}
else {
return MavenUtil.getMavenVersion(generalSettings.getEffectiveMavenHome());
}
}
private static boolean hasResumeFromParameter(MavenRunConfiguration runConfiguration) {
@@ -309,4 +238,145 @@ public class MavenResumeAction extends AnAction {
Messages.showErrorDialog(project, e1.getMessage(), ExecutionBundle.message("restart.error.message.title"));
}
}
private class LegacyMavenResumeProcessAdapter extends ProcessAdapter {
private final MavenRunConfiguration myRunConfiguration;
LegacyMavenResumeProcessAdapter(MavenRunConfiguration runConfiguration) {myRunConfiguration = runConfiguration;}
@Override
public void processTerminated(@NotNull ProcessEvent event) {
if (myState == STATE_WTF) return;
if (event.getExitCode() == 0 && myBuildingProjectIndex != myMavenProjectNames.size()) {
log(String.format("Build was success, but not all project was build. Project build order: %s, build index: %d",
myMavenProjectNames,
myBuildingProjectIndex));
}
if (event.getExitCode() == 1 && myBuildingProjectIndex > 0) {
if (myBuildingProjectIndex == 1 && !hasResumeFromParameter(myRunConfiguration)) {
return;
}
myResumeFromModuleName = myMavenProjectNames.get(myBuildingProjectIndex - 1);
MavenProject mavenProject = findProjectByName(myResumeFromModuleName);
if (mavenProject != null) {
myResumeModuleId = mavenProject.getMavenId().getGroupId() + ':' + mavenProject.getMavenId().getArtifactId();
}
}
}
@Override
public void onTextAvailable(@NotNull ProcessEvent event, @NotNull Key outputType) {
if (outputType != ProcessOutputTypes.STDOUT) return;
String text = event.getText().trim();
if (text.isEmpty()) return;
String textWithoutInfo = "";
if (text.startsWith("[INFO] ")) {
textWithoutInfo = text.substring("[INFO] ".length()).trim();
}
switch (myState) {
case STATE_INITIAL: // initial state.
if (textWithoutInfo.equals("Reactor build order:")) {
myState = STATE_READING_PROJECT_LIST_OLD_MAVEN;
}
else if (textWithoutInfo.equals("Reactor Build Order:")) {
myState = STATE_READING_PROJECT_LIST;
}
break;
case STATE_READING_PROJECT_LIST:
if (textWithoutInfo.equals("------------------------------------------------------------------------")) {
myState = STATE_WAIT_FOR_BUILD;
}
else if (textWithoutInfo.length() > 0) {
myMavenProjectNames.add(textWithoutInfo);
}
else if (!myMavenProjectNames.isEmpty()) {
myState = STATE_WAIT_FOR______;
}
break;
case STATE_READING_PROJECT_LIST_OLD_MAVEN:
if (textWithoutInfo.length() > 0) {
if (text.startsWith("[INFO] ")) {
myMavenProjectNames.add(textWithoutInfo);
}
else {
myState = STATE_WAIT_FOR_BUILD;
}
}
break;
case STATE_WAIT_FOR_BUILD:
if (textWithoutInfo.startsWith("Building ")) {
String projectName = textWithoutInfo.substring("Building ".length());
if (myBuildingProjectIndex >= myMavenProjectNames.size() ||
!projectName.startsWith(myMavenProjectNames.get(myBuildingProjectIndex))) {
myState = STATE_WTF;
log(String.format("Invalid project building order. Defined order: %s, error index: %d, invalid line: %s",
myMavenProjectNames, myBuildingProjectIndex, text));
break;
}
myBuildingProjectIndex++;
}
myState = STATE_WAIT_FOR______;
break;
case STATE_WAIT_FOR______:
if (textWithoutInfo.equals("------------------------------------------------------------------------")) {
myState = STATE_WAIT_FOR_BUILD;
}
break;
case STATE_WTF:
break;
default:
throw new IllegalStateException();
}
}
}
private class MavenSpyResumeProcessAdapter extends ProcessAdapter {
private final MavenRunConfiguration myRunConfiguration;
MavenSpyResumeProcessAdapter(MavenRunConfiguration runConfiguration) {
myRunConfiguration = runConfiguration;
}
@Override
public void processTerminated(@NotNull ProcessEvent event) {
myContext.getStartedProjects();
if (event.getExitCode() == 0 && myContext.getProjectsInReactor().size() != myContext.getStartedProjects().size()) {
log(String.format("Build was success, but not all project was build. Project build order: %s, built projects: %s",
myContext.getProjectsInReactor(),
myContext.getStartedProjects()));
}
if (event.getExitCode() != 0) {
if (myContext.getStartedProjects().isEmpty()) {
return;
}
if (myContext.getStartedProjects().size() == 1 && !hasResumeFromParameter(myRunConfiguration)) {
return;
}
myResumeModuleId = myContext.getStartedProjects().get(myContext.getStartedProjects().size() - 1);
String[] splitted = myResumeModuleId.split(":");
if (splitted.length < 2) {
myResumeFromModuleName = myResumeModuleId;
}
else {
myResumeFromModuleName = splitted[1];
}
}
}
}
}
@@ -1,6 +1,7 @@
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.idea.maven.execution;
import com.intellij.build.BuildDescriptor;
import com.intellij.build.BuildTreeFilters;
import com.intellij.build.BuildView;
import com.intellij.build.DefaultBuildDescriptor;
@@ -31,6 +32,7 @@ import com.intellij.util.io.BaseDataReader;
import com.intellij.util.io.BaseOutputReader;
import com.intellij.util.xmlb.XmlSerializer;
import org.jdom.Element;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.maven.buildtool.BuildToolConsoleProcessAdapter;
@@ -39,11 +41,13 @@ import org.jetbrains.idea.maven.buildtool.MavenBuildEventProcessor;
import org.jetbrains.idea.maven.dom.MavenDomUtil;
import org.jetbrains.idea.maven.dom.MavenPropertyResolver;
import org.jetbrains.idea.maven.dom.model.MavenDomProjectModel;
import org.jetbrains.idea.maven.externalSystemIntegration.output.MavenParsingContext;
import org.jetbrains.idea.maven.model.MavenConstants;
import org.jetbrains.idea.maven.project.MavenGeneralSettings;
import org.jetbrains.idea.maven.project.MavenGeneralSettingsEditor;
import org.jetbrains.idea.maven.project.MavenProjectsManager;
import org.jetbrains.idea.maven.project.ProjectBundle;
import org.jetbrains.idea.maven.utils.MavenLog;
import org.jetbrains.idea.maven.utils.MavenUtil;
import java.io.File;
@@ -313,42 +317,52 @@ public class MavenRunConfiguration extends LocatableConfigurationBase implements
final ProcessHandler processHandler = startProcess();
final ConsoleView console = createConsoleViewAndAttachToProcess(executor, processHandler);
ExternalSystemTaskId taskId = ExternalSystemTaskId.create(MavenUtil.SYSTEM_ID, EXECUTE_TASK, getProject());
DefaultBuildDescriptor descriptor =
new DefaultBuildDescriptor(taskId, myName, getEnvironment().getProject().getBasePath(), System.currentTimeMillis());
AnAction[] actions = console instanceof BuildView ?
new AnAction[]{BuildTreeFilters.createFilteringActionsGroup((BuildView)console)} : AnAction.EMPTY_ARRAY;
DefaultExecutionResult res = new DefaultExecutionResult(console, processHandler, actions);
final BuildView buildView = createBuildView(executor, taskId, descriptor);
if (buildView == null) {
MavenLog.LOG.warn("buildView is null for " + myName);
}
MavenParsingContext context = initializeParsersAndAttachToProcess(buildView, taskId, descriptor, processHandler);
AnAction[] actions = buildView != null ?
new AnAction[]{BuildTreeFilters.createFilteringActionsGroup(buildView)} : AnAction.EMPTY_ARRAY;
DefaultExecutionResult res = new DefaultExecutionResult(buildView, processHandler, actions);
if (MavenResumeAction.isApplicable(getEnvironment().getProject(), getJavaParameters(), MavenRunConfiguration.this)) {
MavenResumeAction resumeAction = new MavenResumeAction(res.getProcessHandler(), runner, getEnvironment());
MavenResumeAction resumeAction = new MavenResumeAction(res.getProcessHandler(), runner, getEnvironment(), context);
res.setRestartActions(resumeAction);
}
return res;
}
private @Nullable
ConsoleView createConsoleViewAndAttachToProcess(Executor executor, ProcessHandler processHandler) throws ExecutionException {
@Contract("null, _, _, _ -> null;_, _, _,_ -> _")
@Nullable
private MavenParsingContext initializeParsersAndAttachToProcess(@Nullable BuildView buildView,
@NotNull ExternalSystemTaskId taskId,
@NotNull BuildDescriptor descriptor,
@NotNull ProcessHandler processHandler)
throws ExecutionException {
if (buildView == null) {
return null;
}
MavenBuildEventProcessor eventProcessor =
new MavenBuildEventProcessor(getProject(), getProject().getBasePath(), buildView, descriptor, taskId);
processHandler.addProcessListener(new BuildToolConsoleProcessAdapter(eventProcessor, true));
buildView.attachToProcess(new MavenHandlerFilterSpyWrapper(processHandler));
return eventProcessor.getParsingContext();
}
@Nullable
private BuildView createBuildView(@NotNull Executor executor, @NotNull ExternalSystemTaskId taskId,
@NotNull BuildDescriptor descriptor) throws ExecutionException {
ConsoleView console = super.createConsole(executor);
if (console == null) {
return null;
}
if (!Registry.is("maven.build.tool.window.enabled")) {
console.attachToProcess(processHandler);
return console;
}
else {
ExternalSystemTaskId taskId = ExternalSystemTaskId.create(MavenUtil.SYSTEM_ID, EXECUTE_TASK, getProject());
DefaultBuildDescriptor descriptor =
new DefaultBuildDescriptor(taskId, myName, getEnvironment().getProject().getBasePath(), System.currentTimeMillis());
BuildView buildView = BuildViewMavenConsole.createBuildView(getProject(), console, descriptor);
MavenBuildEventProcessor eventProcessor =
new MavenBuildEventProcessor(getProject(), getProject().getBasePath(), buildView, descriptor, taskId);
processHandler.addProcessListener(new BuildToolConsoleProcessAdapter(eventProcessor, true));
buildView.attachToProcess(new MavenHandlerFilterSpyWrapper(processHandler));
return buildView;
}
return BuildViewMavenConsole.createBuildView(getProject(), console, descriptor);
}
@NotNull
@@ -49,6 +49,10 @@ public class MavenLogOutputParser implements BuildOutputParser {
}
}
public MavenParsingContext getParsingContext() {
return myParsingContext;
}
private void completeParsers(Consumer<? super BuildEvent> messageConsumer) {
for (MavenLoggedEventParser parser : myRegisteredEvents) {
parser.finish(myTaskId, messageConsumer);
@@ -3,10 +3,17 @@ package org.jetbrains.idea.maven.externalSystemIntegration.output
import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskId
import com.intellij.util.containers.ContainerUtil
import java.util.*
import java.util.concurrent.CopyOnWriteArrayList
class MavenParsingContext(private val myTaskId: ExternalSystemTaskId) {
lateinit var projectsInReactor: List<String>
val startedProjects = CopyOnWriteArrayList<String>()
/*init {
startedProjects = CopyOnWriteArrayList()
}*/
private val context = ContainerUtil.createConcurrentIntObjectMap<ArrayList<MavenExecutionEntry>>()
private var lastAddedThreadId: Int = 0
@@ -32,11 +39,20 @@ class MavenParsingContext(private val myTaskId: ExternalSystemTaskId) {
if (currentProject == null && create) {
currentProject = ProjectExecutionEntry(id ?: "", threadId)
startedProjects.add(removeVersion(currentProject))
add(threadId, currentProject)
}
return currentProject
}
private fun removeVersion(currentProject: ProjectExecutionEntry): String {
val splitted = currentProject.name.split(":")
if (splitted.size < 3) {
return currentProject.name
}
return "${splitted[0]}:${splitted[1]}"
}
fun getProject(threadId: Int, parameters: Map<String, String>, create: Boolean): ProjectExecutionEntry? {
return getProject(threadId, parameters["id"], create)
}
@@ -11,10 +11,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.idea.maven.externalSystemIntegration.output.MavenParsingContext;
import org.jetbrains.idea.maven.utils.MavenLog;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.*;
import java.util.function.Consumer;
import java.util.stream.Collectors;
@@ -74,6 +71,11 @@ public class MavenSpyOutputParser {
Map<String, String> parameters,
Consumer<? super BuildEvent> messageConsumer) {
switch (type) {
case "SessionStarted": {
List<String> projectsInReactor = getProjectsInReactor(parameters);
myContext.setProjectsInReactor(projectsInReactor);
return;
}
case "ProjectStarted": {
MavenParsingContext.ProjectExecutionEntry execution = myContext.getProject(threadId, parameters, true);
if(execution == null){
@@ -146,6 +148,22 @@ public class MavenSpyOutputParser {
}
}
private static List<String> getProjectsInReactor(Map<String, String> parameters) {
String joined = parameters.get("projects");
if (StringUtil.isEmptyOrSpaces(joined)) {
return Collections.emptyList();
}
List<String> result = new ArrayList<>();
for (String project : joined.split("&&")) {
if (StringUtil.isEmptyOrSpaces(project)) {
continue;
}
result.add(project);
}
return result;
}
private void artifactDownloaded(int threadId, Map<String, String> parameters, Consumer<? super BuildEvent> messageConsumer) {
String artifactCoord = parameters.get("artifactCoord");
if (artifactCoord == null || !downloadingMap.add(artifactCoord)) {