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 <[email protected]>
  • Loading branch information
angelozerr committed Sep 16, 2024
1 parent e246f7f commit 77e8d8e
Show file tree
Hide file tree
Showing 12 changed files with 557 additions and 143 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
******************************************************************************/
package com.redhat.devtools.intellij.quarkus.buildtool;

import com.intellij.execution.Executor;
import com.intellij.execution.RunnerAndConfigurationSettings;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.openapi.module.Module;
Expand All @@ -21,13 +22,10 @@
import com.intellij.util.messages.MessageBusConnection;
import com.redhat.devtools.intellij.quarkus.run.QuarkusRunConfiguration;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.maven.model.MavenId;

import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
Expand Down Expand Up @@ -64,18 +62,20 @@ static String getDeploymentJarId(File file) {
if (quarkusFile.exists()) {
try (Reader r = new FileReader(quarkusFile)) {
result = getQuarkusExtension(r);
} catch (IOException e) {}
} catch (IOException e) {
}
}
} else {
try {
JarFile jarFile = new JarFile(file);
JarEntry entry = jarFile.getJarEntry(QUARKUS_EXTENSION_PROPERTIES);
if (entry != null) {
try (Reader r = new InputStreamReader(jarFile.getInputStream(entry),"UTF-8")) {
try (Reader r = new InputStreamReader(jarFile.getInputStream(entry), "UTF-8")) {
result = getQuarkusExtension(r);
}
}
} catch (IOException e) {}
} catch (IOException e) {
}
}
return result;
}
Expand All @@ -91,7 +91,7 @@ static String getQuarkusExtension(Reader r) throws IOException {
}

