Skip to content

Commit

Permalink
fix: Debug run config invalid with Gradle
Browse files Browse the repository at this point in the history
Fixes #1311

Signed-off-by: azerr <azerr@redhat.com>
  • Loading branch information
angelozerr committed Sep 14, 2024
1 parent e246f7f commit cd5bc58
Show file tree
Hide file tree
Showing 6 changed files with 168 additions and 43 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.redhat.devtools.intellij.quarkus.run;

import com.intellij.execution.ExecutionListener;
import com.intellij.execution.RunnerAndConfigurationSettings;
import com.intellij.execution.executors.DefaultDebugExecutor;
import com.intellij.execution.process.ProcessEvent;
import com.intellij.execution.process.ProcessHandler;
import com.intellij.execution.process.ProcessListener;
import com.intellij.execution.runners.ExecutionEnvironment;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Key;
import org.jetbrains.annotations.NotNull;

import static com.redhat.devtools.intellij.quarkus.run.QuarkusRunConfiguration.QUARKUS_CONFIGURATION;

class AttachDebuggerExecutionListener implements ExecutionListener {

private final @NotNull Project project;

AttachDebuggerExecutionListener(@NotNull Project project) {
this.project = project;
}

public void processStarting(@NotNull String executorId,
@NotNull ExecutionEnvironment env,
@NotNull ProcessHandler handler) {
if (!DefaultDebugExecutor.EXECUTOR_ID.equals(executorId)) {
return;
}
RunnerAndConfigurationSettings settings = env.getRunnerAndConfigurationSettings();
if (settings != null && settings.getConfiguration() instanceof QuarkusRunConfiguration) {
handler.addProcessListener(new AttachDebuggerProcessListener(project, env));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package com.redhat.devtools.intellij.quarkus.run;

import com.intellij.execution.DefaultExecutionTarget;
import com.intellij.execution.RunManager;
import com.intellij.execution.RunnerAndConfigurationSettings;
import com.intellij.execution.executors.DefaultDebugExecutor;
import com.intellij.execution.process.ProcessEvent;
import com.intellij.execution.process.ProcessListener;
import com.intellij.execution.remote.RemoteConfiguration;
import com.intellij.execution.remote.RemoteConfigurationType;
import com.intellij.execution.runners.ExecutionEnvironment;
import com.intellij.execution.runners.ExecutionUtil;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Key;
import org.jetbrains.annotations.NotNull;

import java.io.IOException;
import java.net.ConnectException;
import java.net.Socket;
import java.nio.charset.StandardCharsets;

import static com.redhat.devtools.intellij.quarkus.run.QuarkusRunConfiguration.QUARKUS_CONFIGURATION;

class AttachDebuggerProcessListener implements ProcessListener {

private static final String JWDP_HANDSHAKE = "JDWP-Handshake";

private final Project project;
private final ExecutionEnvironment env;
private boolean connected;

AttachDebuggerProcessListener(@NotNull Project project,
@NotNull ExecutionEnvironment env) {
this.project = project;
this.env = env;
}

@Override
public void onTextAvailable(@NotNull ProcessEvent event, @NotNull Key outputType) {
String message = event.getText();
if (!connected && message.startsWith("Listening for transport dt_socket at address: ")) {
connected = true;
String s = message.substring("Listening for transport dt_socket at address: ".length(), message.length()).trim();
int port = Integer.valueOf(s);
ProgressManager.getInstance().run(new Task.Backgroundable(project, QUARKUS_CONFIGURATION, false) {
@Override
public void run(@NotNull ProgressIndicator indicator) {
String name = env.getRunProfile().getName();
createRemoteConfiguration(indicator, port,name);
}
});
}
}

private void createRemoteConfiguration(ProgressIndicator indicator, int port, String name) {
indicator.setText("Connecting Java debugger to port " + port);
try {
waitForPortAvailable(port, indicator);
RunnerAndConfigurationSettings settings = RunManager.getInstance(project).createConfiguration(name + " (Remote)", RemoteConfigurationType.class);
RemoteConfiguration remoteConfiguration = (RemoteConfiguration) settings.getConfiguration();
remoteConfiguration.PORT = Integer.toString(port);
long groupId = ExecutionEnvironment.getNextUnusedExecutionId();
ExecutionUtil.runConfiguration(settings, DefaultDebugExecutor.getDebugExecutorInstance(), DefaultExecutionTarget.INSTANCE, groupId);
} catch (IOException e) {
ApplicationManager.getApplication()
.invokeLater(() -> Messages.showErrorDialog("Can' t connector to port " + port, "Quarkus"));
}
}

private void waitForPortAvailable(int port, ProgressIndicator monitor) throws IOException {
long start = System.currentTimeMillis();
while (System.currentTimeMillis() - start < 120_000 && !monitor.isCanceled()) {
try (Socket socket = new Socket("localhost", port)) {
socket.getOutputStream().write(JWDP_HANDSHAKE.getBytes(StandardCharsets.US_ASCII));
return;
} catch (ConnectException e) {
try {
Thread.sleep(1000L);
} catch (InterruptedException e1) {
throw new IOException(e1);
}
}
}
throw new IOException("Can't connect remote debuger to port " + port);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.redhat.devtools.intellij.quarkus.run;

import com.intellij.execution.configurations.RunProfile;
import com.intellij.execution.executors.DefaultDebugExecutor;
import com.intellij.openapi.externalSystem.service.execution.ExternalSystemRunConfiguration;
import com.intellij.openapi.externalSystem.service.execution.ExternalSystemTaskDebugRunner;
import com.redhat.devtools.intellij.quarkus.buildtool.BuildToolDelegate;
import com.redhat.devtools.intellij.quarkus.buildtool.maven.MavenToolDelegate;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class QuarkusExternalSystemTaskDebugRunner extends ExternalSystemTaskDebugRunner {

private static final Logger log = LoggerFactory.getLogger(QuarkusExternalSystemTaskDebugRunner.class);

@Override
public boolean canRun(@NotNull String executorId, @NotNull RunProfile profile) {
if (!DefaultDebugExecutor.EXECUTOR_ID.equals(executorId)) {
return false;
}
if (profile instanceof QuarkusRunConfiguration quarkusRunConfiguration) {
BuildToolDelegate delegate = BuildToolDelegate.getDelegate(quarkusRunConfiguration.getModule());
return !(delegate instanceof MavenToolDelegate);
}
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,25 @@
import com.intellij.execution.configurations.RunProfileState;
import com.intellij.execution.configurations.RuntimeConfigurationException;
import com.intellij.execution.executors.DefaultDebugExecutor;
import com.intellij.execution.process.ProcessEvent;
import com.intellij.execution.process.ProcessHandler;
import com.intellij.execution.process.ProcessListener;
import com.intellij.execution.remote.RemoteConfiguration;
import com.intellij.execution.remote.RemoteConfigurationType;
import com.intellij.execution.runners.ExecutionEnvironment;
import com.intellij.execution.runners.ExecutionEnvironmentBuilder;
import com.intellij.execution.runners.ExecutionUtil;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.externalSystem.service.execution.ExternalSystemRunnableState;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.options.SettingsEditor;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Key;
import com.redhat.devtools.intellij.quarkus.QuarkusModuleUtil;
import com.redhat.devtools.intellij.quarkus.buildtool.BuildToolDelegate;
import com.redhat.devtools.intellij.quarkus.telemetry.TelemetryEventName;
Expand All @@ -53,13 +58,12 @@
import static com.intellij.execution.runners.ExecutionUtil.createEnvironment;

public class QuarkusRunConfiguration extends ModuleBasedConfiguration<RunConfigurationModule, QuarkusRunConfigurationOptions> {

private final static Logger LOGGER = LoggerFactory.getLogger(QuarkusRunConfiguration.class);
private static final String QUARKUS_CONFIGURATION = "Quarkus Configuration";
static final String QUARKUS_CONFIGURATION = "Quarkus Configuration";

private int port = 5005;

private static final String JWDP_HANDSHAKE = "JDWP-Handshake";

public QuarkusRunConfiguration(Project project, ConfigurationFactory factory, String name) {
super(name, getRunConfigurationModule(project), factory);
}
Expand Down Expand Up @@ -135,6 +139,7 @@ public RunProfileState getState(@NotNull Executor executor, @NotNull ExecutionEn
// Create a Gradle or Maven run configuration in memory
RunnerAndConfigurationSettings settings = toolDelegate.getConfigurationDelegate(module, this);
if (settings != null) {
QuarkusRunConfigurationManager.getInstance(module.getProject()); // to be sure that Quarkus execution listener is registered
long groupId = ExecutionEnvironment.getNextUnusedExecutionId();
state = doRunConfiguration(settings, executor, DefaultExecutionTarget.INSTANCE, groupId, null);
}
Expand All @@ -143,49 +148,9 @@ public RunProfileState getState(@NotNull Executor executor, @NotNull ExecutionEn
}
// Send "run-run" telemetry event
TelemetryManager.instance().send(TelemetryEventName.RUN_RUN, telemetryData);

if (executor.getId().equals(DefaultDebugExecutor.EXECUTOR_ID)) {
ProgressManager.getInstance().run(new Task.Backgroundable(getProject(), QUARKUS_CONFIGURATION, false) {
@Override
public void run(@NotNull ProgressIndicator indicator) {
createRemoteConfiguration(indicator);
}
});
}
return state;
}

private void waitForPortAvailable(int port, ProgressIndicator monitor) throws IOException {
long start = System.currentTimeMillis();
while (System.currentTimeMillis() - start < 60_000 && !monitor.isCanceled()) {
try (Socket socket = new Socket("localhost", port)) {
socket.getOutputStream().write(JWDP_HANDSHAKE.getBytes(StandardCharsets.US_ASCII));
return;
} catch (ConnectException e) {
try {
Thread.sleep(1000L);
} catch (InterruptedException e1) {
throw new IOException(e1);
}
}
}
throw new IOException("Can't connect remote debuger to port " + port);
}

private void createRemoteConfiguration(ProgressIndicator indicator) {
indicator.setText("Connecting Java debugger to port " + getPort());
try {
waitForPortAvailable(getPort(), indicator);
RunnerAndConfigurationSettings settings = RunManager.getInstance(getProject()).createConfiguration(getName() + " (Remote)", RemoteConfigurationType.class);
RemoteConfiguration remoteConfiguration = (RemoteConfiguration) settings.getConfiguration();
remoteConfiguration.PORT = Integer.toString(getPort());
long groupId = ExecutionEnvironment.getNextUnusedExecutionId();
ExecutionUtil.runConfiguration(settings, DefaultDebugExecutor.getDebugExecutorInstance(), DefaultExecutionTarget.INSTANCE, groupId);
} catch (IOException e) {
ApplicationManager.getApplication().invokeLater(() -> Messages.showErrorDialog("Can' t connector to port " + getPort(), "Quarkus"));
}
}

public String getProfile() {
return getOptions().getProfile();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
*******************************************************************************/
package com.redhat.devtools.intellij.quarkus.run;

import com.intellij.execution.ExecutionManager;
import com.intellij.execution.RunManager;
import com.intellij.execution.RunnerAndConfigurationSettings;
import com.intellij.execution.dashboard.RunDashboardManager;
Expand Down Expand Up @@ -56,6 +57,7 @@ public static QuarkusRunConfigurationManager getInstance(Project project) {
public QuarkusRunConfigurationManager(Project project) {
this.project = project;
connection = addProjectImportListener(project);
connection.subscribe(ExecutionManager.EXECUTION_TOPIC, new AttachDebuggerExecutionListener(project));
}

public @Nullable RunnerAndConfigurationSettings findExistingConfigurationFor(@NotNull Module module) {
Expand Down
1 change: 1 addition & 0 deletions src/main/resources/META-INF/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,7 @@
<projectService serviceImplementation="com.redhat.devtools.intellij.quarkus.QuarkusDeploymentSupport"/>

<!--Quarkus run config -->
<programRunner implementation="com.redhat.devtools.intellij.quarkus.run.QuarkusExternalSystemTaskDebugRunner"/>
<projectService
serviceImplementation="com.redhat.devtools.intellij.quarkus.run.QuarkusRunConfigurationManager"/>
<configurationType implementation="com.redhat.devtools.intellij.quarkus.run.QuarkusRunConfigurationType"/>
Expand Down

0 comments on commit cd5bc58

Please sign in to comment.