Skip to content

Commit 9f3f9a9

Browse files
committed
Improve workspace lock error dialog.
Write workspace lock info like user, host, java process id, display properties onto a new file .lock_info if the lock was successful. Read the current lock data in case of lock was unsuccessful and show it in error dialog. If the .lock_info does not exist or the file has no info then nothing is shown. For older eclipse versions. see #2343
1 parent 1eead54 commit 9f3f9a9

File tree

3 files changed

+227
-2
lines changed

3 files changed

+227
-2
lines changed

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

Lines changed: 215 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,11 @@
2525
import java.io.FileOutputStream;
2626
import java.io.IOException;
2727
import java.io.OutputStream;
28+
import java.net.InetAddress;
2829
import java.net.MalformedURLException;
2930
import java.net.URL;
31+
import java.nio.file.Files;
32+
import java.nio.file.Path;
3033
import java.util.LinkedHashMap;
3134
import java.util.Map;
3235
import java.util.Properties;
@@ -49,7 +52,11 @@
4952
import org.eclipse.osgi.service.datalocation.Location;
5053
import org.eclipse.osgi.util.NLS;
5154
import org.eclipse.swt.SWT;
55+
import org.eclipse.swt.layout.FillLayout;
56+
import org.eclipse.swt.widgets.Composite;
57+
import org.eclipse.swt.widgets.Control;
5258
import org.eclipse.swt.widgets.Display;
59+
import org.eclipse.swt.widgets.Label;
5360
import org.eclipse.swt.widgets.Shell;
5461
import org.eclipse.ui.IWorkbench;
5562
import org.eclipse.ui.PlatformUI;
@@ -79,6 +86,22 @@ public class IDEApplication implements IApplication, IExecutableExtension {
7986

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

89+
private static final String LOCK_INFO_FILENAME = ".lock_info"; //$NON-NLS-1$
90+
91+
private static final String DISPLAY_VAR = "DISPLAY"; //$NON-NLS-1$
92+
93+
private static final String HOST_NAME_VAR = "HOSTNAME"; //$NON-NLS-1$
94+
95+
private static final String PROCESS_ID = "process-id"; //$NON-NLS-1$
96+
97+
private static final String DISPLAY = "display"; //$NON-NLS-1$
98+
99+
private static final String HOST = "host"; //$NON-NLS-1$
100+
101+
private static final String USER = "user"; //$NON-NLS-1$
102+
103+
private static final String USER_NAME = "user.name"; //$NON-NLS-1$
104+
82105
// Use the branding plug-in of the platform feature since this is most likely
83106
// to change on an update of the IDE.
84107
private static final String WORKSPACE_CHECK_REFERENCE_BUNDLE_NAME = "org.eclipse.platform"; //$NON-NLS-1$
@@ -225,6 +248,7 @@ protected Object checkInstanceLocation(Shell shell, Map applicationArguments) {
225248
try {
226249
if (instanceLoc.lock()) {
227250
writeWorkspaceVersion();
251+
writeWsLockInfo();
228252
return null;
229253
}
230254

@@ -237,10 +261,19 @@ protected Object checkInstanceLocation(Shell shell, Map applicationArguments) {
237261
if (isDevLaunchMode(applicationArguments)) {
238262
return EXIT_WORKSPACE_LOCKED;
239263
}
264+
265+
String wsLockedError = NLS.bind(IDEWorkbenchMessages.IDEApplication_workspaceCannotLockMessage,
266+
workspaceDirectory.getAbsolutePath());
267+
// check if there is a lock info then append it to error message.
268+
String lockInfo = getWorkspaceLockInfo(instanceLoc.getURL());
269+
if (lockInfo != null && !lockInfo.isBlank()) {
270+
wsLockedError = wsLockedError + System.lineSeparator() + System.lineSeparator()
271+
+ NLS.bind(IDEWorkbenchMessages.IDEApplication_Ws_Lock_Owner_Message, lockInfo);
272+
}
240273
MessageDialog.openError(
241274
shell,
242275
IDEWorkbenchMessages.IDEApplication_workspaceCannotLockTitle,
243-
NLS.bind(IDEWorkbenchMessages.IDEApplication_workspaceCannotLockMessage, workspaceDirectory.getAbsolutePath()));
276+
wsLockedError);
244277
} else {
245278
MessageDialog.openError(
246279
shell,
@@ -313,6 +346,7 @@ protected Object checkInstanceLocation(Shell shell, Map applicationArguments) {
313346
if (instanceLoc.set(workspaceUrl, true)) {
314347
launchData.writePersistedData();
315348
writeWorkspaceVersion();
349+
writeWsLockInfo();
316350
return null;
317351
}
318352
} catch (IllegalStateException e) {
@@ -332,17 +366,196 @@ protected Object checkInstanceLocation(Shell shell, Map applicationArguments) {
332366

333367
// by this point it has been determined that the workspace is
334368
// already in use -- force the user to choose again
369+
370+
String lockInfo = getWorkspaceLockInfo(workspaceUrl);
371+
335372
MessageDialog dialog = new MessageDialog(null, IDEWorkbenchMessages.IDEApplication_workspaceInUseTitle,
336373
null, NLS.bind(IDEWorkbenchMessages.IDEApplication_workspaceInUseMessage, workspaceUrl.getFile()),
337374
MessageDialog.ERROR, 1, IDEWorkbenchMessages.IDEApplication_workspaceInUse_Retry,
338-
IDEWorkbenchMessages.IDEApplication_workspaceInUse_Choose);
375+
IDEWorkbenchMessages.IDEApplication_workspaceInUse_Choose) {
376+
@Override
377+
protected Control createCustomArea(Composite parent) {
378+
if (lockInfo == null || lockInfo.isBlank()) {
379+
return null;
380+
}
381+
382+
Composite container = new Composite(parent, SWT.NONE);
383+
container.setLayout(new FillLayout());
384+
385+
Label multiLineText = new Label(container, SWT.NONE);
386+
multiLineText.setText(NLS.bind(IDEWorkbenchMessages.IDEApplication_Ws_Lock_Owner_Message, lockInfo));
387+
388+
return container;
389+
}
390+
};
339391
// the return value influences the next loop's iteration
340392
returnValue = dialog.open();
341393
// Remember the locked workspace as recent workspace
342394
launchData.writePersistedData();
343395
}
344396
}
345397

398+
/**
399+
* Read workspace lock file and parse all the properties present. Based on the
400+
* eclipse version and operating system some or all the properties may not
401+
* present. In such scenario it will return empty string.
402+
*
403+
* @return Previous lock owner details.
404+
*/
405+
private String getWorkspaceLockInfo(URL workspaceUrl) {
406+
File lockFile = getLockInfoFile(workspaceUrl, false);
407+
if (lockFile == null) {
408+
return null;
409+
}
410+
StringBuilder sb = new StringBuilder();
411+
Properties props = new Properties();
412+
try (FileInputStream is = new FileInputStream(lockFile)) {
413+
props.load(is);
414+
String prop = props.getProperty(USER);
415+
if (prop != null) {
416+
sb.append(NLS.bind(IDEWorkbenchMessages.IDEApplication_Ws_Lock_Owner_User, prop));
417+
}
418+
prop = props.getProperty(HOST);
419+
if (prop != null) {
420+
sb.append(NLS.bind(IDEWorkbenchMessages.IDEApplication_Ws_Lock_Owner_Host, prop));
421+
}
422+
prop = props.getProperty(DISPLAY);
423+
if (prop != null) {
424+
sb.append(NLS.bind(IDEWorkbenchMessages.IDEApplication_Ws_Lock_Owner_Display, prop));
425+
}
426+
prop = props.getProperty(PROCESS_ID);
427+
if (prop != null) {
428+
sb.append(NLS.bind(IDEWorkbenchMessages.IDEApplication_Ws_Lock_Owner_Process_Id, prop));
429+
}
430+
return sb.toString();
431+
} catch (Exception e) {
432+
IDEWorkbenchPlugin.log("Could not read lock file: ", e); //$NON-NLS-1$
433+
}
434+
return null;
435+
}
436+
437+
/**
438+
* Write lock owner details onto workspace lock file. Data includes user, host,
439+
* display and current java process id.
440+
*/
441+
protected void writeWsLockInfo() {
442+
Properties props = new Properties();
443+
444+
String user = System.getProperty(USER_NAME);
445+
if (user != null) {
446+
props.setProperty(USER, user);
447+
}
448+
String host = getHostName();
449+
if (host != null) {
450+
props.setProperty(HOST, host);
451+
}
452+
String display = getDisplay();
453+
if (display != null) {
454+
props.setProperty(DISPLAY, display);
455+
}
456+
String pid = getProcessId();
457+
if (pid != null) {
458+
props.setProperty(PROCESS_ID, pid);
459+
}
460+
461+
if (props.isEmpty()) {
462+
return;
463+
}
464+
465+
Location instanceLoc = Platform.getInstanceLocation();
466+
if (instanceLoc == null || instanceLoc.isReadOnly()) {
467+
return;
468+
}
469+
470+
File lockFile = getLockInfoFile(instanceLoc.getURL(), true);
471+
if (lockFile == null) {
472+
return;
473+
}
474+
475+
if (lockFile == null || !lockFile.exists()) {
476+
return;
477+
}
478+
479+
try (OutputStream output = new FileOutputStream(lockFile)) {
480+
props.store(output, null);
481+
} catch (IOException e) {
482+
IDEWorkbenchPlugin.log("Could not modify lock file", e); //$NON-NLS-1$
483+
}
484+
}
485+
486+
private String getDisplay() {
487+
String displayEnv = null;
488+
try {
489+
displayEnv = System.getenv(DISPLAY_VAR);
490+
} catch (Exception e) {
491+
IDEWorkbenchPlugin.log("Failed to read DISPLAY variable.", e); //$NON-NLS-1$
492+
}
493+
return displayEnv;
494+
}
495+
496+
private String getProcessId() {
497+
Long pid = null;
498+
try {
499+
pid = ProcessHandle.current().pid();
500+
} catch (Exception e) {
501+
IDEWorkbenchPlugin.log("Failed to read Java process id.", e); //$NON-NLS-1$
502+
}
503+
return pid != null ? pid.toString() : null;
504+
}
505+
506+
private String getHostName() {
507+
String hostName = null;
508+
509+
// Try fast approach first. Some OS(Like Linux) has HOSTNAME environment
510+
// variable set.
511+
try {
512+
hostName = System.getenv(HOST_NAME_VAR);
513+
if (hostName != null && !hostName.isEmpty()) {
514+
return hostName;
515+
}
516+
} catch (Exception e) {
517+
// Ignore here because we will try another method in the next step.
518+
}
519+
520+
try {
521+
hostName = InetAddress.getLocalHost().getHostName();
522+
} catch (Exception e) {
523+
IDEWorkbenchPlugin.log("Failed to read host name.", e); //$NON-NLS-1$
524+
}
525+
return hostName;
526+
}
527+
528+
/**
529+
* Returns the .lock_info file if it is present or it will create new file.
530+
*
531+
* @param workspaceUrl
532+
* @return Create .lock file if it does not present and return.
533+
*/
534+
private File getLockInfoFile(URL workspaceUrl, boolean create) {
535+
if (workspaceUrl == null) {
536+
return null;
537+
}
538+
Path lockInfoPath = Path.of(workspaceUrl.getPath(), METADATA_FOLDER, LOCK_INFO_FILENAME);
539+
540+
if (Files.exists(lockInfoPath)) {
541+
return lockInfoPath.toFile();
542+
}
543+
544+
try {
545+
if (create) {
546+
Path createdPath = Files.createFile(lockInfoPath);
547+
if (createdPath != null) {
548+
return createdPath.toFile();
549+
}
550+
}
551+
} catch (IOException e) {
552+
IDEWorkbenchPlugin.log("Failed to create workspace lock file.", e); //$NON-NLS-1$
553+
return null;
554+
}
555+
556+
return null;
557+
}
558+
346559
@SuppressWarnings("rawtypes")
347560
private static boolean isDevLaunchMode(Map args) {
348561
// see org.eclipse.pde.internal.core.PluginPathFinder.isDevLaunchMode()

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1147,6 +1147,12 @@ public class IDEWorkbenchMessages extends NLS {
11471147
public static String WorkbenchPreference_maxSimultaneousBuilds;
11481148
public static String WorkbenchPreference_maxSimultaneousBuildIntervalError;
11491149

1150+
public static String IDEApplication_Ws_Lock_Owner_User;
1151+
public static String IDEApplication_Ws_Lock_Owner_Host;
1152+
public static String IDEApplication_Ws_Lock_Owner_Display;
1153+
public static String IDEApplication_Ws_Lock_Owner_Process_Id;
1154+
public static String IDEApplication_Ws_Lock_Owner_Message;
1155+
11501156
static {
11511157
// load message values from bundle file
11521158
NLS.initializeMessages(BUNDLE_NAME, IDEWorkbenchMessages.class);

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1150,3 +1150,9 @@ OpenDelayedUrlAction_title=Open URL
11501150
editorAssociationOverride_error_couldNotCreate_message=The ''{0}'' extension from plug-in ''{1}'' to the ''org.eclipse.ui.ide.editorAssociationOverride'' extension point failed to load the editor association override class.
11511151
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}''.
11521152
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.
1153+
1154+
IDEApplication_Ws_Lock_Owner_User=User: {0}\n
1155+
IDEApplication_Ws_Lock_Owner_Host=Host: {0}\n
1156+
IDEApplication_Ws_Lock_Owner_Display=Display: {0}\n
1157+
IDEApplication_Ws_Lock_Owner_Process_Id=Process ID: {0}\n
1158+
IDEApplication_Ws_Lock_Owner_Message=Workspace lock is currently held by:\n{0}

0 commit comments

Comments
 (0)