From a823e5cedaf67d0ad0d698ddb6ed01d1b24f1e73 Mon Sep 17 00:00:00 2001 From: Ivan Bessonov Date: Wed, 27 Jun 2018 13:04:03 +0300 Subject: [PATCH] IDEA-189973 maven - support for debugging of exec:exec maven run configurations --- ...igurationExecutionEnvironmentProvider.java | 2 +- .../execution/MavenRunConfiguration.java | 141 ++++++++++++++++-- 2 files changed, 130 insertions(+), 13 deletions(-) diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/execution/MavenApplicationConfigurationExecutionEnvironmentProvider.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/execution/MavenApplicationConfigurationExecutionEnvironmentProvider.java index 5c4b8d595bc8..65aa3e164e19 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/execution/MavenApplicationConfigurationExecutionEnvironmentProvider.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/execution/MavenApplicationConfigurationExecutionEnvironmentProvider.java @@ -147,7 +147,7 @@ public class MavenApplicationConfigurationExecutionEnvironmentProvider implement mavenRunConfiguration.setShowConsoleOnStdErr(applicationConfiguration.isShowConsoleOnStdErr()); } - private static List patchVmParameters(ParametersList vmParameters) { + public static List patchVmParameters(ParametersList vmParameters) { List patchedVmParameters = new ArrayList<>(vmParameters.getList()); for (Iterator iterator = patchedVmParameters.iterator(); iterator.hasNext(); ) { String parameter = iterator.next(); diff --git a/plugins/maven/src/main/java/org/jetbrains/idea/maven/execution/MavenRunConfiguration.java b/plugins/maven/src/main/java/org/jetbrains/idea/maven/execution/MavenRunConfiguration.java index 9184070326a1..755a23a9c9e0 100644 --- a/plugins/maven/src/main/java/org/jetbrains/idea/maven/execution/MavenRunConfiguration.java +++ b/plugins/maven/src/main/java/org/jetbrains/idea/maven/execution/MavenRunConfiguration.java @@ -3,6 +3,8 @@ */ package org.jetbrains.idea.maven.execution; +import com.intellij.debugger.impl.DebuggerManagerImpl; +import com.intellij.debugger.settings.DebuggerSettings; import com.intellij.diagnostic.logging.LogConfigurationPanel; import com.intellij.execution.*; import com.intellij.execution.configurations.*; @@ -11,18 +13,35 @@ import com.intellij.execution.process.ProcessAdapter; import com.intellij.execution.process.ProcessEvent; import com.intellij.execution.runners.ExecutionEnvironment; import com.intellij.execution.runners.ProgramRunner; +import com.intellij.execution.util.JavaParametersUtil; import com.intellij.openapi.options.SettingsEditor; import com.intellij.openapi.options.SettingsEditorGroup; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.InvalidDataException; import com.intellij.openapi.util.WriteExternalException; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vfs.VfsUtil; +import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.wm.ToolWindowId; import com.intellij.util.xmlb.XmlSerializer; import org.jdom.Element; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +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.model.MavenConstants; import org.jetbrains.idea.maven.project.*; +import java.io.File; +import java.util.List; +import java.util.regex.Pattern; + +import static com.intellij.openapi.util.io.FileUtil.toSystemDependentName; +import static com.intellij.util.containers.ContainerUtil.indexOf; +import static org.jetbrains.idea.maven.execution.MavenApplicationConfigurationExecutionEnvironmentProvider.patchVmParameters; + public class MavenRunConfiguration extends LocatableConfigurationBase implements ModuleRunProfile { private MavenSettings mySettings; @@ -127,18 +146,7 @@ public class MavenRunConfiguration extends LocatableConfigurationBase implements @NotNull public RemoteConnectionCreator createRemoteConnectionCreator(JavaParameters javaParameters) { - return new RemoteConnectionCreator() { - @Nullable - @Override - public RemoteConnection createRemoteConnection(ExecutionEnvironment environment) { - return null; //TODO IDEA-189973 filter and patch exec goals to support debug - } - - @Override - public boolean isPollConnection() { - return true; - } - }; + return new ExecRemoteConnectionCreator(javaParameters, this); } private void updateProjectsFolders() { @@ -223,4 +231,113 @@ public class MavenRunConfiguration extends LocatableConfigurationBase implements return new MavenSettings(myGeneralSettings, myRunnerSettings, myRunnerParameters); } } + + private static class ExecRemoteConnectionCreator implements RemoteConnectionCreator { + + private static final Pattern EXEC_MAVEN_PLUGIN_PATTERN = Pattern.compile("org[.]codehaus[.]mojo:exec-maven-plugin(:[\\d.]+)?:exec"); + + private final JavaParameters myJavaParameters; + private final MavenRunConfiguration myRunConfiguration; + + public ExecRemoteConnectionCreator(JavaParameters javaParameters, MavenRunConfiguration runConfiguration) { + myJavaParameters = javaParameters; + myRunConfiguration = runConfiguration; + } + + @Nullable + @Override + public RemoteConnection createRemoteConnection(ExecutionEnvironment environment) { + ParametersList programParametersList = myJavaParameters.getProgramParametersList(); + boolean execGoal = programParametersList.getList().stream().anyMatch(parameter -> + parameter.equals("exec:exec") || EXEC_MAVEN_PLUGIN_PATTERN.matcher(parameter).matches() + ); + if (!execGoal) { + return null; + } + + Project project = myRunConfiguration.getProject(); + MavenRunnerParameters runnerParameters = myRunConfiguration.getRunnerParameters(); + + JavaParameters parameters = new JavaParameters(); + RemoteConnection connection; + try { + // there's no easy and reliable way to know the version of target JRE, but without it there won't be any debugger agent settings + parameters.setJdk(JavaParametersUtil.createProjectJdk(project, null)); + connection = DebuggerManagerImpl.createDebugParameters( + parameters, false, DebuggerSettings.getInstance().DEBUGGER_TRANSPORT, "", false); + } + catch (ExecutionException e) { + throw new RuntimeException("Cannot create debug connection", e); + } + + String execArgsStr; + + String execArgsPrefix = "-Dexec.args="; + int execArgsIndex = indexOf(programParametersList.getList(), (Condition)s -> s.startsWith(execArgsPrefix)); + if (execArgsIndex != -1) { + execArgsStr = programParametersList.get(execArgsIndex).substring(execArgsPrefix.length()); + } + else { + execArgsStr = getExecArgsFromPomXml(project, runnerParameters); + } + + ParametersList execArgs = new ParametersList(); + execArgs.addAll(patchVmParameters(parameters.getVMParametersList())); + + execArgs.addParametersString(execArgsStr); + + String classPath = toSystemDependentName(parameters.getClassPath().getPathsString()); + if (StringUtil.isNotEmpty(classPath)) { + appendToClassPath(execArgs, classPath); + } + + String execArgsCommandLineArg = execArgsPrefix + execArgs.getParametersString(); + if (execArgsIndex != -1) { + programParametersList.set(execArgsIndex, execArgsCommandLineArg); + } + else { + programParametersList.add(execArgsCommandLineArg); + } + + return connection; + } + + @Override + public boolean isPollConnection() { + return true; + } + + private static String getExecArgsFromPomXml(Project project, MavenRunnerParameters runnerParameters) { + VirtualFile workingDir = VfsUtil.findFileByIoFile(runnerParameters.getWorkingDirFile(), false); + if (workingDir != null) { + String pomFileName = StringUtil.defaultIfEmpty(runnerParameters.getPomFileName(), MavenConstants.POM_XML); + VirtualFile pomFile = workingDir.findChild(pomFileName); + if (pomFile != null) { + MavenDomProjectModel projectModel = MavenDomUtil.getMavenDomProjectModel(project, pomFile); + if (projectModel != null) { + return StringUtil.notNullize(MavenPropertyResolver.resolve("${exec.args}", projectModel)); + } + } + } + return ""; + } + + private static void appendToClassPath(ParametersList execArgs, String classPath) { + List execArgsList = execArgs.getList(); + int classPathIndex = execArgsList.indexOf("-classpath"); + if (classPathIndex == -1) { + classPathIndex = execArgsList.indexOf("-cp"); + } + if (classPathIndex == -1) { + execArgs.prependAll("-classpath", "%classpath" + File.pathSeparator + classPath); + } + else if (classPathIndex + 1 == execArgsList.size()) { // invalid command line, but we still have to patch it + execArgs.add("%classpath" + File.pathSeparator + classPath); + } + else { + String oldClassPath = execArgs.get(classPathIndex + 1); + execArgs.set(classPathIndex + 1, oldClassPath + File.pathSeparator + classPath); + } + } + } }