From 540f411d44f0d57f5a4781c1d6c49cc4bdbdc5fe Mon Sep 17 00:00:00 2001 From: Kathryn Kodama Date: Tue, 22 Nov 2022 17:07:29 -0500 Subject: [PATCH] Liberty run and debug configuration Signed-off-by: Kathryn Kodama --- .../tools/intellij/LibertyExplorer.java | 15 +- .../tools/intellij/LibertyModule.java | 48 ++- .../tools/intellij/LibertyModuleNode.java | 2 +- .../tools/intellij/LibertyModules.java | 4 + .../actions/LibertyDevCustomStartAction.java | 114 +++++--- .../actions/LibertyDevRunTestsAction.java | 19 +- .../actions/LibertyDevStartAction.java | 43 ++- .../LibertyDevStartContainerAction.java | 16 +- .../actions/LibertyDevStopAction.java | 18 +- .../actions/LibertyGeneralAction.java | 60 ++-- .../LibertyRunConfiguration.java | 144 +++++++++ .../LibertyRunConfigurationFactory.java | 42 +++ .../LibertyRunConfigurationOptions.java | 50 ++++ .../LibertyRunConfigurationType.java | 56 ++++ .../LibertyRunManagerListener.java | 53 ++++ .../LibertyRunSettingsEditor.form | 43 +++ .../LibertyRunSettingsEditor.java | 81 +++++ .../tools/intellij/util/Constants.java | 22 +- .../tools/intellij/util/DebugModeHandler.java | 276 ++++++++++++++++++ .../intellij/util/LibertyActionUtil.java | 4 +- .../intellij/util/LibertyGradleUtil.java | 2 +- .../intellij/util/LibertyProjectUtil.java | 2 +- src/main/resources/META-INF/plugin.xml | 6 +- .../messages/LibertyBundles.properties | 22 +- 24 files changed, 984 insertions(+), 158 deletions(-) create mode 100644 src/main/java/io/openliberty/tools/intellij/runConfiguration/LibertyRunConfiguration.java create mode 100644 src/main/java/io/openliberty/tools/intellij/runConfiguration/LibertyRunConfigurationFactory.java create mode 100644 src/main/java/io/openliberty/tools/intellij/runConfiguration/LibertyRunConfigurationOptions.java create mode 100644 src/main/java/io/openliberty/tools/intellij/runConfiguration/LibertyRunConfigurationType.java create mode 100644 src/main/java/io/openliberty/tools/intellij/runConfiguration/LibertyRunManagerListener.java create mode 100644 src/main/java/io/openliberty/tools/intellij/runConfiguration/LibertyRunSettingsEditor.form create mode 100644 src/main/java/io/openliberty/tools/intellij/runConfiguration/LibertyRunSettingsEditor.java create mode 100644 src/main/java/io/openliberty/tools/intellij/util/DebugModeHandler.java diff --git a/src/main/java/io/openliberty/tools/intellij/LibertyExplorer.java b/src/main/java/io/openliberty/tools/intellij/LibertyExplorer.java index f6632676f..52452d35a 100644 --- a/src/main/java/io/openliberty/tools/intellij/LibertyExplorer.java +++ b/src/main/java/io/openliberty/tools/intellij/LibertyExplorer.java @@ -94,8 +94,8 @@ public static Tree buildTree(Project project, Color backgroundColor) { mavenBuildFiles = LibertyProjectUtil.getMavenBuildFiles(project); gradleBuildFiles = LibertyProjectUtil.getGradleBuildFiles(project); } catch (IOException | SAXException | ParserConfigurationException e) { - LOGGER.error("Could not find Open Liberty Maven or Gradle projects in workspace", - e.getMessage()); + LOGGER.warn("Could not find Liberty Maven or Gradle projects in workspace", + e); return null; } @@ -109,13 +109,14 @@ public static Tree buildTree(Project project, Color backgroundColor) { String projectName = null; VirtualFile virtualFile = psiFile.getVirtualFile(); if (virtualFile == null) { - LOGGER.error("Could not resolve current Maven project"); + LOGGER.error(String.format("Could not resolve current Maven project %s", psiFile)); + break; } LibertyModuleNode node; try { projectName = LibertyMavenUtil.getProjectNameFromPom(virtualFile); } catch (Exception e) { - LOGGER.error("Could not resolve project name from pom.xml", e.getMessage()); + LOGGER.warn(String.format("Could not resolve project name from build file: %s", virtualFile), e); } if (projectName == null) { projectName = project.getName(); @@ -150,13 +151,14 @@ public static Tree buildTree(Project project, Color backgroundColor) { String projectName = null; VirtualFile virtualFile = psiFile.getVirtualFile(); if (virtualFile == null) { - LOGGER.error("Could not resolve current Gradle project"); + LOGGER.error(String.format("Could not resolve current Gradle project %s", buildFile)); + break; } LibertyModuleNode node; try { projectName = LibertyGradleUtil.getProjectName(virtualFile); } catch (Exception e) { - LOGGER.error("Could not resolve project name from settings.gradle", e.getMessage()); + LOGGER.warn(String.format("Could not resolve project name for project %s", virtualFile), e); } if (projectName == null) { projectName = project.getName(); @@ -312,7 +314,6 @@ private static void executeAction(Tree tree) { action = (LibertyGeneralAction) am.getAction(Constants.VIEW_GRADLE_TEST_REPORT_ACTION_ID); } if (action != null) { - // TODO set Liberty module for the corresponding action action.setLibertyModule(module); // calls action on double click action.actionPerformed(new AnActionEvent(null, DataManager.getInstance().getDataContext(), diff --git a/src/main/java/io/openliberty/tools/intellij/LibertyModule.java b/src/main/java/io/openliberty/tools/intellij/LibertyModule.java index 779c1892d..f18bc01de 100644 --- a/src/main/java/io/openliberty/tools/intellij/LibertyModule.java +++ b/src/main/java/io/openliberty/tools/intellij/LibertyModule.java @@ -25,8 +25,21 @@ public class LibertyModule { private String name; private boolean validContainerVersion; - public LibertyModule(Project project, VirtualFile buildFile, String name, String projectType, boolean validContainerVersion) { + private String customStartParams; + // FIXME not currently being used, need to enable runInContainer checkbox in LibertyRunConfiguration see https://github.com/OpenLiberty/liberty-tools-intellij/issues/160 + private boolean runInContainer; + + private boolean debugMode; + + public LibertyModule(Project project) { this.project = project; + this.customStartParams = ""; + this.runInContainer = false; + this.debugMode = false; + } + + public LibertyModule(Project project, VirtualFile buildFile, String name, String projectType, boolean validContainerVersion) { + this(project); this.buildFile = buildFile; this.name = name; this.projectType = projectType; @@ -34,7 +47,7 @@ public LibertyModule(Project project, VirtualFile buildFile, String name, String } public LibertyModule(Project project, BuildFile buildFile) { - this.project = project; + this(project); this.buildFile = buildFile.getBuildFile().getVirtualFile(); this.name = buildFile.getProjectName(); this.projectType = buildFile.getProjectType(); @@ -80,4 +93,35 @@ public Project getProject() { public void setProject(Project project) { this.project = project; } + + public String getCustomStartParams() { + if (customStartParams == null) { + clearCustomStartParams(); + } + return customStartParams; + } + + public void setCustomStartParams(String customStartParams) { + this.customStartParams = customStartParams; + } + + public void clearCustomStartParams() { + customStartParams = ""; + } + + public boolean runInContainer() { + return runInContainer; + } + + public void setRunInContainer(boolean runInContainer) { + this.runInContainer = runInContainer; + } + + public boolean isDebugMode() { + return debugMode; + } + + public void setDebugMode(boolean debugMode) { + this.debugMode = debugMode; + } } diff --git a/src/main/java/io/openliberty/tools/intellij/LibertyModuleNode.java b/src/main/java/io/openliberty/tools/intellij/LibertyModuleNode.java index f512de897..7a804e4de 100644 --- a/src/main/java/io/openliberty/tools/intellij/LibertyModuleNode.java +++ b/src/main/java/io/openliberty/tools/intellij/LibertyModuleNode.java @@ -14,7 +14,7 @@ import javax.swing.tree.DefaultMutableTreeNode; public class LibertyModuleNode extends DefaultMutableTreeNode { - private LibertyModule libertyModule; + private final LibertyModule libertyModule; public LibertyModuleNode(LibertyModule libertyModule) { super(libertyModule.getName()); diff --git a/src/main/java/io/openliberty/tools/intellij/LibertyModules.java b/src/main/java/io/openliberty/tools/intellij/LibertyModules.java index 342dc7cec..00f5974b0 100644 --- a/src/main/java/io/openliberty/tools/intellij/LibertyModules.java +++ b/src/main/java/io/openliberty/tools/intellij/LibertyModules.java @@ -40,6 +40,7 @@ public synchronized static LibertyModules getInstance() { /** * Add tracked Liberty project to workspace + * * @param module LibertyModule */ public void addLibertyModule(LibertyModule module) { @@ -48,6 +49,7 @@ public void addLibertyModule(LibertyModule module) { /** * Get a Liberty module associated with the corresponding build file + * * @param buildFile build file * @return LibertyModule */ @@ -69,6 +71,7 @@ public LibertyModule getLibertyProjectFromString(String buildFile) throws Malfor /** * Returns all build files associated with a Liberty project + * * @return List Liberty project build files */ public List getLibertyBuildFiles() { @@ -79,6 +82,7 @@ public List getLibertyBuildFiles() { /** * Returns all Liberty modules in the workspace + * * @return List Liberty project modules */ public List getLibertyModules() { diff --git a/src/main/java/io/openliberty/tools/intellij/actions/LibertyDevCustomStartAction.java b/src/main/java/io/openliberty/tools/intellij/actions/LibertyDevCustomStartAction.java index 222e9857f..336ebac36 100644 --- a/src/main/java/io/openliberty/tools/intellij/actions/LibertyDevCustomStartAction.java +++ b/src/main/java/io/openliberty/tools/intellij/actions/LibertyDevCustomStartAction.java @@ -9,15 +9,27 @@ *******************************************************************************/ package io.openliberty.tools.intellij.actions; -import com.intellij.openapi.ui.InputValidator; -import com.intellij.openapi.ui.Messages; -import io.openliberty.tools.intellij.LibertyPluginIcons; -import io.openliberty.tools.intellij.util.Constants; -import io.openliberty.tools.intellij.util.LibertyActionUtil; -import io.openliberty.tools.intellij.util.LibertyProjectUtil; +import com.intellij.execution.ExecutionManager; +import com.intellij.execution.RunManager; +import com.intellij.execution.RunnerAndConfigurationSettings; +import com.intellij.execution.executors.DefaultRunExecutor; +import com.intellij.execution.impl.RunManagerImpl; +import com.intellij.execution.runners.ExecutionEnvironmentBuilder; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.vfs.VfsUtil; +import com.intellij.openapi.vfs.VirtualFile; +import io.openliberty.tools.intellij.runConfiguration.LibertyRunConfiguration; +import io.openliberty.tools.intellij.runConfiguration.LibertyRunConfigurationType; import io.openliberty.tools.intellij.util.LocalizedResourceUtil; -import org.jetbrains.plugins.terminal.ShellTerminalWidget; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; + +/** + * Opens the Liberty run config view for the corresponding Liberty module. Creates a new Liberty run config if one does not exist. + */ public class LibertyDevCustomStartAction extends LibertyGeneralAction { public LibertyDevCustomStartAction() { @@ -26,52 +38,60 @@ public LibertyDevCustomStartAction() { @Override protected void executeLibertyAction() { - String msg; - String initialVal; - if (projectType.equals(Constants.LIBERTY_MAVEN_PROJECT)) { - msg = LocalizedResourceUtil.getMessage("start.liberty.dev.custom.params.message.maven"); - initialVal = ""; - } else { - msg = LocalizedResourceUtil.getMessage("start.liberty.dev.custom.params.message.gradle"); - initialVal = ""; - } + // open run config + RunManager runManager = RunManager.getInstance(project); + List libertySettings = runManager.getConfigurationSettingsList(LibertyRunConfigurationType.getInstance()); + List libertyModuleSettings = new ArrayList<>(); - InputValidator validator = new InputValidator() { - @Override - public boolean checkInput(String inputString) { - if (inputString != null && !inputString.startsWith("-")) { - return false; + libertySettings.forEach(setting -> { + // find all Liberty run configs associated with this build file + LibertyRunConfiguration runConfig = (LibertyRunConfiguration) setting.getConfiguration(); + try { + VirtualFile vBuildFile = VfsUtil.findFileByURL(new URL(runConfig.getBuildFile())); + if (vBuildFile.equals(libertyModule.getBuildFile())) { + libertyModuleSettings.add(setting); } - return true; + } catch (MalformedURLException e) { + String msg = LocalizedResourceUtil.getMessage("liberty.build.file.does.not.resolve", actionCmd, project.getName()); + notifyError(msg); + LOGGER.warn(String.format("Could not get Liberty run configuration. ", msg)); } - - @Override - public boolean canClose(String inputString) { - return true; + }); + RunnerAndConfigurationSettings selectedLibertyConfig; + if (libertyModuleSettings.isEmpty()) { + // create new run config + selectedLibertyConfig = createNewLibertyRunConfig(runManager); + } else { + // TODO possible enhancement: if 1+ run configs, prompt user to select the one they want + // 1+ run configs found for the given project + RunnerAndConfigurationSettings selectedConfig = runManager.getSelectedConfiguration(); + if (libertyModuleSettings.contains(selectedConfig)) { + // if the selected config is for the Liberty module, use that run config + selectedLibertyConfig = selectedConfig; + } else { + // pick first in list run config in list + selectedLibertyConfig = libertyModuleSettings.get(0); } - }; - - String customParams = Messages.showInputDialog(project, msg, - LocalizedResourceUtil.getMessage("liberty.dev.custom.params"), - LibertyPluginIcons.libertyIcon_40, initialVal, validator); - - String startCmd = null; - if (customParams == null) { - return; } - if (projectType.equals(Constants.LIBERTY_MAVEN_PROJECT)) { - startCmd = "mvn io.openliberty.tools:liberty-maven-plugin:dev " + customParams; - } else if (projectType.equals(Constants.LIBERTY_GRADLE_PROJECT)) { - startCmd = "gradle libertyDev " + customParams; + // opens run config dialog + selectedLibertyConfig.setEditBeforeRun(true); + ExecutionEnvironmentBuilder builder = ExecutionEnvironmentBuilder.createOrNull(DefaultRunExecutor.getRunExecutorInstance(), selectedLibertyConfig); + if (builder != null) { + ExecutionManager.getInstance(project).restartRunProfile(builder.build()); } + } - ShellTerminalWidget widget = LibertyProjectUtil.getTerminalWidget(project, projectName, true); - if (widget == null) { - LOGGER.debug("Unable to start Liberty dev mode with custom parameters, could not get or create terminal widget for " + projectName); - return; - } - String cdToProjectCmd = "cd \"" + buildFile.getParent().getCanonicalPath() + "\""; - LibertyActionUtil.executeCommand(widget, cdToProjectCmd); - LibertyActionUtil.executeCommand(widget, startCmd); + /** + * Creates a new run config for the libertyModule selected + * + * @param runManager + * @return RunnerAndConfigurationSettings newly created run config settings + */ + protected RunnerAndConfigurationSettings createNewLibertyRunConfig(RunManager runManager) { + RunnerAndConfigurationSettings runConfigSettings = runManager.createConfiguration(runManager.suggestUniqueName(libertyModule.getName(), LibertyRunConfigurationType.getInstance()), LibertyRunConfigurationType.class); + LibertyRunConfiguration libertyRunConfiguration = (LibertyRunConfiguration) runConfigSettings.getConfiguration(); + // pre-populate build file and name + libertyRunConfiguration.setBuildFile(String.valueOf(libertyModule.getBuildFile())); + return runConfigSettings; } } diff --git a/src/main/java/io/openliberty/tools/intellij/actions/LibertyDevRunTestsAction.java b/src/main/java/io/openliberty/tools/intellij/actions/LibertyDevRunTestsAction.java index 7fe09c4f8..45905f0f7 100644 --- a/src/main/java/io/openliberty/tools/intellij/actions/LibertyDevRunTestsAction.java +++ b/src/main/java/io/openliberty/tools/intellij/actions/LibertyDevRunTestsAction.java @@ -9,14 +9,7 @@ *******************************************************************************/ package io.openliberty.tools.intellij.actions; -import com.intellij.notification.Notification; -import com.intellij.notification.NotificationListener; -import com.intellij.notification.NotificationType; -import com.intellij.notification.Notifications; -import io.openliberty.tools.intellij.LibertyPluginIcons; -import io.openliberty.tools.intellij.util.Constants; import io.openliberty.tools.intellij.util.LibertyActionUtil; -import io.openliberty.tools.intellij.util.LibertyProjectUtil; import io.openliberty.tools.intellij.util.LocalizedResourceUtil; import org.jetbrains.plugins.terminal.ShellTerminalWidget; @@ -29,18 +22,8 @@ public LibertyDevRunTestsAction() { @Override protected void executeLibertyAction() { String runTestsCommand = " "; - ShellTerminalWidget widget = LibertyProjectUtil.getTerminalWidget(project, projectName, false); - + ShellTerminalWidget widget = getTerminalWidget(false); if (widget == null) { - Notification notif = new Notification(Constants.LIBERTY_DEV_DASHBOARD_ID - , LibertyPluginIcons.libertyIcon - , LocalizedResourceUtil.getMessage("liberty.dev.not.started.notification.title") - , "" - , LocalizedResourceUtil.getMessage("liberty.dev.not.started.notification.content", projectName) - , NotificationType.WARNING - , NotificationListener.URL_OPENING_LISTENER); - Notifications.Bus.notify(notif, project); - LOGGER.error("Cannot run tests, corresponding project terminal does not exist."); return; } LibertyActionUtil.executeCommand(widget, runTestsCommand); diff --git a/src/main/java/io/openliberty/tools/intellij/actions/LibertyDevStartAction.java b/src/main/java/io/openliberty/tools/intellij/actions/LibertyDevStartAction.java index 59f5a9658..bdab0c8f6 100644 --- a/src/main/java/io/openliberty/tools/intellij/actions/LibertyDevStartAction.java +++ b/src/main/java/io/openliberty/tools/intellij/actions/LibertyDevStartAction.java @@ -10,13 +10,18 @@ package io.openliberty.tools.intellij.actions; import io.openliberty.tools.intellij.util.Constants; +import io.openliberty.tools.intellij.util.DebugModeHandler; import io.openliberty.tools.intellij.util.LibertyActionUtil; -import io.openliberty.tools.intellij.util.LibertyProjectUtil; import io.openliberty.tools.intellij.util.LocalizedResourceUtil; import io.openliberty.tools.intellij.util.LibertyGradleUtil; import io.openliberty.tools.intellij.util.LibertyMavenUtil; import org.jetbrains.plugins.terminal.ShellTerminalWidget; +import java.io.IOException; + +/** + * Runs the dev mode start command on the corresponding Liberty module. + */ public class LibertyDevStartAction extends LibertyGeneralAction { public LibertyDevStartAction() { @@ -25,20 +30,42 @@ public LibertyDevStartAction() { @Override protected void executeLibertyAction() { - ShellTerminalWidget widget = LibertyProjectUtil.getTerminalWidget(project, projectName, true); String startCmd = null; - - if (projectType.equals(Constants.LIBERTY_MAVEN_PROJECT)) { - startCmd = LibertyMavenUtil.getMavenSettingsCmd(project) + " io.openliberty.tools:liberty-maven-plugin:dev"; - } else if (projectType.equals(Constants.LIBERTY_GRADLE_PROJECT)) { - startCmd = LibertyGradleUtil.getGradleSettingsCmd(project) + " libertyDev"; + int debugPort = -1; + DebugModeHandler debugHandler = new DebugModeHandler(); + String start = projectType.equals(Constants.LIBERTY_MAVEN_PROJECT) ? LibertyMavenUtil.getMavenSettingsCmd(project) + Constants.LIBERTY_MAVEN_START_CMD : LibertyGradleUtil.getGradleSettingsCmd(project) + Constants.LIBERTY_GRADLE_START_CMD; + String startInContainer = projectType.equals(Constants.LIBERTY_MAVEN_PROJECT) ? LibertyMavenUtil.getMavenSettingsCmd(project) + Constants.LIBERTY_MAVEN_START_CONTAINER_CMD : LibertyGradleUtil.getGradleSettingsCmd(project) + Constants.LIBERTY_GRADLE_START_CONTAINER_CMD; + startCmd = libertyModule.runInContainer() ? startInContainer : start; + startCmd += libertyModule.getCustomStartParams(); + if (libertyModule.isDebugMode()) { + try { + String debugParam = projectType.equals(Constants.LIBERTY_MAVEN_PROJECT) ? Constants.LIBERTY_MAVEN_DEBUG_PARAM : Constants.LIBERTY_GRADLE_DEBUG_PARAM; + debugPort = debugHandler.getDebugPort(libertyModule); + String debugStr = debugParam + debugPort; + // do not append if debug port is already specified as part of start command + if (!startCmd.contains(debugStr)) { + startCmd += " " + debugParam + debugPort; + } + } catch (IOException e) { + String msg = LocalizedResourceUtil.getMessage("liberty.debug.port.unresolved", actionCmd, projectName); + notifyError(msg); + LOGGER.error(msg); + } } + + ShellTerminalWidget widget = getTerminalWidget(true); if (widget == null) { - LOGGER.debug("Unable to start Liberty dev mode, could not get or create terminal widget for " + projectName); return; } + String cdToProjectCmd = "cd \"" + buildFile.getParent().getCanonicalPath() + "\""; LibertyActionUtil.executeCommand(widget, cdToProjectCmd); LibertyActionUtil.executeCommand(widget, startCmd); + if (libertyModule.isDebugMode() && debugPort != -1) { + // Create remote configuration to attach debugger + debugHandler.createDebugConfiguration(libertyModule, debugPort); + libertyModule.setDebugMode(false); + } } + } \ No newline at end of file diff --git a/src/main/java/io/openliberty/tools/intellij/actions/LibertyDevStartContainerAction.java b/src/main/java/io/openliberty/tools/intellij/actions/LibertyDevStartContainerAction.java index 4a295fc38..dd1a7a0c9 100644 --- a/src/main/java/io/openliberty/tools/intellij/actions/LibertyDevStartContainerAction.java +++ b/src/main/java/io/openliberty/tools/intellij/actions/LibertyDevStartContainerAction.java @@ -9,10 +9,7 @@ *******************************************************************************/ package io.openliberty.tools.intellij.actions; -import io.openliberty.tools.intellij.util.Constants; -import io.openliberty.tools.intellij.util.LibertyActionUtil; -import io.openliberty.tools.intellij.util.LibertyProjectUtil; -import io.openliberty.tools.intellij.util.LocalizedResourceUtil; +import io.openliberty.tools.intellij.util.*; import org.jetbrains.plugins.terminal.ShellTerminalWidget; public class LibertyDevStartContainerAction extends LibertyGeneralAction { @@ -23,16 +20,9 @@ public LibertyDevStartContainerAction() { @Override protected void executeLibertyAction() { - String startCmd = null; - if (projectType.equals(Constants.LIBERTY_MAVEN_PROJECT)) { - startCmd = "mvn io.openliberty.tools:liberty-maven-plugin:devc"; - } else if (projectType.equals(Constants.LIBERTY_GRADLE_PROJECT)) { - startCmd = "gradle libertyDevc"; - } - - ShellTerminalWidget widget = LibertyProjectUtil.getTerminalWidget(project, projectName, true); + String startCmd = projectType.equals(Constants.LIBERTY_MAVEN_PROJECT) ? LibertyMavenUtil.getMavenSettingsCmd(project) + Constants.LIBERTY_MAVEN_START_CONTAINER_CMD : LibertyGradleUtil.getGradleSettingsCmd(project) + Constants.LIBERTY_GRADLE_START_CONTAINER_CMD; + ShellTerminalWidget widget = getTerminalWidget(true); if (widget == null) { - LOGGER.debug("Unable to start Liberty dev mode in a container, could not get or create terminal widget for " + projectName); return; } String cdToProjectCmd = "cd \"" + buildFile.getParent().getCanonicalPath() + "\""; diff --git a/src/main/java/io/openliberty/tools/intellij/actions/LibertyDevStopAction.java b/src/main/java/io/openliberty/tools/intellij/actions/LibertyDevStopAction.java index 6f3760c0a..0727374fd 100644 --- a/src/main/java/io/openliberty/tools/intellij/actions/LibertyDevStopAction.java +++ b/src/main/java/io/openliberty/tools/intellij/actions/LibertyDevStopAction.java @@ -9,14 +9,7 @@ *******************************************************************************/ package io.openliberty.tools.intellij.actions; -import com.intellij.notification.Notification; -import com.intellij.notification.NotificationListener; -import com.intellij.notification.NotificationType; -import com.intellij.notification.Notifications; -import io.openliberty.tools.intellij.LibertyPluginIcons; -import io.openliberty.tools.intellij.util.Constants; import io.openliberty.tools.intellij.util.LibertyActionUtil; -import io.openliberty.tools.intellij.util.LibertyProjectUtil; import io.openliberty.tools.intellij.util.LocalizedResourceUtil; import org.jetbrains.plugins.terminal.ShellTerminalWidget; @@ -28,18 +21,9 @@ public LibertyDevStopAction() { @Override protected void executeLibertyAction() { - ShellTerminalWidget widget = LibertyProjectUtil.getTerminalWidget(project, projectName, false); + ShellTerminalWidget widget = getTerminalWidget(false); String stopCmd = "q"; if (widget == null) { - Notification notif = new Notification(Constants.LIBERTY_DEV_DASHBOARD_ID - , LibertyPluginIcons.libertyIcon - , LocalizedResourceUtil.getMessage("liberty.dev.not.started.notification.title") - , "" - , LocalizedResourceUtil.getMessage("liberty.dev.not.started.notification.content", projectName) - , NotificationType.WARNING - , NotificationListener.URL_OPENING_LISTENER); - Notifications.Bus.notify(notif, project); - LOGGER.error("Cannot stop Liberty dev mode, corresponding project terminal does not exist."); return; } LibertyActionUtil.executeCommand(widget, stopCmd); diff --git a/src/main/java/io/openliberty/tools/intellij/actions/LibertyGeneralAction.java b/src/main/java/io/openliberty/tools/intellij/actions/LibertyGeneralAction.java index 93cbc4b10..f4623da1a 100644 --- a/src/main/java/io/openliberty/tools/intellij/actions/LibertyGeneralAction.java +++ b/src/main/java/io/openliberty/tools/intellij/actions/LibertyGeneralAction.java @@ -26,6 +26,7 @@ import io.openliberty.tools.intellij.util.LibertyProjectUtil; import io.openliberty.tools.intellij.util.LocalizedResourceUtil; import org.jetbrains.annotations.NotNull; +import org.jetbrains.plugins.terminal.ShellTerminalWidget; import java.util.Arrays; import java.util.List; @@ -42,6 +43,7 @@ public class LibertyGeneralAction extends AnAction { /** * Set the LibertyModule that the action should run on + * * @param libertyModule */ public void setLibertyModule(LibertyModule libertyModule) { @@ -61,7 +63,7 @@ public void actionPerformed(@NotNull AnActionEvent e) { // TODO prompt user to select project String msg = LocalizedResourceUtil.getMessage("liberty.project.does.not.resolve", actionCmd); notifyError(msg); - LOGGER.debug(msg); + LOGGER.warn(msg); return; } } @@ -69,8 +71,7 @@ public void actionPerformed(@NotNull AnActionEvent e) { buildFile = (VirtualFile) e.getDataContext().getData(Constants.LIBERTY_BUILD_FILE); // if still null, prompt for user to select if (buildFile == null) { - List libertyModules = LibertyModules.getInstance() - .getLibertyModules(getSupportedProjectTypes()); + List libertyModules = LibertyModules.getInstance().getLibertyModules(getSupportedProjectTypes()); if (!libertyModules.isEmpty()) { // Only one project. Select it. if (libertyModules.size() == 1) { @@ -79,12 +80,7 @@ public void actionPerformed(@NotNull AnActionEvent e) { // Multiple projects. Pop up dialog for user to select. else { final String[] projectNames = toProjectNames(libertyModules); - final int ret = Messages.showChooseDialog(project, - LocalizedResourceUtil.getMessage("liberty.project.file.selection.dialog.message", actionCmd), - LocalizedResourceUtil.getMessage("liberty.project.file.selection.dialog.title"), - LibertyPluginIcons.libertyIcon_40, - projectNames, - projectNames[0]); + final int ret = Messages.showChooseDialog(project, LocalizedResourceUtil.getMessage("liberty.project.file.selection.dialog.message", actionCmd), LocalizedResourceUtil.getMessage("liberty.project.file.selection.dialog.title"), LibertyPluginIcons.libertyIcon_40, projectNames, projectNames[0]); if (ret >= 0 && ret < libertyModules.size()) { setLibertyModule(libertyModules.get(ret)); } @@ -98,7 +94,7 @@ public void actionPerformed(@NotNull AnActionEvent e) { if (buildFile == null) { String msg = LocalizedResourceUtil.getMessage("liberty.build.file.does.not.resolve", actionCmd, project.getName()); notifyError(msg); - LOGGER.debug(msg); + LOGGER.warn(msg); return; } } @@ -109,13 +105,18 @@ public void actionPerformed(@NotNull AnActionEvent e) { if (projectType == null) { projectType = (String) e.getDataContext().getData(Constants.LIBERTY_PROJECT_TYPE); } + if (projectType == null || (!projectType.equals(Constants.LIBERTY_MAVEN_PROJECT) && !projectType.equals(Constants.LIBERTY_GRADLE_PROJECT))) { + String msg = LocalizedResourceUtil.getMessage("liberty.project.type.invalid", actionCmd, projectName); + notifyError(msg); + LOGGER.warn(msg); + return; + } executeLibertyAction(); } /* Returns project type(s) applicable to this action. */ protected List getSupportedProjectTypes() { - return Arrays.asList( - Constants.LIBERTY_MAVEN_PROJECT, Constants.LIBERTY_GRADLE_PROJECT); + return Arrays.asList(Constants.LIBERTY_MAVEN_PROJECT, Constants.LIBERTY_GRADLE_PROJECT); } @@ -136,14 +137,35 @@ protected void executeLibertyAction() { // must be implemented by individual actions } + /** + * Displays error message dialog to user + * + * @param errMsg + */ protected void notifyError(String errMsg) { - Notification notif = new Notification(Constants.LIBERTY_DEV_DASHBOARD_ID, - LibertyPluginIcons.libertyIcon, - LocalizedResourceUtil.getMessage("liberty.action.cannot.start"), - "", - errMsg, - NotificationType.WARNING, - NotificationListener.URL_OPENING_LISTENER); + Notification notif = new Notification(Constants.LIBERTY_DEV_DASHBOARD_ID, LibertyPluginIcons.libertyIcon, LocalizedResourceUtil.getMessage("liberty.action.cannot.start"), "", errMsg, NotificationType.WARNING, NotificationListener.URL_OPENING_LISTENER); Notifications.Bus.notify(notif, project); } + + /** + * Returns the IntelliJ terminal widget for the corresponding Liberty module + * + * @param createWidget create terminal widget if it does not already exist + * @return ShellTerminalWidget + */ + protected ShellTerminalWidget getTerminalWidget(boolean createWidget) { + ShellTerminalWidget widget = LibertyProjectUtil.getTerminalWidget(project, projectName, createWidget); + if (widget == null) { + String msg; + if (createWidget) { + msg = LocalizedResourceUtil.getMessage("liberty.terminal.cannot.resolve", actionCmd, projectName); + } else { + msg = LocalizedResourceUtil.getMessage("liberty.dev.not.started.notification.content", actionCmd, projectName); + } + notifyError(msg); + LOGGER.warn(msg); + return null; + } + return widget; + } } diff --git a/src/main/java/io/openliberty/tools/intellij/runConfiguration/LibertyRunConfiguration.java b/src/main/java/io/openliberty/tools/intellij/runConfiguration/LibertyRunConfiguration.java new file mode 100644 index 000000000..100287444 --- /dev/null +++ b/src/main/java/io/openliberty/tools/intellij/runConfiguration/LibertyRunConfiguration.java @@ -0,0 +1,144 @@ +/******************************************************************************* + * Copyright (c) 2022 IBM Corporation. + * + * 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. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package io.openliberty.tools.intellij.runConfiguration; + +import com.intellij.execution.ExecutionException; +import com.intellij.execution.Executor; +import com.intellij.execution.configurations.*; +import com.intellij.execution.executors.DefaultDebugExecutor; +import com.intellij.execution.runners.ExecutionEnvironment; +import com.intellij.ide.DataManager; +import com.intellij.openapi.actionSystem.*; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.options.SettingsEditor; +import com.intellij.openapi.project.Project; +import io.openliberty.tools.intellij.LibertyModule; +import io.openliberty.tools.intellij.LibertyModules; +import io.openliberty.tools.intellij.actions.LibertyDevStartAction; +import io.openliberty.tools.intellij.util.Constants; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.net.MalformedURLException; +import java.util.Collection; + +/** + * Defines a Liberty run & debug configuration. Each configuration is tied to a Liberty module + */ +public class LibertyRunConfiguration extends ModuleBasedConfiguration { + protected static Logger LOGGER = Logger.getInstance(LibertyRunConfiguration.class); + + private final LibertyModules libertyModules; + private LibertyModule libertyModule; + + public LibertyRunConfiguration(Project project, ConfigurationFactory factory, String name) { + super(name, getRunConfigurationModule(project), factory); + this.libertyModules = LibertyModules.getInstance(); + } + + @NotNull + private static RunConfigurationModule getRunConfigurationModule(Project project) { + RunConfigurationModule module = new RunConfigurationModule(project); + // TODO set module to first valid Liberty module? + module.setModuleToAnyFirstIfNotSpecified(); + return module; + } + + public Module getModule() { + return getConfigurationModule().getModule(); + } + + public void setModule(Module module) { + getConfigurationModule().setModule(module); + } + + public String getParams() { + return getOptions().getParams(); + } + + public void setParams(String params) { + getOptions().setParams(params); + } + + public String getBuildFile() { + return getOptions().getBuildFile(); + } + + public void setBuildFile(String buildFile) { + getOptions().setBuildFile(buildFile); + } + + // FIXME runInContainer, see https://github.com/OpenLiberty/liberty-tools-intellij/issues/160 + public Boolean runInContainer() { + return getOptions().runInContainer(); + } + + public void setRunInContainer(Boolean runInContainer) { + getOptions().setRunInContainer(runInContainer); + } + + @Override + public Collection getValidModules() { + // TODO return only valid Liberty modules? + return getAllModules(); + } + + @NotNull + @Override + protected LibertyRunConfigurationOptions getOptions() { + return (LibertyRunConfigurationOptions) super.getOptions(); + } + + @Override + public void checkConfiguration() throws RuntimeConfigurationException { + if (getModule() == null) { + throw new RuntimeConfigurationException("No module selected", "Liberty configuration"); + } + // TODO do we need additional checking here? + } + + @NotNull + @Override + public SettingsEditor getConfigurationEditor() { + return new LibertyRunSettingsEditor(getProject()); + } + + @Nullable + @Override + public RunProfileState getState(@NotNull Executor executor, @NotNull ExecutionEnvironment environment) throws ExecutionException { + try { + libertyModule = libertyModules.getLibertyProjectFromString(getBuildFile()); + } catch (MalformedURLException e) { + LOGGER.error(String.format("Could not resolve the Liberty module associated with build file: %s", getBuildFile())); + throw new ExecutionException(e); + } + + // run the start dev mode action + AnAction action = ActionManager.getInstance().getAction(Constants.LIBERTY_DEV_START_ACTION_ID); + LibertyDevStartAction libAction = (LibertyDevStartAction) action; + libAction.setLibertyModule(libertyModule); + // set custom start params + if (getParams() != null) { + libertyModule.setCustomStartParams(getParams()); + } else { + libertyModule.setCustomStartParams(""); + } + // FIXME implement runInContainer checkbox from run config see https://github.com/OpenLiberty/liberty-tools-intellij/issues/160 + // libertyModule.setRunInContainer(runInContainer()); + + if (executor.getId() == DefaultDebugExecutor.EXECUTOR_ID) { + libertyModule.setDebugMode(true); + } + action.actionPerformed(new AnActionEvent(null, DataManager.getInstance().getDataContext(null), ActionPlaces.UNKNOWN, new Presentation(), ActionManager.getInstance(), 0)); + return null; + } + +} \ No newline at end of file diff --git a/src/main/java/io/openliberty/tools/intellij/runConfiguration/LibertyRunConfigurationFactory.java b/src/main/java/io/openliberty/tools/intellij/runConfiguration/LibertyRunConfigurationFactory.java new file mode 100644 index 000000000..dbe0b63a8 --- /dev/null +++ b/src/main/java/io/openliberty/tools/intellij/runConfiguration/LibertyRunConfigurationFactory.java @@ -0,0 +1,42 @@ +/******************************************************************************* + * Copyright (c) 2022 IBM Corporation. + * + * 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. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package io.openliberty.tools.intellij.runConfiguration; + +import com.intellij.execution.configurations.ConfigurationFactory; +import com.intellij.execution.configurations.ConfigurationType; +import com.intellij.execution.configurations.RunConfiguration; +import com.intellij.openapi.components.BaseState; +import com.intellij.openapi.project.Project; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class LibertyRunConfigurationFactory extends ConfigurationFactory { + public LibertyRunConfigurationFactory(ConfigurationType type) { + super(type); + } + + @NotNull + @Override + public String getId() { + return getType().getId(); + } + + @NotNull + @Override + public RunConfiguration createTemplateConfiguration(@NotNull Project project) { + return new LibertyRunConfiguration(project, this, "Liberty"); + } + + @Nullable + @Override + public Class getOptionsClass() { + return LibertyRunConfigurationOptions.class; + } +} \ No newline at end of file diff --git a/src/main/java/io/openliberty/tools/intellij/runConfiguration/LibertyRunConfigurationOptions.java b/src/main/java/io/openliberty/tools/intellij/runConfiguration/LibertyRunConfigurationOptions.java new file mode 100644 index 000000000..7100b6e12 --- /dev/null +++ b/src/main/java/io/openliberty/tools/intellij/runConfiguration/LibertyRunConfigurationOptions.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2022 IBM Corporation. + * + * 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. + * + * SPDX-License-Identifier: EPL-2.0 + ******************************************************************************/ +package io.openliberty.tools.intellij.runConfiguration; + +import com.intellij.execution.configurations.ModuleBasedConfigurationOptions; +import com.intellij.openapi.components.StoredProperty; +import com.intellij.openapi.diagnostic.Logger; + +/** + * Defines options that can be configured for each Liberty run & debug configuration + */ +public class LibertyRunConfigurationOptions extends ModuleBasedConfigurationOptions { + protected static Logger LOGGER = Logger.getInstance(LibertyRunConfigurationOptions.class); + private final StoredProperty paramsProperty = string("").provideDelegate(this, "params"); + + private final StoredProperty buildFileProperty = string("").provideDelegate(this, "buildFile"); + + private final StoredProperty runInContainerProperty = property(false).provideDelegate(this, "runInContainer"); + + public String getParams() { + return paramsProperty.getValue(this); + } + + public void setParams(String params) { + paramsProperty.setValue(this, params); + } + + public String getBuildFile() { + return buildFileProperty.getValue(this); + } + + public void setBuildFile(String buildFile) { + buildFileProperty.setValue(this, buildFile); + } + + public Boolean runInContainer() { + return runInContainerProperty.getValue(this); + } + + public void setRunInContainer(Boolean runInContainer) { + runInContainerProperty.setValue(this, runInContainer); + } +} \ No newline at end of file diff --git a/src/main/java/io/openliberty/tools/intellij/runConfiguration/LibertyRunConfigurationType.java b/src/main/java/io/openliberty/tools/intellij/runConfiguration/LibertyRunConfigurationType.java new file mode 100644 index 000000000..31ba30012 --- /dev/null +++ b/src/main/java/io/openliberty/tools/intellij/runConfiguration/LibertyRunConfigurationType.java @@ -0,0 +1,56 @@ +/******************************************************************************* + * Copyright (c) 2022 IBM Corporation. + * + * 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. + * + * SPDX-License-Identifier: EPL-2.0 + ******************************************************************************/ +package io.openliberty.tools.intellij.runConfiguration; + +import com.intellij.execution.configurations.ConfigurationFactory; +import com.intellij.execution.configurations.ConfigurationType; +import com.intellij.execution.configurations.ConfigurationTypeUtil; +import io.openliberty.tools.intellij.LibertyPluginIcons; +import io.openliberty.tools.intellij.util.LocalizedResourceUtil; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; + +public class LibertyRunConfigurationType implements ConfigurationType { + public static final String ID = "Liberty"; + + public static LibertyRunConfigurationType getInstance() { + return ConfigurationTypeUtil.findConfigurationType(LibertyRunConfigurationType.class); + } + + @NotNull + @Override + public String getDisplayName() { + return "Liberty"; + } + + @Nls + @Override + public String getConfigurationTypeDescription() { + return LocalizedResourceUtil.getMessage("liberty.run.config.title"); + } + + @Override + public Icon getIcon() { + return LibertyPluginIcons.libertyIcon; + } + + @NotNull + @Override + public String getId() { + return ID; + } + + @Override + public ConfigurationFactory[] getConfigurationFactories() { + return new ConfigurationFactory[]{new LibertyRunConfigurationFactory(this)}; + } +} \ No newline at end of file diff --git a/src/main/java/io/openliberty/tools/intellij/runConfiguration/LibertyRunManagerListener.java b/src/main/java/io/openliberty/tools/intellij/runConfiguration/LibertyRunManagerListener.java new file mode 100644 index 000000000..9e120f7ed --- /dev/null +++ b/src/main/java/io/openliberty/tools/intellij/runConfiguration/LibertyRunManagerListener.java @@ -0,0 +1,53 @@ +/******************************************************************************* + * Copyright (c) 2022 IBM Corporation. + * + * 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. + * + * SPDX-License-Identifier: EPL-2.0 + ******************************************************************************/ +package io.openliberty.tools.intellij.runConfiguration; + +import com.intellij.execution.RunManagerListener; +import com.intellij.execution.RunnerAndConfigurationSettings; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.vfs.VfsUtil; +import com.intellij.openapi.vfs.VirtualFile; +import io.openliberty.tools.intellij.LibertyModule; +import io.openliberty.tools.intellij.LibertyModules; +import org.jetbrains.annotations.NotNull; + +import java.net.MalformedURLException; +import java.net.URL; + +/** + * Listens to creation/deletion of Liberty run configurations + */ +public class LibertyRunManagerListener implements RunManagerListener { + + protected static Logger LOGGER = Logger.getInstance(LibertyRunManagerListener.class); + + /** + * When a Liberty run configuration is removed, clear custom start parameters from Liberty module + * + * @param settings + */ + @Override + public void runConfigurationRemoved(@NotNull RunnerAndConfigurationSettings settings) { + if (settings.getConfiguration() instanceof LibertyRunConfiguration) { + LibertyRunConfiguration runConfig = (LibertyRunConfiguration) settings.getConfiguration(); + LibertyModules libertyModules = LibertyModules.getInstance(); + try { + VirtualFile vBuildFile = VfsUtil.findFileByURL(new URL(runConfig.getBuildFile())); + LibertyModule libertyModule = libertyModules.getLibertyModule(vBuildFile); + if (libertyModule != null && libertyModule.getCustomStartParams().equals(runConfig.getParams())) { + libertyModule.clearCustomStartParams(); + } + } catch (MalformedURLException e) { + LOGGER.warn(String.format("Unable to clear custom start parameters for Liberty project associated with Liberty run configuration associated with: %s. Could not resolve build file.", runConfig.getBuildFile()), e); + } + + } + } +} diff --git a/src/main/java/io/openliberty/tools/intellij/runConfiguration/LibertyRunSettingsEditor.form b/src/main/java/io/openliberty/tools/intellij/runConfiguration/LibertyRunSettingsEditor.form new file mode 100644 index 000000000..a2308a0fd --- /dev/null +++ b/src/main/java/io/openliberty/tools/intellij/runConfiguration/LibertyRunSettingsEditor.form @@ -0,0 +1,43 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/src/main/java/io/openliberty/tools/intellij/runConfiguration/LibertyRunSettingsEditor.java b/src/main/java/io/openliberty/tools/intellij/runConfiguration/LibertyRunSettingsEditor.java new file mode 100644 index 000000000..a5b4db9b3 --- /dev/null +++ b/src/main/java/io/openliberty/tools/intellij/runConfiguration/LibertyRunSettingsEditor.java @@ -0,0 +1,81 @@ +/******************************************************************************* + * Copyright (c) 2022 IBM Corporation. + * + * 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. + * + * SPDX-License-Identifier: EPL-2.0 + ******************************************************************************/ +package io.openliberty.tools.intellij.runConfiguration; + +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.options.ConfigurationException; +import com.intellij.openapi.options.SettingsEditor; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.ComboBox; +import com.intellij.openapi.ui.LabeledComponent; +import com.intellij.ui.EditorTextField; +import io.openliberty.tools.intellij.LibertyModules; +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; + +/** + * Editor associated with Liberty run & debug configurations. Defines when configuration changes are updated, default values, etc. + */ +public class LibertyRunSettingsEditor extends SettingsEditor { + + private JPanel root; + private LabeledComponent editableParams; + private LabeledComponent libertyModule; + + // FIXME runInContainer + // private LabeledComponent runInContainer; + + public LibertyRunSettingsEditor(Project project) { + libertyModule.getComponent().setModel(new DefaultComboBoxModel(LibertyModules.getInstance().getLibertyBuildFiles().toArray())); + } + + @Override + protected void resetEditorFrom(@NotNull LibertyRunConfiguration configuration) { + + String buildFile = configuration.getBuildFile(); + for (int i = 0; i < libertyModule.getComponent().getItemCount(); i++) { + String libertyModuleBuildFile = libertyModule.getComponent().getItemAt(i).toString(); + if (buildFile.equals(libertyModuleBuildFile)) { + // need to use setSelectedIndex instead of setSelectedItem, setSelectedItem(buildFile) results in no behaviour change + libertyModule.getComponent().setSelectedIndex(i); + break; + } + } + // FIXME runInContainer state is not being saved, cannot "Apply" run in container checkbox change to run config, see https://github.com/OpenLiberty/liberty-tools-intellij/issues/160 + // runInContainer.getComponent().setSelected(configuration.runInContainer()); + editableParams.getComponent().setText(configuration.getParams()); + } + + @Override + protected void applyEditorTo(@NotNull LibertyRunConfiguration configuration) throws ConfigurationException { + configuration.setParams(editableParams.getComponent().getText()); + configuration.setBuildFile(String.valueOf(libertyModule.getComponent().getSelectedItem())); + // FIXME runInContainer + // configuration.setRunInContainer(runInContainer.getComponent().isSelected()); + } + + @NotNull + @Override + protected JComponent createEditor() { + return root; + } + + public void createUIComponents() { + ComboBox comboBox = new ComboBox(); + libertyModule = new LabeledComponent<>(); + libertyModule.setComponent(comboBox); + editableParams = new LabeledComponent<>(); + editableParams.setComponent(new EditorTextField()); + // FIXME runInContainer + // runInContainer = new LabeledComponent<>(); + // runInContainer.setComponent(new StateRestoringCheckBox()); + } +} \ No newline at end of file diff --git a/src/main/java/io/openliberty/tools/intellij/util/Constants.java b/src/main/java/io/openliberty/tools/intellij/util/Constants.java index 481c3e13b..6df4a7829 100644 --- a/src/main/java/io/openliberty/tools/intellij/util/Constants.java +++ b/src/main/java/io/openliberty/tools/intellij/util/Constants.java @@ -16,6 +16,10 @@ public final class Constants { public static final String LIBERTY_GRADLE_PROJECT = "Liberty Gradle Project"; public static final String LIBERTY_MAVEN_PROJECT = "Liberty Maven Project"; + public static final String LIBERTY_MAVEN_START_CMD = " io.openliberty.tools:liberty-maven-plugin:dev "; + public static final String LIBERTY_MAVEN_START_CONTAINER_CMD = " io.openliberty.tools:liberty-maven-plugin:devc "; + public static final String LIBERTY_GRADLE_START_CMD = " libertyDev "; + public static final String LIBERTY_GRADLE_START_CONTAINER_CMD = " libertyDevc "; public static final String LIBERTY_DEV_START = LocalizedResourceUtil.getMessage("start.dev"); public static final String LIBERTY_DEV_CUSTOM_START = LocalizedResourceUtil.getMessage("start.dev.custom.params"); public static final String LIBERTY_DEV_START_CONTAINER = LocalizedResourceUtil.getMessage("start.dev.container"); @@ -26,11 +30,14 @@ public final class Constants { public static final String VIEW_INTEGRATION_TEST_REPORT = LocalizedResourceUtil.getMessage("action.io.openliberty.tools.intellij.actions.ViewIntegrationTestReport.text"); public static final String VIEW_UNIT_TEST_REPORT = LocalizedResourceUtil.getMessage("action.io.openliberty.tools.intellij.actions.ViewUnitTestReport.text"); public static final String LIBERTY_MAVEN_PLUGIN_CONTAINER_VERSION = "3.3-M1"; + public static final String LIBERTY_MAVEN_DEBUG_PARAM = "-DdebugPort="; // Gradle public static final String VIEW_GRADLE_TEST_REPORT = LocalizedResourceUtil.getMessage("action.io.openliberty.tools.intellij.actions.ViewTestReport.text"); public static final String TEST_REPORT_STRING = LocalizedResourceUtil.getMessage("test.summary"); public static final String LIBERTY_GRADLE_PLUGIN_CONTAINER_VERSION = "3.1-M1"; + public static final String LIBERTY_GRADLE_DEBUG_PARAM = "--libertyDebugPort="; + public static final String LIBERTY_TREE = "LibertyTree"; @@ -76,19 +83,4 @@ public static HashMap getFullActionMap() { fullActionsMap.put(VIEW_GRADLE_TEST_REPORT, VIEW_GRADLE_TEST_REPORT_ACTION_ID); return fullActionsMap; } - - public static HashMap getMavenMap() { - HashMap mavenActionsMap = new HashMap<>(); - mavenActionsMap.putAll(CORE_ACTIONS_MAP); - mavenActionsMap.put(VIEW_UNIT_TEST_REPORT, VIEW_UNIT_TEST_REPORT_ACTION_ID); - mavenActionsMap.put(VIEW_INTEGRATION_TEST_REPORT, VIEW_INTEGRATION_TEST_REPORT_ACTION_ID); - return mavenActionsMap; - } - - public static HashMap getGradleMap() { - HashMap gradleActionsMap = new HashMap<>(); - gradleActionsMap.putAll(CORE_ACTIONS_MAP); - gradleActionsMap.put(VIEW_GRADLE_TEST_REPORT, VIEW_GRADLE_TEST_REPORT_ACTION_ID); - return gradleActionsMap; - } } diff --git a/src/main/java/io/openliberty/tools/intellij/util/DebugModeHandler.java b/src/main/java/io/openliberty/tools/intellij/util/DebugModeHandler.java new file mode 100644 index 000000000..b4eb4d23d --- /dev/null +++ b/src/main/java/io/openliberty/tools/intellij/util/DebugModeHandler.java @@ -0,0 +1,276 @@ +/******************************************************************************* + * Copyright (c) 2022 IBM Corporation. + * + * 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. + * + * SPDX-License-Identifier: EPL-2.0 + ******************************************************************************/ +package io.openliberty.tools.intellij.util; + +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.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.diagnostic.Logger; +import com.intellij.openapi.progress.ProcessCanceledException; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.progress.Task; +import com.intellij.openapi.ui.Messages; +import io.openliberty.tools.intellij.LibertyModule; +import org.jetbrains.annotations.NotNull; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.net.ConnectException; +import java.net.ServerSocket; +import java.net.Socket; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * Used for creating a debug configuration, connecting IntelliJ debugger to Liberty server JVM + */ +public class DebugModeHandler { + + protected static Logger LOGGER = Logger.getInstance(DebugModeHandler.class); + + // Default host name + private static String DEFAULT_ATTACH_HOST = "localhost"; + + // Regex captures the debug port value from the custom Maven parameters input + private static final Pattern MAVEN_DEBUG_REGEX = Pattern.compile("(?<=" + Constants.LIBERTY_MAVEN_DEBUG_PARAM + ")([^\\s]+)"); + + // Regex captures the debug port value from the custom Gradle parameters input + private static final Pattern GRADLE_DEBUG_REGEX = Pattern.compile("(?<=" + Constants.LIBERTY_GRADLE_DEBUG_PARAM + ")([^\\s]+)"); + + // Debug address key in Liberty server.env files + private static String WLP_ENV_DEBUG_ADDRESS = "WLP_DEBUG_ADDRESS"; + + /** + * Gets a debug port for the Liberty module. First checks if the debug port was specified as part of the start parameters, + * otherwise allocates a random port. + * + * @param libertyModule Liberty module + * @return JVM port to connect to + * @throws IOException + */ + public int getDebugPort(LibertyModule libertyModule) throws IOException { + // 1. Check if debug port was specified as part of start parameters, if so try that port first + String configParams = libertyModule.getCustomStartParams(); + Matcher m; + if (libertyModule.getProjectType().equals(Constants.LIBERTY_MAVEN_PROJECT)) { + m = MAVEN_DEBUG_REGEX.matcher(configParams); + } else { + m = GRADLE_DEBUG_REGEX.matcher(configParams); + } + if (m.find()) { + // get first match only, if for some reason more than one debug port is specified in the params, try with the first port + String userDebugPortStr = m.group(1); + if (userDebugPortStr != null) { + try { + return Integer.parseInt(userDebugPortStr); + } catch (NumberFormatException e) { + LOGGER.warn(String.format("Unable to parse debug port from user configured params: %s",userDebugPortStr)); + } + } + } + + // 2. Get a random port that is not in use + try (ServerSocket socket = new ServerSocket(0)) { + return socket.getLocalPort(); + } + } + + /** + * Creates a new debug configuration for the corresponding Liberty module + * + * @param libertyModule Liberty module + * @param debugPort JVM port to connect to + */ + public void createDebugConfiguration(LibertyModule libertyModule, int debugPort) { + ProgressManager.getInstance().run(new Task.Backgroundable(libertyModule.getProject(), LocalizedResourceUtil.getMessage("liberty.run.config.title"), true) { + @Override + public void run(@NotNull ProgressIndicator indicator) { + createDebugConfiguration(indicator, libertyModule, debugPort); + } + }); + } + + /** + * Creates a new remote Java application debug configuration + * + * @param indicator progress monitor + * @param libertyModule Liberty module + * @param debugPort JVM port to connect to + */ + private void createDebugConfiguration(ProgressIndicator indicator, LibertyModule libertyModule, int debugPort) { + indicator.setText(LocalizedResourceUtil.getMessage("attaching.debugger")); + try { + String debugPortStr = waitForSocketActivation(indicator, libertyModule, DEFAULT_ATTACH_HOST, debugPort); + RunnerAndConfigurationSettings settings = RunManager.getInstance(libertyModule.getProject()).createConfiguration(libertyModule.getName() + " (Remote)", RemoteConfigurationType.class); + RemoteConfiguration remoteConfiguration = (RemoteConfiguration) settings.getConfiguration(); + remoteConfiguration.PORT = debugPortStr; + long groupId = ExecutionEnvironment.getNextUnusedExecutionId(); + LOGGER.debug(String.format("%s: attempting to attach debugger to port %s"), libertyModule.getName(), debugPortStr); + ExecutionUtil.runConfiguration(settings, DefaultDebugExecutor.getDebugExecutorInstance(), DefaultExecutionTarget.INSTANCE, groupId); + } catch (Exception e) { + // do not show error if debug attachment was cancelled by user + if (!(e instanceof ProcessCanceledException)) { + LOGGER.error(String.format("Cannot connect debugger to port %s", debugPort), e); + ApplicationManager.getApplication().invokeLater(() -> Messages.showErrorDialog(LocalizedResourceUtil.getMessage("cannot.connect.debug.port", debugPort), "Liberty")); + } + } + } + + /** + * Waits for the JDWP socket on the JVM to start listening for connections + * + * @param monitor progress monitor + * @param libertyModule Liberty module + * @param host JVM host to connect to + * @param debugPort JVM port to connect to + * @return debug port as a String + * @throws Exception + */ + private String waitForSocketActivation(ProgressIndicator monitor, LibertyModule libertyModule, String host, int debugPort) throws Exception { + byte[] handshakeString = "JDWP-Handshake".getBytes(StandardCharsets.US_ASCII); + int retryLimit = 120; + int envReadMinLimit = 30; + int envReadMaxLimit = 60; + int envReadInterval = 5; + + Path serverEnvPath = getServerEnvPath(libertyModule); + + for (int retryCount = 0; retryCount < retryLimit; retryCount++) { + // check if cancelled + if (monitor.isCanceled()) { + return null; + } + + if (serverEnvPath == null) { + serverEnvPath = getServerEnvPath(libertyModule); + TimeUnit.SECONDS.sleep(1); + continue; + } + + // Check the server.env at the default location for any updates to WLP_DEBUG_ADDRESS for any changes. + // There is a small window in which the allocated port could have been taken by another process. + // If the port is already in use, dev mode will allocate a random port and reflect that by updating the server.env file. + if (retryCount >= envReadMinLimit && retryCount < envReadMaxLimit && (retryCount % envReadInterval == 0)) { + String envPortStr = readDebugPortFromServerEnv(serverEnvPath.toFile()); + if (envPortStr != null) { + int envPort = Integer.parseInt(envPortStr); + if (envPort != debugPort) { + debugPort = envPort; + } + } + } + + try (Socket socket = new Socket(host, debugPort)) { + socket.getOutputStream().write(handshakeString); + return String.valueOf(debugPort); + } catch (ConnectException e) { + TimeUnit.SECONDS.sleep(1); + } + } + + throw new Exception(String.format("Unable to connect to JVM on host: %s and port: %s", host, debugPort)); + } + + /** + * Returns the default path of the server.env file after Liberty server deployment. + * + * @param libertyModule The Liberty module for which this operations is being performed. + * + * @return The default path of the server.env file after Liberty server deployment. + * + * @throws Exception + */ + private Path getServerEnvPath(LibertyModule libertyModule) throws Exception { + String projectPath = libertyModule.getBuildFile().getParent().getPath(); + Path basePath = null; + if (libertyModule.getProjectType().equals(Constants.LIBERTY_MAVEN_PROJECT)) { + basePath = Paths.get(projectPath, "target", "liberty", "wlp", "usr", "servers"); + } else if (libertyModule.getProjectType().equals(Constants.LIBERTY_GRADLE_PROJECT)) { + basePath = Paths.get(projectPath, "build", "wlp", "usr", "servers"); + } else { + throw new Exception(String.format("Unexpected project build type: %s. Liberty module %s does not appear to be a Maven or Gradle built project", + libertyModule.getProjectType(), libertyModule.getName())); + } + + // Make sure the base path exists. If not return null. + File basePathFile = new File(basePath.toString()); + if (!basePathFile.exists()) { + return null; + } + + try (Stream matchedStream = Files.find(basePath, 2, (path, basicFileAttribute) -> { + if (basicFileAttribute.isRegularFile()) { + return path.getFileName().toString().equalsIgnoreCase("server.env"); + } + return false; + });) { + List matchedPaths = matchedStream.collect(Collectors.toList()); + int numberOfFilesFound = matchedPaths.size(); + + if (numberOfFilesFound != 1) { + if (numberOfFilesFound == 0) { + LOGGER.trace(String.format("Unable to find the server.env file for project %s", libertyModule.getName())); + return null; + } else { + throw new Exception(String.format("More than one server.env files were found for project %s. Unable to determine the server.env file to use", libertyModule.getName())); + } + } + return matchedPaths.get(0); + } + } + + /** + * Returns the last debug port entry in server.env. Null if not found. + * + * @param serverEnv The server.env file object. + * + * @return The last debug port entry in server.env. Null if not found.]] + * + * @throws Exception + */ + private String readDebugPortFromServerEnv(File serverEnv) throws Exception { + String port = null; + + if (serverEnv.exists()) { + try (BufferedReader reader = new BufferedReader(new FileReader(serverEnv))) { + String line = null; + String lastEntry = null; + while ((line = reader.readLine()) != null) { + if (line.contains(WLP_ENV_DEBUG_ADDRESS)) { + lastEntry = line; + } + } + if (lastEntry != null) { + String[] parts = lastEntry.split("="); + port = parts[1].trim(); + } + } + } + return port; + } +} + diff --git a/src/main/java/io/openliberty/tools/intellij/util/LibertyActionUtil.java b/src/main/java/io/openliberty/tools/intellij/util/LibertyActionUtil.java index 5ad235a89..444af0cdd 100644 --- a/src/main/java/io/openliberty/tools/intellij/util/LibertyActionUtil.java +++ b/src/main/java/io/openliberty/tools/intellij/util/LibertyActionUtil.java @@ -40,8 +40,8 @@ public static void executeCommand(ShellTerminalWidget widget, String cmd) { StringBuilder result = new StringBuilder(); result.append(cmd).append(enterCode); connector.write(result.toString()); - } catch (IOException ex) { - LOGGER.error(ex.getMessage()); + } catch (IOException e) { + LOGGER.error(String.format("Failed to execute command: %s", cmd), e); } } diff --git a/src/main/java/io/openliberty/tools/intellij/util/LibertyGradleUtil.java b/src/main/java/io/openliberty/tools/intellij/util/LibertyGradleUtil.java index e1f51885b..331860c48 100644 --- a/src/main/java/io/openliberty/tools/intellij/util/LibertyGradleUtil.java +++ b/src/main/java/io/openliberty/tools/intellij/util/LibertyGradleUtil.java @@ -51,7 +51,7 @@ public static String getProjectName(VirtualFile file) { return name.replaceAll("^[\"']+|[\"']+$", ""); } } catch (IOException e) { - LOGGER.error("Could not read " + settingsPath, e.getMessage()); + LOGGER.error(String.format("Could not read project name from file %s", settingsPath), e); } } return null; diff --git a/src/main/java/io/openliberty/tools/intellij/util/LibertyProjectUtil.java b/src/main/java/io/openliberty/tools/intellij/util/LibertyProjectUtil.java index 2c9f246df..48479d743 100644 --- a/src/main/java/io/openliberty/tools/intellij/util/LibertyProjectUtil.java +++ b/src/main/java/io/openliberty/tools/intellij/util/LibertyProjectUtil.java @@ -189,7 +189,7 @@ private static ArrayList getBuildFiles(Project project, String buildF buildFiles.add(buildFile); } } catch (Exception e) { - LOGGER.error("Error parsing build.gradle", e.getMessage()); + LOGGER.error(String.format("Error parsing build.gradle %s", gradleFile), e.getMessage()); } } } diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index d3c50968f..6ad2a3647 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -29,12 +29,12 @@ com.intellij.properties org.jetbrains.idea.maven com.intellij.gradle - + @@ -111,6 +111,10 @@ + + + + messages.LibertyBundles diff --git a/src/main/resources/messages/LibertyBundles.properties b/src/main/resources/messages/LibertyBundles.properties index 53299150f..a5673ff87 100644 --- a/src/main/resources/messages/LibertyBundles.properties +++ b/src/main/resources/messages/LibertyBundles.properties @@ -40,11 +40,12 @@ action.io.openliberty.tools.intellij.actions.RemoveLibertyProjectAction.descript # Messages for Liberty actions and pop-up dialog no.liberty.projects.detected=No Liberty Maven or Liberty Gradle projects detected in this workspace. -liberty.dev.not.started.notification.title=Liberty dev mode has not been started -liberty.dev.not.started.notification.content=Liberty dev mode has not been started on {0}. \nStart Liberty dev mode from the Liberty tool window. -liberty.project.does.not.resolve=Unable to {0}: Could not resolve project. Ensure you run the Liberty action from the Liberty tool window. -liberty.build.file.does.not.resolve=Unable to {0}: Could not resolve build file for {1}. Ensure you run the Liberty action from the Liberty tool window. -liberty.action.cannot.start=Liberty action was not able to start +liberty.dev.not.started.notification.content=Unable to {0}: Liberty dev mode may not have been started on {1}. \nStart Liberty dev mode from the Liberty tool window. +liberty.project.does.not.resolve=Unable to {0}: could not resolve project. Ensure you run the Liberty action from the Liberty tool window. +liberty.build.file.does.not.resolve=Unable to {0}: could not resolve build file for {1}. Ensure you run the Liberty action from the Liberty tool window. +liberty.project.type.invalid=Unable to {0}: could not resolve Liberty project type for {1}. +liberty.terminal.cannot.resolve=Unable to {0}: could not resolve an IntelliJ terminal for {1}. +liberty.action.cannot.start=Liberty action was not able to run liberty.project.file.selection.dialog.title=Liberty project liberty.project.file.selection.dialog.message=Select Liberty project for {0} @@ -106,4 +107,13 @@ test.summary=Test Summary view.unit.test.report=view unit test report unit.test.report.does.not.exist=Unit Test Report Does Not Exist - +# Run & Debug Configuration +liberty.run.config.title=Liberty Run Configuration +liberty.debug.port.unresolved=Unable to {0} in debug mode: could not resolve debug port for {1}. +attaching.debugger=Attaching Debugger to Liberty Server JVM... +cannot.connect.debug.port=Cannot connect debugger to port {0}. +run.config.liberty.project=Liberty Project +run.config.liberty.project.tool.tip=Select the build file for your Liberty project +run.config.start.parameters=Start Parameters +run.config.start.parameters.tool.tip=eg. -DhotTests=true (Maven) --hotTests=true (Gradle) +specify.custom.parameters.for.the.mvn.liberty.dev.command=Specify custom parameters for the mvn liberty:dev command