From 5c608dfcf1771f647ad1d97bb92323dc3d863ae4 Mon Sep 17 00:00:00 2001 From: Adam Wisniewski Date: Thu, 7 Mar 2024 14:25:59 -0500 Subject: [PATCH] Add debug restart Signed-off-by: Adam Wisniewski --- .../META-INF/MANIFEST.MF | 3 + .../tools/eclipse/DevModeOperations.java | 11 +- .../tools/eclipse/debug/DebugModeHandler.java | 186 +++++++++++------- .../eclipse/debug/LibertyDebugTarget.java | 52 +++++ .../LibertyHotCodeReplaceErrorDialog.java | 77 ++++++++ .../debug/LibertyHotCodeReplaceListener.java | 166 ++++++++++++++++ .../ui/terminal/ProjectTabController.java | 6 + 7 files changed, 426 insertions(+), 75 deletions(-) create mode 100644 bundles/io.openliberty.tools.eclipse.ui/src/io/openliberty/tools/eclipse/debug/LibertyDebugTarget.java create mode 100644 bundles/io.openliberty.tools.eclipse.ui/src/io/openliberty/tools/eclipse/debug/LibertyHotCodeReplaceErrorDialog.java create mode 100644 bundles/io.openliberty.tools.eclipse.ui/src/io/openliberty/tools/eclipse/debug/LibertyHotCodeReplaceListener.java diff --git a/bundles/io.openliberty.tools.eclipse.ui/META-INF/MANIFEST.MF b/bundles/io.openliberty.tools.eclipse.ui/META-INF/MANIFEST.MF index 3a104c9f..933c83e9 100644 --- a/bundles/io.openliberty.tools.eclipse.ui/META-INF/MANIFEST.MF +++ b/bundles/io.openliberty.tools.eclipse.ui/META-INF/MANIFEST.MF @@ -33,6 +33,9 @@ Import-Package: org.eclipse.cdt.launch.ui, org.eclipse.jdt.core, org.eclipse.jdt.debug.core, org.eclipse.jdt.debug.ui.launchConfigurations, + org.eclipse.jdt.internal.debug.core.model, + org.eclipse.jdt.internal.debug.ui, + org.eclipse.jdt.internal.debug.ui.snippeteditor, org.eclipse.jdt.launching, org.eclipse.jdt.launching.sourcelookup.containers, org.eclipse.jem.util.emf.workbench, diff --git a/bundles/io.openliberty.tools.eclipse.ui/src/io/openliberty/tools/eclipse/DevModeOperations.java b/bundles/io.openliberty.tools.eclipse.ui/src/io/openliberty/tools/eclipse/DevModeOperations.java index 200b1b11..95a44308 100644 --- a/bundles/io.openliberty.tools.eclipse.ui/src/io/openliberty/tools/eclipse/DevModeOperations.java +++ b/bundles/io.openliberty.tools.eclipse.ui/src/io/openliberty/tools/eclipse/DevModeOperations.java @@ -18,7 +18,6 @@ import java.net.URL; import java.nio.file.Path; import java.nio.file.Paths; -import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -241,7 +240,7 @@ public void start(IProject iProject, String parms, String javaHomePath, ILaunch // If there is a debugPort, start the job to attach the debugger to the Liberty server JVM. if (debugPort != null) { - debugModeHandler.startDebugAttacher(project, launch, debugPort); + debugModeHandler.startDebugAttacher(project, launch, debugPort, false); } } catch (CommandNotFoundException e) { String msg = "Maven or Gradle command not found for project " + projectName; @@ -356,7 +355,7 @@ public void startInContainer(IProject iProject, String parms, String javaHomePat // If there is a debugPort, start the job to attach the debugger to the Liberty server JVM. if (debugPort != null) { - debugModeHandler.startDebugAttacher(project, launch, debugPort); + debugModeHandler.startDebugAttacher(project, launch, debugPort, false); } } catch (Exception e) { String msg = "An error was detected during the start in container request on project " + projectName; @@ -989,6 +988,12 @@ public static Path getMavenIntegrationTestReportPath(String projectPath) { return path; } + public Path getLibertyPluginConfigXmlPath(String projectPath) { + Path path = Paths.get(projectPath, "target", "liberty-plugin-config.xml"); + + return path; + } + /** * Returns the path of the HTML file containing the unit test report. * diff --git a/bundles/io.openliberty.tools.eclipse.ui/src/io/openliberty/tools/eclipse/debug/DebugModeHandler.java b/bundles/io.openliberty.tools.eclipse.ui/src/io/openliberty/tools/eclipse/debug/DebugModeHandler.java index 02b81199..e7bc9b81 100644 --- a/bundles/io.openliberty.tools.eclipse.ui/src/io/openliberty/tools/eclipse/debug/DebugModeHandler.java +++ b/bundles/io.openliberty.tools.eclipse.ui/src/io/openliberty/tools/eclipse/debug/DebugModeHandler.java @@ -12,9 +12,6 @@ *******************************************************************************/ package io.openliberty.tools.eclipse.debug; -import java.io.BufferedReader; -import java.io.File; -import java.io.FileReader; import java.io.IOException; import java.net.ConnectException; import java.net.ServerSocket; @@ -25,9 +22,12 @@ import java.nio.file.Paths; import java.util.List; import java.util.Map; +import java.util.Scanner; import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; -import java.util.stream.Stream; + +import javax.xml.XMLConstants; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; @@ -41,16 +41,16 @@ import org.eclipse.debug.core.model.IDebugTarget; import org.eclipse.jdi.Bootstrap; import org.eclipse.jdi.TimeoutException; -import org.eclipse.jdt.debug.core.JDIDebugModel; import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants; import org.eclipse.jdt.launching.JavaRuntime; -import org.eclipse.osgi.util.NLS; import org.eclipse.swt.widgets.Display; import org.eclipse.ui.IViewPart; import org.eclipse.ui.IWorkbench; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.PlatformUI; +import org.w3c.dom.Document; +import org.w3c.dom.NodeList; import com.sun.jdi.VirtualMachine; import com.sun.jdi.connect.AttachingConnector; @@ -62,9 +62,10 @@ import io.openliberty.tools.eclipse.LibertyDevPlugin; import io.openliberty.tools.eclipse.Project; import io.openliberty.tools.eclipse.Project.BuildType; +import io.openliberty.tools.eclipse.logging.Logger; import io.openliberty.tools.eclipse.logging.Trace; -import io.openliberty.tools.eclipse.messages.Messages; import io.openliberty.tools.eclipse.ui.dashboard.DashboardView; +import io.openliberty.tools.eclipse.ui.launch.LaunchConfigurationHelper; import io.openliberty.tools.eclipse.ui.terminal.ProjectTabController; import io.openliberty.tools.eclipse.ui.terminal.TerminalListener; import io.openliberty.tools.eclipse.utils.ErrorHandler; @@ -95,6 +96,9 @@ public class DebugModeHandler { /** Job status return code indicating that an error took place while attempting to attach the debugger to the JVM. */ public static int JOB_STATUS_DEBUGGER_CONN_ERROR = 1; + /** Instance to this class. */ + private LaunchConfigurationHelper launchConfigHelper = LaunchConfigurationHelper.getInstance(); + /** DevModeOperations instance. */ private DevModeOperations devModeOps; @@ -217,7 +221,7 @@ public String calculateDebugPort(Project project, String inputParms) throws Exce * * @throws Exception */ - public void startDebugAttacher(Project project, ILaunch launch, String debugPort) { + public void startDebugAttacher(Project project, ILaunch launch, String port, boolean readDebugPort) { String projectName = project.getIProject().getName(); Job job = new Job("Attaching Debugger to JVM...") { @@ -228,6 +232,27 @@ protected IStatus run(IProgressMonitor monitor) { return Status.CANCEL_STATUS; } + String debugPort = null; + + if (readDebugPort) { + // Read debug port from server.env. If devmode has restarted the server, it may + // take a few seconds for it to find a new port in the event that the previous port + // is still being held by the OS. + Thread.sleep(10000); + + try { + Path serverEnvPath = getServerEnvFile(project); + if (serverEnvPath != null) { + debugPort = readDebugPortFromServerEnv(serverEnvPath); + } + } catch (Exception e) { + Logger.logError("Failure while attempting to read debug port from server.env file", e); + } + } + if (debugPort == null) { + debugPort = port; + } + String portToConnect = waitForSocketActivation(project, DEFAULT_ATTACH_HOST, debugPort, monitor); if (portToConnect == null) { return Status.CANCEL_STATUS; @@ -236,7 +261,8 @@ protected IStatus run(IProgressMonitor monitor) { AttachingConnector connector = getAttachingConnector(); Map map = connector.defaultArguments(); configureConnector(map, DEFAULT_ATTACH_HOST, Integer.parseInt(portToConnect)); - IDebugTarget debugTarget = createRemoteJDTDebugTarget(launch, Integer.parseInt(portToConnect), DEFAULT_ATTACH_HOST, + IDebugTarget debugTarget = createRemoteJDTDebugTarget(launch, project, Integer.parseInt(portToConnect), + DEFAULT_ATTACH_HOST, connector, map); launch.addDebugTarget(debugTarget); @@ -334,7 +360,7 @@ private void configureConnector(Map map, String host, int port } } - private IDebugTarget createRemoteJDTDebugTarget(ILaunch launch, int remoteDebugPortNum, String hostName, + private IDebugTarget createRemoteJDTDebugTarget(ILaunch launch, Project project, int remoteDebugPortNum, String hostName, AttachingConnector connector, Map map) throws CoreException { if (launch == null || hostName == null || hostName.length() == 0) { return null; @@ -351,8 +377,13 @@ private IDebugTarget createRemoteJDTDebugTarget(ILaunch launch, int remoteDebugP throw new CoreException( new Status(IStatus.ERROR, this.getClass(), IJavaLaunchConfigurationConstants.ERR_CONNECTION_FAILED, "", ex)); } - debugTarget = JDIDebugModel.newDebugTarget(launch, remoteVM, hostName + ":" + remoteDebugPortNum, null, true, false, true); - return debugTarget; + LibertyDebugTarget libertyDebugTarget = new LibertyDebugTarget(launch, remoteVM, hostName + ":" + remoteDebugPortNum, + new RestartDebugger(project, launch, String.valueOf(remoteDebugPortNum))); + + // Add hot code replace listener to listen for hot code replace failure. + libertyDebugTarget.addHotCodeReplaceListener(new LibertyHotCodeReplaceListener()); + + return libertyDebugTarget; } /** @@ -461,94 +492,76 @@ private void openDebugPerspective() { } /** - * Returns the default path of the server.env file after Liberty server deployment. + * Returns the path of the server.env file after Liberty server deployment. * * @param project The project for which this operations is being performed. * - * @return The default path of the server.env file after Liberty server deployment. + * @return The path of the server.env file after Liberty server deployment. * * @throws Exception */ - private Path getServerEnvPath(Project project) throws Exception { - Path basePath = null; + private Path getServerEnvFile(Project project) throws Exception { + Project serverProj = getLibertyServerProject(project); String projectPath = serverProj.getPath(); - String projectName = serverProj.getName(); - BuildType buildType = serverProj.getBuildType(); - if (buildType == Project.BuildType.MAVEN) { - basePath = Paths.get(projectPath, "target", "liberty", "wlp", "usr", "servers"); - } else if (buildType == Project.BuildType.GRADLE) { - basePath = Paths.get(projectPath, "build", "wlp", "usr", "servers"); - } else { - throw new Exception("Unexpected project build type: " + buildType + ". Project" + projectName - + "does not appear to be a Maven or Gradle built project."); - } + Path libertyPluginConfigXmlPath = devModeOps.getLibertyPluginConfigXmlPath(projectPath); + + // Read server.env path from liberty-plugin-config.xml + + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); + DocumentBuilder db = dbf.newDocumentBuilder(); + + Document doc = db.parse(libertyPluginConfigXmlPath.toFile()); + doc.getDocumentElement().normalize(); + + NodeList list = doc.getElementsByTagName("serverDirectory"); + Path serverEnvPath = Paths.get(list.item(0).getTextContent(), "server.env"); - // Make sure the base path exists. If not return null. - File basePathFile = new File(basePath.toString()); - if (!basePathFile.exists()) { + // Make sure the server.env path exists. If not return null. + if (!Files.exists(serverEnvPath)) { return null; } - try (Stream matchedStream = Files.find(basePath, 2, (path, basicFileAttribute) -> { - if (basicFileAttribute.isRegularFile()) { - return path.getFileName().toString().equalsIgnoreCase(WLP_SERVER_ENV_FILE_NAME); - } - return false; - });) { - List matchedPaths = matchedStream.collect(Collectors.toList()); - int numberOfFilesFound = matchedPaths.size(); - - if (numberOfFilesFound != 1) { - if (numberOfFilesFound == 0) { - String msg = "Unable to find the server.env file for project " + projectName + "."; - if (Trace.isEnabled()) { - Trace.getTracer().trace(Trace.TRACE_UI, msg); - } - return null; - } else { - String msg = "More than one server.env file was found for project " + projectName - + ". Unable to determine which server.env file to use."; - if (Trace.isEnabled()) { - Trace.getTracer().trace(Trace.TRACE_UI, msg); - } - ErrorHandler.processErrorMessage(NLS.bind(Messages.multiple_server_env, projectName), false); - throw new Exception(msg); - } - } - return matchedPaths.get(0); - } + return serverEnvPath; + } /** * Returns the port value associated with the WLP_DEBUG_ADDRESS entry in server.env. Null if not found. If there are multiple * WLP_DEBUG_ADDRESS entries, the last entry is returned. * - * @param serverEnv The server.env file object. + * @param serverEnv The server.env Path object. * * @return Returns the port value associated with the WLP_DEBUG_ADDRESS entry in server.env. Null if not found. If there are * multiple WLP_DEBUG_ADDRESS entries, the last entry is returned. * * @throws Exception */ - public String readDebugPortFromServerEnv(File serverEnv) throws Exception { + public String readDebugPortFromServerEnv(Path 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(); + if (Files.exists(serverEnv)) { + String lastEntry = null; + + Scanner scan = new Scanner(serverEnv); + String line; + + while (scan.hasNextLine()) { + line = scan.nextLine(); + if (line.contains(WLP_ENV_DEBUG_ADDRESS)) { + lastEntry = line; } } + + scan.close(); + + if (lastEntry != null) { + String[] parts = lastEntry.split("="); + port = parts[1].trim(); + } + } return port; @@ -645,4 +658,33 @@ private Project getLibertyServerProject(Project project) throws Exception { private class DataHolder { boolean closed; } + + /** + * This class is used as a callback to DebugModeHandler. LibertyDebugTarget will + * use this to restart the debugger in the event the Liberty server is restarted by dev mode + * or a hot code replace failure occurs. + */ + class RestartDebugger { + + private Project project; + private ILaunch launch; + private String port; + + RestartDebugger(Project project, ILaunch launch, String port) { + this.project = project; + this.launch = launch; + this.port = port; + } + + /** + * Recreate and re-attach the debug target to the running server + */ + public void restart() { + // If dev mode restarted the server, the debug port may have changed and + // we need to read the new value from server.env. At this point, we do not + // know if the debugger restarted due to a HCR failure, a manual disconnect, + // or a dev mode restart, so we need to read the port in all cases. + startDebugAttacher(project, launch, port, true); + } + } } diff --git a/bundles/io.openliberty.tools.eclipse.ui/src/io/openliberty/tools/eclipse/debug/LibertyDebugTarget.java b/bundles/io.openliberty.tools.eclipse.ui/src/io/openliberty/tools/eclipse/debug/LibertyDebugTarget.java new file mode 100644 index 00000000..aeb964ba --- /dev/null +++ b/bundles/io.openliberty.tools.eclipse.ui/src/io/openliberty/tools/eclipse/debug/LibertyDebugTarget.java @@ -0,0 +1,52 @@ +package io.openliberty.tools.eclipse.debug; + +import org.eclipse.debug.core.ILaunch; +import org.eclipse.jdt.internal.debug.core.model.JDIDebugTarget; + +import com.sun.jdi.VirtualMachine; +import com.sun.jdi.event.VMDeathEvent; +import com.sun.jdi.event.VMDisconnectEvent; + +import io.openliberty.tools.eclipse.debug.DebugModeHandler.RestartDebugger; + +/** + * This class is an extension of the JDIDebugTarget and allows the debugger to be restarted + * automatically if the VM restarts or a hot code replace failure occurs. + */ +public class LibertyDebugTarget extends JDIDebugTarget { + + private RestartDebugger restartDebugger; + + public LibertyDebugTarget(ILaunch launch, VirtualMachine jvm, String name, RestartDebugger restartDebugger) { + super(launch, jvm, "Liberty Application Debug: " + name, true, true, null, true); + + this.restartDebugger = restartDebugger; + } + + @Override + public void handleVMDeath(VMDeathEvent event) { + disconnected(); + } + + @Override + public void handleVMDisconnect(VMDisconnectEvent event) { + disconnected(); + } + + /** + * Updates the state of this target for disconnection from the VM. + */ + @Override + protected void disconnected() { + setDisconnecting(false); + if (!isDisconnected()) { + setDisconnected(true); + cleanup(); + + getLaunch().removeDebugTarget(this); + restartDebugger.restart(); + restartDebugger = null; + } + } + +} diff --git a/bundles/io.openliberty.tools.eclipse.ui/src/io/openliberty/tools/eclipse/debug/LibertyHotCodeReplaceErrorDialog.java b/bundles/io.openliberty.tools.eclipse.ui/src/io/openliberty/tools/eclipse/debug/LibertyHotCodeReplaceErrorDialog.java new file mode 100644 index 00000000..1ed20449 --- /dev/null +++ b/bundles/io.openliberty.tools.eclipse.ui/src/io/openliberty/tools/eclipse/debug/LibertyHotCodeReplaceErrorDialog.java @@ -0,0 +1,77 @@ +/******************************************************************************* +* Copyright (c) 2022, 2023 IBM Corporation 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. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation - initial implementation +*******************************************************************************/ +package io.openliberty.tools.eclipse.debug; + +import org.eclipse.core.runtime.IStatus; +import org.eclipse.debug.core.model.IDebugTarget; +import org.eclipse.jdt.internal.debug.ui.DebugUIMessages; +import org.eclipse.jdt.internal.debug.ui.HotCodeReplaceErrorDialog; +import org.eclipse.jface.dialogs.IDialogConstants; +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Shell; + +/** + * This class is an extension of the Eclipse JDT HotCodeReplaceErrorDialog. It provides + * a custom display when a hot code replace failure occurs allowing for the user to refresh the + * debugger. + */ +public class LibertyHotCodeReplaceErrorDialog extends HotCodeReplaceErrorDialog { + + public LibertyHotCodeReplaceErrorDialog(Shell parentShell, String dialogTitle, String message, IStatus status, String preferenceKey, + String toggleMessage, IPreferenceStore store, IDebugTarget target) { + super(parentShell, dialogTitle, message, status, preferenceKey, toggleMessage, store, target); + } + + @Override + protected void createButtonsForButtonBar(Composite parent) { + createButton(parent, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL, true); + createDetailsButton(parent); + getButton(IDialogConstants.OK_ID).setText(DebugUIMessages.HotCodeReplaceErrorDialog_0); + createButton(parent, DISCONNECT_ID, "Refresh Debugger", false); + + blockMnemonicWithoutModifier(getToggleButton()); + } + + // /* + // * (non-Javadoc) + // * @see org.eclipse.jface.dialogs.Dialog#buttonPressed(int) + // */ + // @Override + // protected void buttonPressed(final int id) { + // if (id == DISCONNECT_ID) { + // final DebugException[] ex = new DebugException[1]; + // final String[] operation = new String[1]; + // ex[0] = null; + // Runnable r = new Runnable() { + // @Override + // public void run() { + // try { + // operation[0] = DebugUIMessages.HotCodeReplaceErrorDialog_6; + // target.disconnect(); + // } catch (Exception e) { + // IStatus errorStatus = new Status(IStatus.ERROR, JDIDebugPlugin.getUniqueIdentifier(), e.getMessage(), e); + // ex[0] = new DebugException(errorStatus); + // } + // } + // }; + // BusyIndicator.showWhile(getShell().getDisplay(), r); + // if (ex[0] != null) { + // JDIDebugUIPlugin.statusDialog(NLS.bind(DebugUIMessages.HotCodeReplaceErrorDialog_2, operation), ex[0].getStatus()); + // } + // okPressed(); + // } else { + // super.buttonPressed(id); + // } + // } +} diff --git a/bundles/io.openliberty.tools.eclipse.ui/src/io/openliberty/tools/eclipse/debug/LibertyHotCodeReplaceListener.java b/bundles/io.openliberty.tools.eclipse.ui/src/io/openliberty/tools/eclipse/debug/LibertyHotCodeReplaceListener.java new file mode 100644 index 00000000..f38c38b5 --- /dev/null +++ b/bundles/io.openliberty.tools.eclipse.ui/src/io/openliberty/tools/eclipse/debug/LibertyHotCodeReplaceListener.java @@ -0,0 +1,166 @@ +/******************************************************************************* +* Copyright (c) 2022, 2023 IBM Corporation 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. +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* IBM Corporation - initial implementation +*******************************************************************************/ +package io.openliberty.tools.eclipse.debug; + +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.debug.core.DebugException; +import org.eclipse.debug.core.ILaunch; +import org.eclipse.debug.core.ILaunchConfiguration; +import org.eclipse.debug.ui.DebugUITools; +import org.eclipse.jdt.debug.core.IJavaDebugTarget; +import org.eclipse.jdt.debug.core.IJavaHotCodeReplaceListener; +import org.eclipse.jdt.internal.debug.ui.DebugUIMessages; +import org.eclipse.jdt.internal.debug.ui.IJDIPreferencesConstants; +import org.eclipse.jdt.internal.debug.ui.JDIDebugUIPlugin; +import org.eclipse.jdt.internal.debug.ui.snippeteditor.ScrapbookLauncher; +import org.eclipse.jface.viewers.ILabelProvider; +import org.eclipse.osgi.util.NLS; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; + +/** + * Most of the code in this class is taken the Eclipse JDT JavaHotCodeReplaceListener: + * . + * https://raw.githubusercontent.com/eclipse-jdt/eclipse.jdt.debug/master/org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/JavaHotCodeReplaceListener.java + * . + * The behavior is the same except our LibertyHotCodeReplaceErrorDialog is used. + */ +public class LibertyHotCodeReplaceListener implements IJavaHotCodeReplaceListener { + + private LibertyHotCodeReplaceErrorDialog libertyHotCodeReplaceFailedErrorDialog = null; + + private ILabelProvider fLabelProvider = DebugUITools.newDebugModelPresentation(); + + /** + * @see IJavaHotCodeReplaceListener#hotCodeReplaceSucceeded(IJavaDebugTarget) + */ + @Override + public void hotCodeReplaceSucceeded(IJavaDebugTarget target) { + } + + /** + * @see IJavaHotCodeReplaceListener#hotCodeReplaceFailed(IJavaDebugTarget, DebugException) + */ + @Override + public void hotCodeReplaceFailed(final IJavaDebugTarget target, final DebugException exception) { + if ((exception != null + && !JDIDebugUIPlugin.getDefault().getPreferenceStore().getBoolean(IJDIPreferencesConstants.PREF_ALERT_HCR_FAILED)) || + ((exception == null) && !JDIDebugUIPlugin.getDefault().getPreferenceStore() + .getBoolean(IJDIPreferencesConstants.PREF_ALERT_HCR_NOT_SUPPORTED))) { + return; + } + // do not report errors for snippet editor targets + // that do not support HCR. HCR is simulated by using + // a new class loader for each evaluation + ILaunch launch = target.getLaunch(); + if (launch.getAttribute(ScrapbookLauncher.SCRAPBOOK_LAUNCH) != null) { + if (!target.supportsHotCodeReplace()) { + return; + } + } + final Display display = JDIDebugUIPlugin.getStandardDisplay(); + if (display.isDisposed()) { + return; + } + + String name = null; + try { + name = target.getName(); + } catch (DebugException e) { + name = fLabelProvider.getText(target); + } + final String vmName = name; + final IStatus status; + final String preference; + final String alertMessage; + ILaunchConfiguration config = target.getLaunch().getLaunchConfiguration(); + final String launchName = (config != null ? config.getName() : DebugUIMessages.JavaHotCodeReplaceListener_0); + if (exception == null) { + status = new Status(IStatus.WARNING, JDIDebugUIPlugin.getUniqueIdentifier(), IStatus.WARNING, + DebugUIMessages.JDIDebugUIPlugin_The_target_VM_does_not_support_hot_code_replace_1, null); + preference = IJDIPreferencesConstants.PREF_ALERT_HCR_NOT_SUPPORTED; + alertMessage = DebugUIMessages.JDIDebugUIPlugin_3; + } else { + status = new Status(IStatus.WARNING, JDIDebugUIPlugin.getUniqueIdentifier(), IStatus.WARNING, exception.getMessage(), + exception.getCause()); + preference = IJDIPreferencesConstants.PREF_ALERT_HCR_FAILED; + alertMessage = DebugUIMessages.JDIDebugUIPlugin_1; + } + final String title = DebugUIMessages.JDIDebugUIPlugin_Hot_code_replace_failed_1; + final String message = NLS.bind( + DebugUIMessages.JDIDebugUIPlugin__0__was_unable_to_replace_the_running_code_with_the_code_in_the_workspace__2, + new Object[] { vmName, launchName }); + display.asyncExec(new Runnable() { + @Override + public void run() { + if (display.isDisposed()) { + return; + } + if (libertyHotCodeReplaceFailedErrorDialog != null) { + Shell shell = libertyHotCodeReplaceFailedErrorDialog.getShell(); + if (shell != null && !shell.isDisposed()) { + return; + } + } + Shell shell = JDIDebugUIPlugin.getActiveWorkbenchShell(); + libertyHotCodeReplaceFailedErrorDialog = new LibertyHotCodeReplaceErrorDialog(shell, title, message, status, preference, + alertMessage, + JDIDebugUIPlugin.getDefault().getPreferenceStore(), target) { + @Override + public boolean close() { + libertyHotCodeReplaceFailedErrorDialog = null; + return super.close(); + } + }; + libertyHotCodeReplaceFailedErrorDialog.setBlockOnOpen(false); + libertyHotCodeReplaceFailedErrorDialog.open(); + } + }); + } + + /** + * @see IJavaHotCodeReplaceListener#obsoleteMethods(IJavaDebugTarget) + */ + @Override + public void obsoleteMethods(final IJavaDebugTarget target) { + if (!JDIDebugUIPlugin.getDefault().getPreferenceStore().getBoolean(IJDIPreferencesConstants.PREF_ALERT_OBSOLETE_METHODS)) { + return; + } + final Display display = JDIDebugUIPlugin.getStandardDisplay(); + if (display.isDisposed()) { + return; + } + final String vmName = fLabelProvider.getText(target); + final String dialogTitle = DebugUIMessages.JDIDebugUIPlugin_Obsolete_methods_remain_1; + final String message = NLS.bind(DebugUIMessages.JDIDebugUIPlugin__0__contains_obsolete_methods_1, new Object[] { vmName }); + final IStatus status = new Status(IStatus.WARNING, JDIDebugUIPlugin.getUniqueIdentifier(), IStatus.WARNING, + DebugUIMessages.JDIDebugUIPlugin_Stepping_may_be_hazardous_1, null); + final String toggleMessage = DebugUIMessages.JDIDebugUIPlugin_2; + display.asyncExec(new Runnable() { + @Override + public void run() { + if (display.isDisposed()) { + return; + } + Shell shell = JDIDebugUIPlugin.getActiveWorkbenchShell(); + LibertyHotCodeReplaceErrorDialog dialog = new LibertyHotCodeReplaceErrorDialog(shell, dialogTitle, message, status, + IJDIPreferencesConstants.PREF_ALERT_OBSOLETE_METHODS, + toggleMessage, JDIDebugUIPlugin.getDefault().getPreferenceStore(), target); + dialog.setBlockOnOpen(false); + dialog.open(); + } + }); + } + +} diff --git a/bundles/io.openliberty.tools.eclipse.ui/src/io/openliberty/tools/eclipse/ui/terminal/ProjectTabController.java b/bundles/io.openliberty.tools.eclipse.ui/src/io/openliberty/tools/eclipse/ui/terminal/ProjectTabController.java index fa8318f3..3e6adab7 100644 --- a/bundles/io.openliberty.tools.eclipse.ui/src/io/openliberty/tools/eclipse/ui/terminal/ProjectTabController.java +++ b/bundles/io.openliberty.tools.eclipse.ui/src/io/openliberty/tools/eclipse/ui/terminal/ProjectTabController.java @@ -245,6 +245,12 @@ public boolean isProjectTabMarkedClosed(String projectName) { if (tabTitle != null && tabTitle.startsWith("")) { return true; } + } else { + // At this point, the project is no longer in the projectTabMap. Either it was never added (this project + // was never started) or it has already stopped. In either case, the project tab is unavailable (e.g. "closed") + // for this project. This is particularly needed during debugger restart processing. If the server has stopped + // the restart uses this method to indicate it can abort reconnecting. + return true; } return false;