From fc83bc2321236c45a53d3c4570c1768ca47e7809 Mon Sep 17 00:00:00 2001 From: sougandhs Date: Sat, 8 Nov 2025 08:26:21 +0530 Subject: [PATCH] Prompt user when switching to a locked workspace in Eclipse Prompts user on attempting to switch to a workspace that is currently locked by another Eclipse instance, a prompt dialog is now shown informing the user about the lock details and preventing Eclipse from exiting --- .../internal/copy/WorkbenchSWTMessages.java | 13 ++- .../swt/internal/copy/messages.properties | 8 +- .../ide/application/IDEApplication.java | 90 +++---------------- .../ui/internal/ide/IDEWorkbenchMessages.java | 10 +-- .../ui/internal/ide/messages.properties | 8 +- .../org/eclipse/ui/internal/Workbench.java | 87 +++++++++++++++++- 6 files changed, 118 insertions(+), 98 deletions(-) diff --git a/bundles/org.eclipse.e4.ui.workbench.swt/src/org/eclipse/e4/ui/workbench/swt/internal/copy/WorkbenchSWTMessages.java b/bundles/org.eclipse.e4.ui.workbench.swt/src/org/eclipse/e4/ui/workbench/swt/internal/copy/WorkbenchSWTMessages.java index d7edc32bd8d..addfe96e481 100644 --- a/bundles/org.eclipse.e4.ui.workbench.swt/src/org/eclipse/e4/ui/workbench/swt/internal/copy/WorkbenchSWTMessages.java +++ b/bundles/org.eclipse.e4.ui.workbench.swt/src/org/eclipse/e4/ui/workbench/swt/internal/copy/WorkbenchSWTMessages.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2005, 2019 IBM Corporation and others. + * Copyright (c) 2005, 2025 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -42,6 +42,17 @@ public class WorkbenchSWTMessages extends NLS { public static String InternalError; public static String IDEApplication_versionTitle; public static String IDEApplication_versionMessage; + public static String IDEApplication_workspaceLockMessage; + + public static String IDEApplication_workspaceLockOwner; + + public static String IDEApplication_workspaceLockHost; + + public static String IDEApplication_workspaceLockDisplay; + + public static String IDEApplication_workspaceLockPID; + + public static String IDEApplication_workspaceCannotLockMessage2; static { // load message values from bundle file diff --git a/bundles/org.eclipse.e4.ui.workbench.swt/src/org/eclipse/e4/ui/workbench/swt/internal/copy/messages.properties b/bundles/org.eclipse.e4.ui.workbench.swt/src/org/eclipse/e4/ui/workbench/swt/internal/copy/messages.properties index 4bfe270a62e..b27474e8284 100644 --- a/bundles/org.eclipse.e4.ui.workbench.swt/src/org/eclipse/e4/ui/workbench/swt/internal/copy/messages.properties +++ b/bundles/org.eclipse.e4.ui.workbench.swt/src/org/eclipse/e4/ui/workbench/swt/internal/copy/messages.properties @@ -1,5 +1,5 @@ ############################################################################### -# Copyright (c) 2000, 2019 IBM Corporation and others. +# Copyright (c) 2000, 2025 IBM Corporation and others. # # This program and the accompanying materials # are made available under the terms of the Eclipse Public License 2.0 @@ -42,3 +42,9 @@ This workspace was written with a different version of the product and needs to {0}\n\n\ Updating the workspace may make it incompatible with other versions of the product.\n\ Press OK to update the workspace and open it. Press Cancel to select a different workspace. +IDEApplication_workspaceLockOwner=User:\t\t{0}\n +IDEApplication_workspaceLockHost=Host:\t\t{0}\n +IDEApplication_workspaceLockDisplay=Display:\t\t{0}\n +IDEApplication_workspaceLockPID=Process ID:\t{0}\n +IDEApplication_workspaceLockMessage=Workspace lock is currently held by:\n{0} +IDEApplication_workspaceCannotLockMessage2=Could not switch to the selected workspace ''{0}'' because it is currently in use by another Eclipse instance. \ No newline at end of file diff --git a/bundles/org.eclipse.ui.ide.application/src/org/eclipse/ui/internal/ide/application/IDEApplication.java b/bundles/org.eclipse.ui.ide.application/src/org/eclipse/ui/internal/ide/application/IDEApplication.java index de109fddcea..dc87d107fb7 100644 --- a/bundles/org.eclipse.ui.ide.application/src/org/eclipse/ui/internal/ide/application/IDEApplication.java +++ b/bundles/org.eclipse.ui.ide.application/src/org/eclipse/ui/internal/ide/application/IDEApplication.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2003, 2020 IBM Corporation and others. + * Copyright (c) 2003, 2025 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -24,11 +24,9 @@ import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; -import java.io.InputStream; import java.io.OutputStream; import java.net.InetAddress; import java.net.MalformedURLException; -import java.net.URISyntaxException; import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; @@ -42,7 +40,6 @@ import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; -import org.eclipse.core.runtime.URIUtil; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.core.runtime.preferences.ConfigurationScope; import org.eclipse.equinox.app.IApplication; @@ -89,8 +86,6 @@ public class IDEApplication implements IApplication, IExecutableExtension { private static final String VERSION_FILENAME = "version.ini"; //$NON-NLS-1$ - private static final Path LOCK_INFO_FILE = Path.of(METADATA_FOLDER, ".lock_info"); //$NON-NLS-1$ - private static final String DISPLAY_VAR = "DISPLAY"; //$NON-NLS-1$ private static final String HOST_NAME_VAR = "HOSTNAME"; //$NON-NLS-1$ @@ -223,12 +218,13 @@ public void setInitializationData(IConfigurationElement config, } /** - * Return null if a valid workspace path has been set and an exit code otherwise. - * Prompt for and set the path if possible and required. + * Returns null if a valid workspace has been selected or locked + * successfully, and an exit code otherwise. Prompts for and sets the workspace + * path if required. * * @param applicationArguments the command line arguments - * @return null if a valid instance location has been set and an exit code - * otherwise + * @return null if a valid instance location has been set and an + * exit code otherwise */ @SuppressWarnings("rawtypes") protected Object checkInstanceLocation(Shell shell, Map applicationArguments) { @@ -273,18 +269,11 @@ protected Object checkInstanceLocation(Shell shell, Map applicationArguments) { return EXIT_WORKSPACE_LOCKED; } - String wsLockedError = NLS.bind(IDEWorkbenchMessages.IDEApplication_workspaceCannotLockMessage, - workspaceDirectory.getAbsolutePath()); // check if there is a lock info then append it to error message. - String lockInfo = getWorkspaceLockInfo(instanceLoc.getURL()); - if (lockInfo != null && !lockInfo.isBlank()) { - wsLockedError = wsLockedError + System.lineSeparator() + System.lineSeparator() - + NLS.bind(IDEWorkbenchMessages.IDEApplication_Ws_Lock_Owner_Message, lockInfo); + String lockInfo = Workbench.getWorkspaceLockDetails(instanceLoc.getURL()); + if (lockInfo != null) { + Workbench.showWorkspaceLockedDialog(workspaceDirectory.getAbsolutePath(), lockInfo); } - MessageDialog.openError( - shell, - IDEWorkbenchMessages.IDEApplication_workspaceCannotLockTitle, - wsLockedError); } else { MessageDialog.openError( shell, @@ -378,7 +367,7 @@ protected Object checkInstanceLocation(Shell shell, Map applicationArguments) { // by this point it has been determined that the workspace is // already in use -- force the user to choose again - String lockInfo = getWorkspaceLockInfo(workspaceUrl); + String lockInfo = Workbench.getWorkspaceLockDetails(workspaceUrl); MessageDialog dialog = new MessageDialog(null, IDEWorkbenchMessages.IDEApplication_workspaceInUseTitle, null, NLS.bind(IDEWorkbenchMessages.IDEApplication_workspaceInUseMessage, workspaceUrl.getFile()), @@ -412,49 +401,6 @@ protected Control createCustomArea(Composite parent) { } } - /** - * Read workspace lock file and parse all the properties present. Based on the - * eclipse version and operating system some or all the properties may not - * present. In such scenario it will return empty string. - * - * @return Previous lock owner details. - */ - protected String getWorkspaceLockInfo(URL workspaceUrl) { - try { - Path lockFile = getLockInfoFile(workspaceUrl); - if (!Files.exists(lockFile)) { - return null; - } - - StringBuilder sb = new StringBuilder(); - Properties props = new Properties(); - try (InputStream is = Files.newInputStream(lockFile)) { - props.load(is); - String prop = props.getProperty(USER); - if (prop != null) { - sb.append(NLS.bind(IDEWorkbenchMessages.IDEApplication_Ws_Lock_Owner_User, prop)); - } - prop = props.getProperty(HOST); - if (prop != null) { - sb.append(NLS.bind(IDEWorkbenchMessages.IDEApplication_Ws_Lock_Owner_Host, prop)); - } - prop = props.getProperty(DISPLAY); - if (prop != null) { - sb.append(NLS.bind(IDEWorkbenchMessages.IDEApplication_Ws_Lock_Owner_Disp, prop)); - } - prop = props.getProperty(PROCESS_ID); - if (prop != null) { - sb.append(NLS.bind(IDEWorkbenchMessages.IDEApplication_Ws_Lock_Owner_P_Id, prop)); - } - return sb.toString(); - } - } catch (Exception e) { - IDEWorkbenchPlugin.log("Could not read lock info file: ", e); //$NON-NLS-1$ - - } - return null; - } - /** * Write lock owner details onto workspace lock file. Data includes user, host, * display and current java process id. @@ -532,20 +478,6 @@ private String getHostName() { return hostName; } - /** - * Returns the .lock_info file. Does not check if it exists. - * - * @param workspaceUrl - * @return .lock_info file. - */ - private static Path getLockInfoFile(URL workspaceUrl) { - try { - return Path.of(URIUtil.toURI(workspaceUrl)).resolve(LOCK_INFO_FILE); - } catch (URISyntaxException e) { - throw new IllegalArgumentException(e); - } - } - /** * Creates the .lock_info file if it does not exist. * @@ -553,7 +485,7 @@ private static Path getLockInfoFile(URL workspaceUrl) { * @return .lock_info file. */ private static Path createLockInfoFile(URL workspaceUrl) throws Exception { - Path lockInfoFile = getLockInfoFile(workspaceUrl); + Path lockInfoFile = Workbench.getLockInfoFile(workspaceUrl); if (!Files.exists(lockInfoFile)) { Files.createFile(lockInfoFile); } diff --git a/bundles/org.eclipse.ui.ide/src/org/eclipse/ui/internal/ide/IDEWorkbenchMessages.java b/bundles/org.eclipse.ui.ide/src/org/eclipse/ui/internal/ide/IDEWorkbenchMessages.java index 0a14c764d01..bec664b18a5 100644 --- a/bundles/org.eclipse.ui.ide/src/org/eclipse/ui/internal/ide/IDEWorkbenchMessages.java +++ b/bundles/org.eclipse.ui.ide/src/org/eclipse/ui/internal/ide/IDEWorkbenchMessages.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2005, 2017 IBM Corporation and others. + * Copyright (c) 2005, 2025 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -1050,8 +1050,6 @@ public class IDEWorkbenchMessages extends NLS { public static String IDEApplication_workspaceInvalidMessage; public static String IDEApplication_workspaceCannotBeSetTitle; public static String IDEApplication_workspaceCannotBeSetMessage; - public static String IDEApplication_workspaceCannotLockTitle; - public static String IDEApplication_workspaceCannotLockMessage; public static String IDEApplication_versionTitle_newerWorkspace; public static String IDEApplication_versionTitle_olderWorkspace; public static String IDEApplication_versionMessage_newerWorkspace; @@ -1147,13 +1145,7 @@ public class IDEWorkbenchMessages extends NLS { public static String WorkbenchPreference_maxSimultaneousBuilds; public static String WorkbenchPreference_maxSimultaneousBuildIntervalError; - - public static String IDEApplication_Ws_Lock_Owner_User; - public static String IDEApplication_Ws_Lock_Owner_Host; - public static String IDEApplication_Ws_Lock_Owner_Disp; - public static String IDEApplication_Ws_Lock_Owner_P_Id; public static String IDEApplication_Ws_Lock_Owner_Message; - static { // load message values from bundle file NLS.initializeMessages(BUNDLE_NAME, IDEWorkbenchMessages.class); diff --git a/bundles/org.eclipse.ui.ide/src/org/eclipse/ui/internal/ide/messages.properties b/bundles/org.eclipse.ui.ide/src/org/eclipse/ui/internal/ide/messages.properties index b79612e23ef..348c34e50b9 100644 --- a/bundles/org.eclipse.ui.ide/src/org/eclipse/ui/internal/ide/messages.properties +++ b/bundles/org.eclipse.ui.ide/src/org/eclipse/ui/internal/ide/messages.properties @@ -1,5 +1,5 @@ ############################################################################### -# Copyright (c) 2000, 2022 IBM Corporation and others. +# Copyright (c) 2000, 2025 IBM Corporation and others. # # This program and the accompanying materials # are made available under the terms of the Eclipse Public License 2.0 @@ -1074,8 +1074,6 @@ IDEApplication_workspaceInvalidTitle=Invalid Workspace IDEApplication_workspaceInvalidMessage=Selected workspace is not valid; choose a different one. IDEApplication_workspaceCannotBeSetTitle=Workspace Cannot Be Created IDEApplication_workspaceCannotBeSetMessage=Could not launch the product because the specified workspace cannot be created. The specified workspace directory is either invalid or read-only. -IDEApplication_workspaceCannotLockTitle=Workspace Cannot Be Locked -IDEApplication_workspaceCannotLockMessage=Could not launch the product because the associated workspace at ''{0}'' is currently in use by another Eclipse application. IDEApplication_versionTitle_olderWorkspace=Older Workspace Version IDEApplication_versionTitle_newerWorkspace=Newer Workspace Version IDEApplication_versionMessage_olderWorkspace=The ''{0}'' workspace was written with an older version. \ @@ -1152,8 +1150,4 @@ editorAssociationOverride_error_couldNotCreate_message=The ''{0}'' extension fro editorAssociationOverride_error_invalidElementName_message=An extension from plug-in ''{0}'' to the ''org.eclipse.ui.ide.editorAssociationOverride'' extension point was ignored because it contains the following invalid element: ''{1}''. editorAssociationOverride_error_invalidExtension_message=The ''{0}'' extension from plug-in ''{1}'' to the ''org.eclipse.ui.ide.editorAssociationOverride'' extension point will be ignored because it contains invalid attributes. -IDEApplication_Ws_Lock_Owner_User=User:\t\t{0}\n -IDEApplication_Ws_Lock_Owner_Host=Host:\t\t{0}\n -IDEApplication_Ws_Lock_Owner_Disp=Display:\t\t{0}\n -IDEApplication_Ws_Lock_Owner_P_Id=Process ID:\t{0}\n IDEApplication_Ws_Lock_Owner_Message=Workspace lock is currently held by:\n{0} diff --git a/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/internal/Workbench.java b/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/internal/Workbench.java index 690aa8ac9de..22f271cc7d0 100644 --- a/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/internal/Workbench.java +++ b/bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/internal/Workbench.java @@ -35,9 +35,12 @@ import java.io.InputStream; import java.io.StringReader; import java.io.StringWriter; +import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -49,6 +52,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Properties; import java.util.Set; import java.util.UUID; import java.util.concurrent.atomic.AtomicReference; @@ -80,6 +84,7 @@ import org.eclipse.core.runtime.SafeRunner; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.SubMonitor; +import org.eclipse.core.runtime.URIUtil; import org.eclipse.core.runtime.dynamichelpers.IExtensionTracker; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.core.runtime.preferences.ConfigurationScope; @@ -114,6 +119,7 @@ import org.eclipse.e4.ui.workbench.modeling.EModelService; import org.eclipse.e4.ui.workbench.modeling.EPartService; import org.eclipse.e4.ui.workbench.modeling.ISaveHandler; +import org.eclipse.e4.ui.workbench.swt.internal.copy.WorkbenchSWTMessages; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.util.EcoreUtil; @@ -2699,7 +2705,8 @@ private static String buildCommandLine(String workspace) { * as the workspace location. * * @param workspacePath the new workspace location - * @return {@link IApplication#EXIT_OK} or {@link IApplication#EXIT_RELAUNCH} + * @return {@link IApplication#EXIT_OK} or {@link IApplication#EXIT_RELAUNCH} or + * null */ @SuppressWarnings("restriction") public static Object setRestartArguments(String workspacePath) { @@ -2714,6 +2721,16 @@ public static Object setRestartArguments(String workspacePath) { if (command_line == null) { return IApplication.EXIT_OK; } + Path selectedWorkspace = Path.of(workspacePath); + try { + String workspaceLock = getWorkspaceLockDetails(selectedWorkspace.toUri().toURL()); + if (workspaceLock != null && !workspaceLock.isEmpty()) { + showWorkspaceLockedDialog(workspacePath, workspaceLock); + return null; + } + } catch (MalformedURLException e) { + return null; + } System.setProperty(Workbench.PROP_EXIT_CODE, IApplication.EXIT_RELAUNCH.toString()); System.setProperty(IApplicationContext.EXIT_DATA_PROPERTY, command_line); @@ -3684,4 +3701,72 @@ public void runWithInitialAutoScaleValue(Runnable runnable) { } } + + /** + * Extract the lock details of the selected workspace if it is locked by another + * Eclipse application + * + * @param workspaceUrl the URL of selected workspace + * @return String details of lock owned workspace, + * null or Empty if not locked + */ + @SuppressWarnings("restriction") + public static String getWorkspaceLockDetails(URL workspaceUrl) { + Path lockFile = getLockInfoFile(workspaceUrl); + if (lockFile != null && Files.exists(lockFile)) { + StringBuilder lockDetails = new StringBuilder(); + Properties properties = new Properties(); + try (InputStream is = Files.newInputStream(lockFile)) { + properties.load(is); + String prop = properties.getProperty("user"); //$NON-NLS-1$ + if (prop != null) { + lockDetails.append(NLS.bind(WorkbenchSWTMessages.IDEApplication_workspaceLockOwner, prop)); + } + prop = properties.getProperty("host"); //$NON-NLS-1$ + if (prop != null) { + lockDetails.append(NLS.bind(WorkbenchSWTMessages.IDEApplication_workspaceLockHost, prop)); + } + prop = properties.getProperty("display"); //$NON-NLS-1$ + if (prop != null) { + lockDetails.append(NLS.bind(WorkbenchSWTMessages.IDEApplication_workspaceLockDisplay, prop)); + } + prop = properties.getProperty("process-id"); //$NON-NLS-1$ + if (prop != null) { + lockDetails.append(NLS.bind(WorkbenchSWTMessages.IDEApplication_workspaceLockPID, prop)); + } + + } catch (IOException e) { + WorkbenchPlugin.log(e); + } + return lockDetails.toString(); + } + return null; + } + + /** + * Returns the lock file. + * + * @param workspaceUrl the URL of selected workspace + * @return the path to the .lock_info file within the specified + * workspace, or null if the workspace URL cannot be + * converted to a valid URI + */ + public static Path getLockInfoFile(URL workspaceUrl) { + Path lockFile = Path.of(".metadata", ".lock_info"); //$NON-NLS-1$ //$NON-NLS-2$ + try { + return Path.of(URIUtil.toURI(workspaceUrl)).resolve(lockFile); + } catch (URISyntaxException e) { + return null; + } + } + + @SuppressWarnings("restriction") + public static void showWorkspaceLockedDialog(String workspacePath, String workspaceLock) { + String lockMessage = NLS.bind(WorkbenchSWTMessages.IDEApplication_workspaceCannotLockMessage2, workspacePath); + String wsLockedError = lockMessage + System.lineSeparator() + System.lineSeparator() + + NLS.bind(WorkbenchSWTMessages.IDEApplication_workspaceLockMessage, workspaceLock); + + MessageDialog.openError(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(), + WorkbenchSWTMessages.IDEApplication_workspaceCannotLockTitle, wsLockedError); + } }