public static BuildToolDelegate getDelegate(Module module) {
for(BuildToolDelegate toolDelegate : getDelegates()) {
for (BuildToolDelegate toolDelegate : getDelegates()) {
if (toolDelegate.isValid(module)) {
return toolDelegate;
}
Expand Down Expand Up @@ -160,7 +160,7 @@ default VirtualFile getJarFile(String path) {

default VirtualFile getJarFile(File file) {
VirtualFile virtualFile = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(file);
return virtualFile != null? JarFileSystem.getInstance().getJarRootForLocalFile(virtualFile):null;
return virtualFile != null ? JarFileSystem.getInstance().getJarRootForLocalFile(virtualFile) : null;
}

static final ExtensionPointName<BuildToolDelegate> EP_NAME = ExtensionPointName.create("com.redhat.devtools.intellij.quarkus.toolDelegate");
Expand All @@ -175,18 +175,40 @@ static List<VirtualFile>[] initDeploymentFiles() {

public static BuildToolDelegate[] getDelegates() {
BuildToolDelegate[] delegates = EP_NAME.getExtensions();
Arrays.sort(delegates, (a,b) -> a.getOrder() - b.getOrder());
Arrays.sort(delegates, (a, b) -> a.getOrder() - b.getOrder());
return delegates;
}

RunnerAndConfigurationSettings getConfigurationDelegate(Module module, QuarkusRunConfiguration configuration);
/**
* Returns the configuration delegate (Gradle, Maven) according the given the module and quarkus configuration
* and null otherwise.
*
* @param module the module.
* @param configuration the quarkus configuration.
* @param debugPort the debug port tu use if Quarkus application must be debugged and null otherwise.
* @return the configuration delegate (Gradle,Maven).
*/
@Nullable
RunnerAndConfigurationSettings getConfigurationDelegate(@NotNull Module module,
@NotNull QuarkusRunConfiguration configuration,
@Nullable Integer debugPort);

/**
* Add project import listener.
*
* @param project the project.
* @param project the project.
* @param connection the project connection used to subscribe maven, gradle listener which tracks project import.
* @param listener the project import listener.
* @param listener the project import listener.
*/
void addProjectImportListener(@NotNull Project project, @NotNull MessageBusConnection connection, @NotNull ProjectImportListener listener);
void addProjectImportListener(@NotNull Project project, @NotNull MessageBusConnection connection, @NotNull ProjectImportListener listener);

/**
* Returns the override executor and null otherwise.
*
* @return the override executor and null otherwise.
*/
@Nullable
default Executor getOverridedExecutor() {
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
******************************************************************************/
package com.redhat.devtools.intellij.quarkus.buildtool.gradle;

import com.intellij.execution.Executor;
import com.intellij.execution.ExecutorRegistry;
import com.intellij.execution.RunManager;
import com.intellij.execution.RunnerAndConfigurationSettings;
import com.intellij.ide.util.newProjectWizard.AddModuleWizard;
Expand Down Expand Up @@ -38,6 +40,7 @@
import com.intellij.openapi.roots.ui.configuration.ModulesProvider;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.wm.ToolWindowId;
import com.intellij.projectImport.ProjectImportBuilder;
import com.intellij.projectImport.ProjectImportProvider;
import com.intellij.util.concurrency.NonUrgentExecutor;
Expand Down Expand Up @@ -358,12 +361,14 @@ private ProjectImportProvider getGradleProjectImportProvider() {
}

@Override
public RunnerAndConfigurationSettings getConfigurationDelegate(Module module, QuarkusRunConfiguration configuration) {
public RunnerAndConfigurationSettings getConfigurationDelegate(@NotNull Module module,
@NotNull QuarkusRunConfiguration configuration,
@Nullable Integer debugPort) {
RunnerAndConfigurationSettings settings = RunManager.getInstance(module.getProject()).createConfiguration(module.getName() + " Quarkus (Gradle)", GradleExternalTaskConfigurationType.class);
GradleRunConfiguration gradleConfiguration = (GradleRunConfiguration) settings.getConfiguration();
gradleConfiguration.getSettings().getTaskNames().add("quarkusDev");
gradleConfiguration.getSettings().setEnv(configuration.getEnv());
String parameters = "-Ddebug=" + configuration.getPort();
String parameters = debugPort != null ? ("-Ddebug=" + Integer.toString(debugPort)) : "";
if (StringUtils.isNotBlank(configuration.getProfile())) {
parameters += " -Dquarkus.profile=" + configuration.getProfile();
}
Expand Down Expand Up @@ -407,4 +412,12 @@ private static boolean isValidGradleModule(Module module) {
String name = module.getName();
return !(name.endsWith(".integrationTest") || name.endsWith(".native-test") || name.endsWith(".test"));
}

@Override
public @Nullable Executor getOverridedExecutor() {
// The run and debug gradle must be started with the DefaultRunExecutor
// and not with the DefaultDebugExecutor in debug case otherwise
// stop action doesn't kill the Quarkus application process.
return ExecutorRegistry.getInstance().getExecutorById(ToolWindowId.RUN);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// Copyright 2000-2020 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 com.redhat.devtools.intellij.quarkus.buildtool.gradle;

import com.intellij.build.BuildView;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.ExecutionManager;
import com.intellij.execution.ExecutionResult;
import com.intellij.execution.configurations.RunProfile;
import com.intellij.execution.configurations.RunProfileState;
import com.intellij.execution.configurations.RunnerSettings;
import com.intellij.execution.runners.ExecutionEnvironment;
import com.intellij.execution.runners.ProgramRunner;
import com.intellij.execution.runners.RunContentBuilder;
import com.intellij.execution.testframework.HistoryTestRunnableState;
import com.intellij.execution.ui.RunContentDescriptor;
import com.intellij.openapi.externalSystem.service.execution.ExternalSystemRunnableState;
import com.redhat.devtools.intellij.quarkus.buildtool.BuildToolDelegate;
import com.redhat.devtools.intellij.quarkus.buildtool.maven.MavenToolDelegate;
import com.redhat.devtools.intellij.quarkus.run.QuarkusRunConfiguration;
import org.jetbrains.concurrency.Promises;

/**
* Program runner to run/debug a Gradle configuration.
* <p>
* This class is a copy/paste from the Intellij
* <a href="https://github.com/JetBrains/intellij-community/blob/master/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/execution/ExternalSystemTaskRunner.kt">ExternalSystemTaskRunner</a>
* since this class cannot be extended.
*/
public class GradleRunAndDebugProgramRunner implements ProgramRunner<RunnerSettings> {

private static final String RUNNER_ID = "GradleRunAndDebugProgramRunner";

@Override
public String getRunnerId() {
return RUNNER_ID;
}

@Override
public boolean canRun(String executorId, RunProfile profile) {
// For running / debugging Gradle 'quarkusDev', the program runner must be executed
// with the standard IJ 'ExternalSystemTaskRunner' which works only if
// the profile is an 'ExternalSystemRunConfiguration'. As QuarkusRunConfiguration
// wraps the profile, this condition is not matched.
// GradleRunAndDebugProgramRunner should extend ExternalSystemTaskRunner but as this class
// is final, GradleRunAndDebugProgramRunner is a copy/paste of ExternalSystemTaskRunner
// and the profile check is done by checking if QuarkusRunConfiguration wraps a Gradle run/debug configuration.
if (profile instanceof QuarkusRunConfiguration quarkusRunConfiguration) {
// returns true if the profile is a QuarkusRunConfiguration which wraps a Gradle configuration
BuildToolDelegate delegate = BuildToolDelegate.getDelegate(quarkusRunConfiguration.getModule());
return !(delegate instanceof MavenToolDelegate);
}
return false;
}

@Override
public void execute(ExecutionEnvironment environment) throws ExecutionException {
RunProfileState state = environment.getState();
if (state == null) {
return;
}
ExecutionManager.getInstance(environment.getProject()).startRunProfile(environment, () -> {
try {
return Promises.resolvedPromise(doExecute(state, environment));
} catch (ExecutionException e) {
throw new RuntimeException(e);
}
});
}

private RunContentDescriptor doExecute(RunProfileState state, ExecutionEnvironment environment) throws ExecutionException {
if (!(state instanceof ExternalSystemRunnableState) && !(state instanceof HistoryTestRunnableState)) {
return null;
}

RunContentDescriptor runContentDescriptor;
ExecutionResult executionResult = state.execute(environment.getExecutor(), this);
if (executionResult == null) {
return null;
}
runContentDescriptor = new RunContentBuilder(executionResult, environment).showRunContent(environment.getContentToReuse());
if (runContentDescriptor == null) {
return null;
}

if (state instanceof HistoryTestRunnableState) {
return runContentDescriptor;
}

((ExternalSystemRunnableState) state).setContentDescriptor(runContentDescriptor);

if (executionResult.getExecutionConsole() instanceof BuildView) {
return runContentDescriptor;
}

RunContentDescriptor descriptor = new RunContentDescriptor(
runContentDescriptor.getExecutionConsole(),
runContentDescriptor.getProcessHandler(),
runContentDescriptor.getComponent(),
runContentDescriptor.getDisplayName(),
runContentDescriptor.getIcon(),
null,
runContentDescriptor.getRestartActions()
) {
@Override
public boolean isHiddenContent() {
return true;
}
};
descriptor.setRunnerLayoutUi(runContentDescriptor.getRunnerLayoutUi());
return descriptor;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import com.redhat.devtools.intellij.quarkus.run.QuarkusRunConfiguration;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.idea.maven.execution.MavenRunConfiguration;
import org.jetbrains.idea.maven.execution.MavenRunConfigurationType;
import org.jetbrains.idea.maven.execution.MavenRunnerSettings;
Expand All @@ -39,7 +40,6 @@
import org.jetbrains.idea.maven.project.MavenProject;
import org.jetbrains.idea.maven.project.MavenProjectsManager;
import org.jetbrains.idea.maven.server.MavenEmbedderWrapper;
import org.jetbrains.idea.maven.utils.MavenProcessCanceledException;
import org.jetbrains.idea.maven.utils.MavenUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -260,7 +260,7 @@ private MavenEmbedderWrapper createEmbedderWrapper(Project project, String worki
}

@Override
public RunnerAndConfigurationSettings getConfigurationDelegate(Module module, QuarkusRunConfiguration configuration) {
public RunnerAndConfigurationSettings getConfigurationDelegate(Module module, QuarkusRunConfiguration configuration, @Nullable Integer debugPort) {
RunnerAndConfigurationSettings settings = RunManager.getInstance(module.getProject()).createConfiguration(module.getName() + " Quarkus (Maven)", MavenRunConfigurationType.class);
MavenRunConfiguration mavenConfiguration = (MavenRunConfiguration) settings.getConfiguration();
mavenConfiguration.getRunnerParameters().setResolveToWorkspace(true);
Expand All @@ -271,7 +271,9 @@ public RunnerAndConfigurationSettings getConfigurationDelegate(Module module, Qu
if (StringUtils.isNotBlank(configuration.getProfile())) {
mavenConfiguration.getRunnerSettings().getMavenProperties().put("quarkus.profile", configuration.getProfile());
}
mavenConfiguration.getRunnerSettings().getMavenProperties().put("debug", Integer.toString(configuration.getPort()));
if (debugPort != null) {
mavenConfiguration.getRunnerSettings().getMavenProperties().put("debug", Integer.toString(debugPort));
}
mavenConfiguration.setBeforeRunTasks(configuration.getBeforeRunTasks());
return settings;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*******************************************************************************
* Copyright (c) 2024 Red Hat Inc. and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
* which is available at https://www.apache.org/licenses/LICENSE-2.0.
*
* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
*
* Contributors:
* Red Hat Inc. - initial API and implementation
*******************************************************************************/
package com.redhat.devtools.intellij.quarkus.buildtool.maven;

import com.intellij.debugger.impl.GenericDebuggerRunner;
import com.intellij.execution.configurations.RunProfile;
import com.intellij.execution.executors.DefaultDebugExecutor;
import com.redhat.devtools.intellij.quarkus.buildtool.BuildToolDelegate;
import com.redhat.devtools.intellij.quarkus.run.QuarkusRunConfiguration;
import org.jetbrains.annotations.NotNull;

/**
* Program runner to debug a Maven configuration.
*/
public class QuarkusMavenDebugProgramRunner extends GenericDebuggerRunner {

private static final String RUNNER_ID = "QuarkusMavenDebugProgramRunner";

@Override
public String getRunnerId() {
return RUNNER_ID;
}

@Override
public boolean canRun(@NotNull final String executorId, @NotNull final RunProfile profile) {
if (!executorId.equals(DefaultDebugExecutor.EXECUTOR_ID)) {
return false;
}
// Debuging...
if (profile instanceof QuarkusRunConfiguration quarkusRunConfiguration) {
// returns true if the profile is a QuarkusRunConfiguration which wraps a Maven configuration
BuildToolDelegate delegate = BuildToolDelegate.getDelegate(quarkusRunConfiguration.getModule());
return (delegate instanceof MavenToolDelegate);
}
return false;
}
}
Loading

0 comments on commit 77e8d8e

Please sign in to comment.