Skip to content

Commit e2ca711

Browse files
committed
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
1 parent 39d028e commit e2ca711

File tree

6 files changed

+118
-98
lines changed

6 files changed

+118
-98
lines changed

bundles/org.eclipse.e4.ui.workbench.swt/src/org/eclipse/e4/ui/workbench/swt/internal/copy/WorkbenchSWTMessages.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2005, 2019 IBM Corporation and others.
2+
* Copyright (c) 2005, 2025 IBM Corporation and others.
33
*
44
* This program and the accompanying materials
55
* are made available under the terms of the Eclipse Public License 2.0
@@ -42,6 +42,17 @@ public class WorkbenchSWTMessages extends NLS {
4242
public static String InternalError;
4343
public static String IDEApplication_versionTitle;
4444
public static String IDEApplication_versionMessage;
45+
public static String IDEApplication_workspaceLockMessage;
46+
47+
public static String IDEApplication_workspaceLockOwner;
48+
49+
public static String IDEApplication_workspaceLockHost;
50+
51+
public static String IDEApplication_workspaceLockDisplay;
52+
53+
public static String IDEApplication_workspaceLockPID;
54+
55+
public static String IDEApplication_workspaceCannotLockMessage2;
4556

4657
static {
4758
// load message values from bundle file

bundles/org.eclipse.e4.ui.workbench.swt/src/org/eclipse/e4/ui/workbench/swt/internal/copy/messages.properties

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
###############################################################################
2-
# Copyright (c) 2000, 2019 IBM Corporation and others.
2+
# Copyright (c) 2000, 2025 IBM Corporation and others.
33
#
44
# This program and the accompanying materials
55
# 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
4242
{0}\n\n\
4343
Updating the workspace may make it incompatible with other versions of the product.\n\
4444
Press OK to update the workspace and open it. Press Cancel to select a different workspace.
45+
IDEApplication_workspaceLockOwner=User:\t\t{0}\n
46+
IDEApplication_workspaceLockHost=Host:\t\t{0}\n
47+
IDEApplication_workspaceLockDisplay=Display:\t\t{0}\n
48+
IDEApplication_workspaceLockPID=Process ID:\t{0}\n
49+
IDEApplication_workspaceLockMessage=Workspace lock is currently held by:\n{0}
50+
IDEApplication_workspaceCannotLockMessage2=Could not switch to the selected workspace ''{0}'' because it is currently in use by another Eclipse instance.

bundles/org.eclipse.ui.ide.application/src/org/eclipse/ui/internal/ide/application/IDEApplication.java

Lines changed: 11 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2003, 2020 IBM Corporation and others.
2+
* Copyright (c) 2003, 2025 IBM Corporation and others.
33
*
44
* This program and the accompanying materials
55
* are made available under the terms of the Eclipse Public License 2.0
@@ -24,11 +24,9 @@
2424
import java.io.FileInputStream;
2525
import java.io.FileOutputStream;
2626
import java.io.IOException;
27-
import java.io.InputStream;
2827
import java.io.OutputStream;
2928
import java.net.InetAddress;
3029
import java.net.MalformedURLException;
31-
import java.net.URISyntaxException;
3230
import java.net.URL;
3331
import java.nio.file.Files;
3432
import java.nio.file.Path;
@@ -42,7 +40,6 @@
4240
import org.eclipse.core.runtime.OperationCanceledException;
4341
import org.eclipse.core.runtime.Platform;
4442
import org.eclipse.core.runtime.Status;
45-
import org.eclipse.core.runtime.URIUtil;
4643
import org.eclipse.core.runtime.jobs.Job;
4744
import org.eclipse.core.runtime.preferences.ConfigurationScope;
4845
import org.eclipse.equinox.app.IApplication;
@@ -89,8 +86,6 @@ public class IDEApplication implements IApplication, IExecutableExtension {
8986

9087
private static final String VERSION_FILENAME = "version.ini"; //$NON-NLS-1$
9188

92-
private static final Path LOCK_INFO_FILE = Path.of(METADATA_FOLDER, ".lock_info"); //$NON-NLS-1$
93-
9489
private static final String DISPLAY_VAR = "DISPLAY"; //$NON-NLS-1$
9590

9691
private static final String HOST_NAME_VAR = "HOSTNAME"; //$NON-NLS-1$
@@ -223,12 +218,13 @@ public void setInitializationData(IConfigurationElement config,
223218
}
224219

225220
/**
226-
* Return <code>null</code> if a valid workspace path has been set and an exit code otherwise.
227-
* Prompt for and set the path if possible and required.
221+
* Returns <code>null</code> if a valid workspace has been selected or locked
222+
* successfully, and an exit code otherwise. Prompts for and sets the workspace
223+
* path if required.
228224
*
229225
* @param applicationArguments the command line arguments
230-
* @return <code>null</code> if a valid instance location has been set and an exit code
231-
* otherwise
226+
* @return <code>null</code> if a valid instance location has been set and an
227+
* exit code otherwise
232228
*/
233229
@SuppressWarnings("rawtypes")
234230
protected Object checkInstanceLocation(Shell shell, Map applicationArguments) {
@@ -273,18 +269,11 @@ protected Object checkInstanceLocation(Shell shell, Map applicationArguments) {
273269
return EXIT_WORKSPACE_LOCKED;
274270
}
275271

276-
String wsLockedError = NLS.bind(IDEWorkbenchMessages.IDEApplication_workspaceCannotLockMessage,
277-
workspaceDirectory.getAbsolutePath());
278272
// check if there is a lock info then append it to error message.
279-
String lockInfo = getWorkspaceLockInfo(instanceLoc.getURL());
280-
if (lockInfo != null && !lockInfo.isBlank()) {
281-
wsLockedError = wsLockedError + System.lineSeparator() + System.lineSeparator()
282-
+ NLS.bind(IDEWorkbenchMessages.IDEApplication_Ws_Lock_Owner_Message, lockInfo);
273+
String lockInfo = Workbench.getWorkspaceLockDetails(instanceLoc.getURL());
274+
if (lockInfo != null) {
275+
Workbench.showWorkspaceLockedDialog(workspaceDirectory.getAbsolutePath(), lockInfo);
283276
}
284-
MessageDialog.openError(
285-
shell,
286-
IDEWorkbenchMessages.IDEApplication_workspaceCannotLockTitle,
287-
wsLockedError);
288277
} else {
289278
MessageDialog.openError(
290279
shell,
@@ -378,7 +367,7 @@ protected Object checkInstanceLocation(Shell shell, Map applicationArguments) {
378367
// by this point it has been determined that the workspace is
379368
// already in use -- force the user to choose again
380369

381-
String lockInfo = getWorkspaceLockInfo(workspaceUrl);
370+
String lockInfo = Workbench.getWorkspaceLockDetails(workspaceUrl);
382371

383372
MessageDialog dialog = new MessageDialog(null, IDEWorkbenchMessages.IDEApplication_workspaceInUseTitle,
384373
null, NLS.bind(IDEWorkbenchMessages.IDEApplication_workspaceInUseMessage, workspaceUrl.getFile()),
@@ -412,49 +401,6 @@ protected Control createCustomArea(Composite parent) {
412401
}
413402
}
414403

415-
/**
416-
* Read workspace lock file and parse all the properties present. Based on the
417-
* eclipse version and operating system some or all the properties may not
418-
* present. In such scenario it will return empty string.
419-
*
420-
* @return Previous lock owner details.
421-
*/
422-
protected String getWorkspaceLockInfo(URL workspaceUrl) {
423-
try {
424-
Path lockFile = getLockInfoFile(workspaceUrl);
425-
if (!Files.exists(lockFile)) {
426-
return null;
427-
}
428-
429-
StringBuilder sb = new StringBuilder();
430-
Properties props = new Properties();
431-
try (InputStream is = Files.newInputStream(lockFile)) {
432-
props.load(is);
433-
String prop = props.getProperty(USER);
434-
if (prop != null) {
435-
sb.append(NLS.bind(IDEWorkbenchMessages.IDEApplication_Ws_Lock_Owner_User, prop));
436-
}
437-
prop = props.getProperty(HOST);
438-
if (prop != null) {
439-
sb.append(NLS.bind(IDEWorkbenchMessages.IDEApplication_Ws_Lock_Owner_Host, prop));
440-
}
441-
prop = props.getProperty(DISPLAY);
442-
if (prop != null) {
443-
sb.append(NLS.bind(IDEWorkbenchMessages.IDEApplication_Ws_Lock_Owner_Disp, prop));
444-
}
445-
prop = props.getProperty(PROCESS_ID);
446-
if (prop != null) {
447-
sb.append(NLS.bind(IDEWorkbenchMessages.IDEApplication_Ws_Lock_Owner_P_Id, prop));
448-
}
449-
return sb.toString();
450-
}
451-
} catch (Exception e) {
452-
IDEWorkbenchPlugin.log("Could not read lock info file: ", e); //$NON-NLS-1$
453-
454-
}
455-
return null;
456-
}
457-
458404
/**
459405
* Write lock owner details onto workspace lock file. Data includes user, host,
460406
* display and current java process id.
@@ -532,28 +478,14 @@ private String getHostName() {
532478
return hostName;
533479
}
534480

535-
/**
536-
* Returns the .lock_info file. Does not check if it exists.
537-
*
538-
* @param workspaceUrl
539-
* @return .lock_info file.
540-
*/
541-
private static Path getLockInfoFile(URL workspaceUrl) {
542-
try {
543-
return Path.of(URIUtil.toURI(workspaceUrl)).resolve(LOCK_INFO_FILE);
544-
} catch (URISyntaxException e) {
545-
throw new IllegalArgumentException(e);
546-
}
547-
}
548-
549481
/**
550482
* Creates the .lock_info file if it does not exist.
551483
*
552484
* @param workspaceUrl
553485
* @return .lock_info file.
554486
*/
555487
private static Path createLockInfoFile(URL workspaceUrl) throws Exception {
556-
Path lockInfoFile = getLockInfoFile(workspaceUrl);
488+
Path lockInfoFile = Workbench.getLockInfoFile(workspaceUrl);
557489
if (!Files.exists(lockInfoFile)) {
558490
Files.createFile(lockInfoFile);
559491
}

bundles/org.eclipse.ui.ide/src/org/eclipse/ui/internal/ide/IDEWorkbenchMessages.java

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2005, 2017 IBM Corporation and others.
2+
* Copyright (c) 2005, 2025 IBM Corporation and others.
33
*
44
* This program and the accompanying materials
55
* are made available under the terms of the Eclipse Public License 2.0
@@ -1050,8 +1050,6 @@ public class IDEWorkbenchMessages extends NLS {
10501050
public static String IDEApplication_workspaceInvalidMessage;
10511051
public static String IDEApplication_workspaceCannotBeSetTitle;
10521052
public static String IDEApplication_workspaceCannotBeSetMessage;
1053-
public static String IDEApplication_workspaceCannotLockTitle;
1054-
public static String IDEApplication_workspaceCannotLockMessage;
10551053
public static String IDEApplication_versionTitle_newerWorkspace;
10561054
public static String IDEApplication_versionTitle_olderWorkspace;
10571055
public static String IDEApplication_versionMessage_newerWorkspace;
@@ -1147,13 +1145,7 @@ public class IDEWorkbenchMessages extends NLS {
11471145

11481146
public static String WorkbenchPreference_maxSimultaneousBuilds;
11491147
public static String WorkbenchPreference_maxSimultaneousBuildIntervalError;
1150-
1151-
public static String IDEApplication_Ws_Lock_Owner_User;
1152-
public static String IDEApplication_Ws_Lock_Owner_Host;
1153-
public static String IDEApplication_Ws_Lock_Owner_Disp;
1154-
public static String IDEApplication_Ws_Lock_Owner_P_Id;
11551148
public static String IDEApplication_Ws_Lock_Owner_Message;
1156-
11571149
static {
11581150
// load message values from bundle file
11591151
NLS.initializeMessages(BUNDLE_NAME, IDEWorkbenchMessages.class);

bundles/org.eclipse.ui.ide/src/org/eclipse/ui/internal/ide/messages.properties

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
###############################################################################
2-
# Copyright (c) 2000, 2022 IBM Corporation and others.
2+
# Copyright (c) 2000, 2025 IBM Corporation and others.
33
#
44
# This program and the accompanying materials
55
# are made available under the terms of the Eclipse Public License 2.0
@@ -1074,8 +1074,6 @@ IDEApplication_workspaceInvalidTitle=Invalid Workspace
10741074
IDEApplication_workspaceInvalidMessage=Selected workspace is not valid; choose a different one.
10751075
IDEApplication_workspaceCannotBeSetTitle=Workspace Cannot Be Created
10761076
IDEApplication_workspaceCannotBeSetMessage=Could not launch the product because the specified workspace cannot be created. The specified workspace directory is either invalid or read-only.
1077-
IDEApplication_workspaceCannotLockTitle=Workspace Cannot Be Locked
1078-
IDEApplication_workspaceCannotLockMessage=Could not launch the product because the associated workspace at ''{0}'' is currently in use by another Eclipse application.
10791077
IDEApplication_versionTitle_olderWorkspace=Older Workspace Version
10801078
IDEApplication_versionTitle_newerWorkspace=Newer Workspace Version
10811079
IDEApplication_versionMessage_olderWorkspace=The ''{0}'' workspace was written with an older version. \
@@ -1152,8 +1150,4 @@ editorAssociationOverride_error_couldNotCreate_message=The ''{0}'' extension fro
11521150
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}''.
11531151
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.
11541152

1155-
IDEApplication_Ws_Lock_Owner_User=User:\t\t{0}\n
1156-
IDEApplication_Ws_Lock_Owner_Host=Host:\t\t{0}\n
1157-
IDEApplication_Ws_Lock_Owner_Disp=Display:\t\t{0}\n
1158-
IDEApplication_Ws_Lock_Owner_P_Id=Process ID:\t{0}\n
11591153
IDEApplication_Ws_Lock_Owner_Message=Workspace lock is currently held by:\n{0}

bundles/org.eclipse.ui.workbench/eclipseui/org/eclipse/ui/internal/Workbench.java

Lines changed: 86 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,12 @@
3535
import java.io.InputStream;
3636
import java.io.StringReader;
3737
import java.io.StringWriter;
38+
import java.net.MalformedURLException;
3839
import java.net.URI;
3940
import java.net.URISyntaxException;
4041
import java.net.URL;
42+
import java.nio.file.Files;
43+
import java.nio.file.Path;
4144
import java.util.ArrayList;
4245
import java.util.Arrays;
4346
import java.util.Collection;
@@ -49,6 +52,7 @@
4952
import java.util.List;
5053
import java.util.Map;
5154
import java.util.Objects;
55+
import java.util.Properties;
5256
import java.util.Set;
5357
import java.util.UUID;
5458
import java.util.concurrent.atomic.AtomicReference;
@@ -80,6 +84,7 @@
8084
import org.eclipse.core.runtime.SafeRunner;
8185
import org.eclipse.core.runtime.Status;
8286
import org.eclipse.core.runtime.SubMonitor;
87+
import org.eclipse.core.runtime.URIUtil;
8388
import org.eclipse.core.runtime.dynamichelpers.IExtensionTracker;
8489
import org.eclipse.core.runtime.jobs.Job;
8590
import org.eclipse.core.runtime.preferences.ConfigurationScope;
@@ -114,6 +119,7 @@
114119
import org.eclipse.e4.ui.workbench.modeling.EModelService;
115120
import org.eclipse.e4.ui.workbench.modeling.EPartService;
116121
import org.eclipse.e4.ui.workbench.modeling.ISaveHandler;
122+
import org.eclipse.e4.ui.workbench.swt.internal.copy.WorkbenchSWTMessages;
117123
import org.eclipse.emf.ecore.EObject;
118124
import org.eclipse.emf.ecore.resource.Resource;
119125
import org.eclipse.emf.ecore.util.EcoreUtil;
@@ -2699,7 +2705,8 @@ private static String buildCommandLine(String workspace) {
26992705
* as the workspace location.
27002706
*
27012707
* @param workspacePath the new workspace location
2702-
* @return {@link IApplication#EXIT_OK} or {@link IApplication#EXIT_RELAUNCH}
2708+
* @return {@link IApplication#EXIT_OK} or {@link IApplication#EXIT_RELAUNCH} or
2709+
* <code>null</code>
27032710
*/
27042711
@SuppressWarnings("restriction")
27052712
public static Object setRestartArguments(String workspacePath) {
@@ -2714,6 +2721,16 @@ public static Object setRestartArguments(String workspacePath) {
27142721
if (command_line == null) {
27152722
return IApplication.EXIT_OK;
27162723
}
2724+
Path selectedWorkspace = Path.of(workspacePath);
2725+
try {
2726+
String workspaceLock = getWorkspaceLockDetails(selectedWorkspace.toUri().toURL());
2727+
if (workspaceLock != null && !workspaceLock.isEmpty()) {
2728+
showWorkspaceLockedDialog(workspacePath, workspaceLock);
2729+
return null;
2730+
}
2731+
} catch (MalformedURLException e) {
2732+
return null;
2733+
}
27172734

27182735
System.setProperty(Workbench.PROP_EXIT_CODE, IApplication.EXIT_RELAUNCH.toString());
27192736
System.setProperty(IApplicationContext.EXIT_DATA_PROPERTY, command_line);
@@ -3684,4 +3701,72 @@ public void runWithInitialAutoScaleValue(Runnable runnable) {
36843701
}
36853702

36863703
}
3704+
3705+
/**
3706+
* Extract the lock details of the selected workspace if it is locked by another
3707+
* Eclipse application
3708+
*
3709+
* @param workspaceUrl the <code>URL</code> of selected workspace
3710+
* @return <code>String</code> details of lock owned workspace,
3711+
* <code>null or Empty</code> if not locked
3712+
*/
3713+
@SuppressWarnings("restriction")
3714+
public static String getWorkspaceLockDetails(URL workspaceUrl) {
3715+
Path lockFile = getLockInfoFile(workspaceUrl);
3716+
if (lockFile != null && Files.exists(lockFile)) {
3717+
StringBuilder lockDetails = new StringBuilder();
3718+
Properties properties = new Properties();
3719+
try (InputStream is = Files.newInputStream(lockFile)) {
3720+
properties.load(is);
3721+
String prop = properties.getProperty("user"); //$NON-NLS-1$
3722+
if (prop != null) {
3723+
lockDetails.append(NLS.bind(WorkbenchSWTMessages.IDEApplication_workspaceLockOwner, prop));
3724+
}
3725+
prop = properties.getProperty("host"); //$NON-NLS-1$
3726+
if (prop != null) {
3727+
lockDetails.append(NLS.bind(WorkbenchSWTMessages.IDEApplication_workspaceLockHost, prop));
3728+
}
3729+
prop = properties.getProperty("display"); //$NON-NLS-1$
3730+
if (prop != null) {
3731+
lockDetails.append(NLS.bind(WorkbenchSWTMessages.IDEApplication_workspaceLockDisplay, prop));
3732+
}
3733+
prop = properties.getProperty("process-id"); //$NON-NLS-1$
3734+
if (prop != null) {
3735+
lockDetails.append(NLS.bind(WorkbenchSWTMessages.IDEApplication_workspaceLockPID, prop));
3736+
}
3737+
3738+
} catch (IOException e) {
3739+
WorkbenchPlugin.log(e);
3740+
}
3741+
return lockDetails.toString();
3742+
}
3743+
return null;
3744+
}
3745+
3746+
/**
3747+
* Returns the lock file.
3748+
*
3749+
* @param workspaceUrl the <code>URL</code> of selected workspace
3750+
* @return the path to the <code>.lock_info</code> file within the specified
3751+
* workspace, or <code> null</code> if the workspace URL cannot be
3752+
* converted to a valid URI
3753+
*/
3754+
public static Path getLockInfoFile(URL workspaceUrl) {
3755+
Path lockFile = Path.of(".metadata", ".lock_info"); //$NON-NLS-1$ //$NON-NLS-2$
3756+
try {
3757+
return Path.of(URIUtil.toURI(workspaceUrl)).resolve(lockFile);
3758+
} catch (URISyntaxException e) {
3759+
return null;
3760+
}
3761+
}
3762+
3763+
@SuppressWarnings("restriction")
3764+
public static void showWorkspaceLockedDialog(String workspacePath, String workspaceLock) {
3765+
String lockMessage = NLS.bind(WorkbenchSWTMessages.IDEApplication_workspaceCannotLockMessage2, workspacePath);
3766+
String wsLockedError = lockMessage + System.lineSeparator() + System.lineSeparator()
3767+
+ NLS.bind(WorkbenchSWTMessages.IDEApplication_workspaceLockMessage, workspaceLock);
3768+
3769+
MessageDialog.openError(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(),
3770+
WorkbenchSWTMessages.IDEApplication_workspaceCannotLockTitle, wsLockedError);
3771+
}
36873772
}

0 commit comments

Comments
 (0)