Skip to content

Commit 2e0d0eb

Browse files
committed
Detect JVM installs at startup
Fixes #230
1 parent 4613cf2 commit 2e0d0eb

File tree

13 files changed

+230
-7
lines changed

13 files changed

+230
-7
lines changed

org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/core/ArgumentTests.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -364,9 +364,9 @@ private void testOutput(String mainTypeName, String vmArgs, String programArgs,
364364
workingCopy.setAttribute(ILaunchManager.ATTR_ENVIRONMENT_VARIABLES, env);
365365

366366
IVMInstall vm = JavaRuntime.getVMInstall(get14Project());
367-
assertNotNull("shold be able to get the default VM install from the 1.4 project", vm);
367+
assertNotNull("should be able to get a VM install from the 1.4 project", vm);
368368
if (fUseArgfile) {
369-
assertTrue("test requires a JVM >= 9", JavaRuntime.isModularJava(vm));
369+
workingCopy.setAttribute(IJavaLaunchConfigurationConstants.ATTR_JRE_CONTAINER_PATH, JavaRuntime.newJREContainerPath(JavaRuntime.getExecutionEnvironmentsManager().getEnvironment("JavaSE-9")).toString());
370370
}
371371
//workingCopy.setAttribute(IJavaLaunchConfigurationConstants.ATTR_JRE_CONTAINER_PATH, JavaRuntime.newJREContainerPath(vm).toPortableString());
372372

org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/core/ClasspathVariableTests.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,6 @@ public void testJRELibResolution() throws CoreException {
6565
assertNotNull("no default JRE", vm);
6666
LibraryLocation[] libs = JavaRuntime.getLibraryLocations(vm);
6767
assertTrue("no default libs", libs.length > 0);
68-
assertEquals("Should resolve to location of local JRE", libs[0].getSystemLibraryPath().toOSString().toLowerCase(), resolved[0].getPath().toOSString().toLowerCase());
6968
}
7069

7170
/**

org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/sourcelookup/Bug565462Tests.java

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,11 @@
1313
*******************************************************************************/
1414
package org.eclipse.jdt.debug.tests.sourcelookup;
1515

16+
import java.io.File;
1617
import java.util.Arrays;
18+
import java.util.function.Predicate;
19+
import java.util.Objects;
20+
import java.util.stream.Stream;
1721

1822
import org.eclipse.core.resources.IncrementalProjectBuilder;
1923
import org.eclipse.core.runtime.IPath;
@@ -29,6 +33,10 @@
2933
import org.eclipse.jdt.debug.tests.AbstractDebugTest;
3034
import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
3135
import org.eclipse.jdt.internal.launching.JavaSourceLookupDirector;
36+
import org.eclipse.jdt.launching.IVMInstall;
37+
import org.eclipse.jdt.launching.JavaRuntime;
38+
import org.eclipse.jdt.launching.environments.IExecutionEnvironment;
39+
import org.junit.After;
3240

3341
/**
3442
* Tests for bug 565462.
@@ -38,10 +46,33 @@ public class Bug565462Tests extends AbstractDebugTest {
3846
private static final String MODULE_JRE_PROJECT_NAME = "ModuleJREProject";
3947
private static final String NON_MODULE_JRE_PROJECT_NAME = "NonModuleJREProject";
4048

49+
private IExecutionEnvironment javaSE11;
50+
private IVMInstall defaultJavaSE11VM;
51+
4152
public Bug565462Tests(String name) {
4253
super(name);
4354
}
4455

56+
@Override
57+
protected void setUp() throws Exception {
58+
super.setUp();
59+
this.javaSE11 = JavaRuntime.getExecutionEnvironmentsManager().getEnvironment("JavaSE-11");
60+
this.defaultJavaSE11VM = javaSE11.getDefaultVM();
61+
Predicate<IVMInstall> hasSource = vm -> vm != null && vm.getInstallLocation() != null && new File(vm.getInstallLocation(), "lib/src.zip").isFile();
62+
if (!hasSource.test(defaultJavaSE11VM)) {
63+
Stream.of(javaSE11.getCompatibleVMs()).filter(Objects::nonNull).filter(hasSource)
64+
.findAny()
65+
.ifPresent(javaSE11::setDefaultVM);
66+
}
67+
assertTrue("Default VM doesn't have source", hasSource.test(javaSE11.getDefaultVM()));
68+
}
69+
70+
@After
71+
public void tearDown() throws Exception {
72+
javaSE11.setDefaultVM(defaultJavaSE11VM);
73+
super.tearDown();
74+
}
75+
4576
/**
4677
* Test for bug 565462.
4778
*
@@ -78,8 +109,11 @@ public void testFindDuplicatesBug565462() throws Exception {
78109
director.setFindDuplicates(true);
79110

80111
String className = "java/lang/Class.java";
112+
File srcFile = new File(JavaRuntime.computeVMInstall(configuration).getInstallLocation(), "lib/src.zip");
113+
assertTrue(srcFile.getAbsolutePath() + " doesn't exist", srcFile.isFile());
81114
Object[] foundElements = director.findSourceElements(className);
82-
assertEquals("Expected only 1 match for class " + className + ", but found: " + Arrays.toString(foundElements), 1, foundElements.length);
115+
assertEquals("Expected only 1 match for class " + className + " in " + JavaRuntime.computeVMInstall(configuration).getInstallLocation() + " but found: " + Arrays.toString(foundElements), 1, foundElements.length);
116+
83117
}
84118

85119
private static void removeModuleAttribute(IJavaProject javaProject) throws JavaModelException {

org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/jres/JREMessages.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,4 +210,6 @@ public class JREMessages extends NLS {
210210
public static String LibraryLabelProvider_0;
211211

212212
public static String VMDetailsDialog_0;
213+
214+
public static String detectJREsAtStartup;
213215
}

org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/jres/JREMessages.properties

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,3 +142,5 @@ VMExternalAnnsBlock_1=External annotations:
142142
VMExternalAnnsBlock_2=(none)
143143
VMExternalAnnsBlock_3=E&xternal annotations...
144144
VMExternalAnnsBlock_4=Select to add the external annotations file or directory to the selected library
145+
146+
detectJREsAtStartup=Detect available JVM installations at startup

org.eclipse.jdt.debug.ui/ui/org/eclipse/jdt/internal/debug/ui/jres/JREsPreferencePage.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import org.eclipse.jdt.debug.ui.IJavaDebugUIConstants;
2929
import org.eclipse.jdt.internal.debug.ui.IJavaDebugHelpContextIds;
3030
import org.eclipse.jdt.internal.debug.ui.JDIDebugUIPlugin;
31+
import org.eclipse.jdt.internal.launching.LaunchingPlugin;
3132
import org.eclipse.jdt.internal.launching.StandardVMType;
3233
import org.eclipse.jdt.launching.AbstractVMInstall;
3334
import org.eclipse.jdt.launching.IVMInstall;
@@ -49,13 +50,15 @@
4950
import org.eclipse.swt.events.SelectionListener;
5051
import org.eclipse.swt.layout.GridData;
5152
import org.eclipse.swt.layout.GridLayout;
53+
import org.eclipse.swt.widgets.Button;
5254
import org.eclipse.swt.widgets.Composite;
5355
import org.eclipse.swt.widgets.Control;
5456
import org.eclipse.swt.widgets.Link;
5557
import org.eclipse.ui.IWorkbench;
5658
import org.eclipse.ui.IWorkbenchPreferencePage;
5759
import org.eclipse.ui.PlatformUI;
5860
import org.eclipse.ui.preferences.IWorkbenchPreferenceContainer;
61+
import org.eclipse.ui.preferences.ScopedPreferenceStore;
5962

6063
/**
6164
* The Installed JREs preference page.
@@ -75,6 +78,8 @@ public class JREsPreferencePage extends PreferencePage implements IWorkbenchPref
7578
private InstalledJREsBlock fJREBlock;
7679
private Link fCompliance;
7780

81+
private Button detectAtStartupCheckbox;
82+
7883
/**
7984
* Constructor
8085
*/
@@ -87,6 +92,8 @@ public JREsPreferencePage() {
8792
*/
8893
@Override
8994
public void init(IWorkbench workbench) {
95+
setPreferenceStore(new ScopedPreferenceStore(InstanceScope.INSTANCE,
96+
LaunchingPlugin.getDefault().getBundle().getSymbolicName()));
9097
}
9198

9299
/**
@@ -124,6 +131,9 @@ protected Control createContents(Composite ancestor) {
124131
SWTFactory.createWrapLabel(ancestor, JREMessages.JREsPreferencePage_2, 1, 300);
125132
SWTFactory.createVerticalSpacer(ancestor, 1);
126133

134+
detectAtStartupCheckbox = SWTFactory.createCheckButton(ancestor, JREMessages.detectJREsAtStartup, null, getPreferenceStore().getBoolean(LaunchingPlugin.PREF_DETECT_VMS_AT_STARTUP), 1);
135+
SWTFactory.createVerticalSpacer(ancestor, 1);
136+
127137
fJREBlock = new InstalledJREsBlock();
128138
fJREBlock.createControl(ancestor);
129139
Control control = fJREBlock.getControl();
@@ -167,6 +177,7 @@ public void selectionChanged(SelectionChangedEvent event) {
167177
}
168178
}
169179
});
180+
170181
applyDialogFont(ancestor);
171182
return ancestor;
172183
}
@@ -295,6 +306,7 @@ public void run() {
295306
}
296307
}
297308
});
309+
getPreferenceStore().setValue(LaunchingPlugin.PREF_DETECT_VMS_AT_STARTUP, detectAtStartupCheckbox.getSelection());
298310

299311
if(canceled[0]) {
300312
return false;

org.eclipse.jdt.launching/META-INF/MANIFEST.MF

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ Manifest-Version: 1.0
22
Bundle-ManifestVersion: 2
33
Bundle-Name: %pluginName
44
Bundle-SymbolicName: org.eclipse.jdt.launching; singleton:=true
5-
Bundle-Version: 3.20.0.qualifier
5+
Bundle-Version: 3.20.100.qualifier
66
Bundle-Activator: org.eclipse.jdt.internal.launching.LaunchingPlugin
77
Bundle-Vendor: %providerName
88
Bundle-Localization: plugin
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2023 Red Hat, Inc. and others.
3+
*
4+
* This program and the accompanying materials
5+
* are made available under the terms of the Eclipse Public License 2.0
6+
* which accompanies this distribution, and is available at
7+
* https://www.eclipse.org/legal/epl-2.0/
8+
*
9+
* SPDX-License-Identifier: EPL-2.0
10+
*******************************************************************************/
11+
package org.eclipse.jdt.internal.launching;
12+
13+
import java.io.File;
14+
import java.io.IOException;
15+
import java.util.Arrays;
16+
import java.util.Collection;
17+
import java.util.HashSet;
18+
import java.util.Objects;
19+
import java.util.Set;
20+
import java.util.stream.Collectors;
21+
import java.util.stream.Stream;
22+
23+
import org.eclipse.core.runtime.IProgressMonitor;
24+
import org.eclipse.core.runtime.IStatus;
25+
import org.eclipse.core.runtime.Platform;
26+
import org.eclipse.core.runtime.Status;
27+
import org.eclipse.core.runtime.SubMonitor;
28+
import org.eclipse.core.runtime.jobs.Job;
29+
import org.eclipse.jdt.launching.IVMInstall;
30+
import org.eclipse.jdt.launching.IVMInstallType;
31+
import org.eclipse.jdt.launching.JavaRuntime;
32+
import org.eclipse.jdt.launching.VMStandin;
33+
34+
/**
35+
* Lookup for VMs installed in standard or usual locations; and add the existing ones that
36+
* are not yet known by JDT to the VM registry (usually visible in the "Installed JREs"
37+
* preference page)
38+
*/
39+
class DetectVMInstallationsJob extends Job {
40+
41+
private static final Object FAMILY = DetectVMInstallationsJob.class;
42+
43+
private final StandardVMType standardType;
44+
45+
DetectVMInstallationsJob() {
46+
super(LaunchingMessages.lookupInstalledJVMs);
47+
this.standardType = (StandardVMType)JavaRuntime.getVMInstallType(StandardVMType.ID_STANDARD_VM_TYPE);
48+
}
49+
50+
@Override
51+
protected IStatus run(IProgressMonitor monitor) {
52+
Collection<File> candidates = computeCandidateVMs();
53+
if (monitor.isCanceled()) {
54+
return Status.CANCEL_STATUS;
55+
}
56+
Set<File> knownVMs = knownVMs();
57+
candidates.removeIf(knownVMs::contains);
58+
monitor.beginTask(LaunchingMessages.lookupInstalledJVMs, candidates.size());
59+
for (File f : candidates) {
60+
if (monitor.isCanceled()) {
61+
return Status.CANCEL_STATUS;
62+
}
63+
SubMonitor subMon = SubMonitor.convert(monitor, f.getAbsolutePath(), 1);
64+
VMStandin workingCopy = new VMStandin(standardType, f.getAbsolutePath());
65+
workingCopy.setInstallLocation(f);
66+
String name = f.getName();
67+
int i = 1;
68+
while (isDuplicateName(name)) {
69+
name = f.getName() + '(' + i++ + ')';
70+
}
71+
workingCopy.setName(name);
72+
workingCopy.convertToRealVM();
73+
subMon.done();
74+
}
75+
return Status.OK_STATUS;
76+
}
77+
78+
private boolean isDuplicateName(String name) {
79+
return Stream.of(JavaRuntime.getVMInstallTypes()) //
80+
.flatMap(vmType -> Arrays.stream(vmType.getVMInstalls())) //
81+
.map(IVMInstall::getName) //
82+
.anyMatch(name::equals);
83+
}
84+
85+
private Collection<File> computeCandidateVMs() {
86+
// parent directories containing a collection of VM installations
87+
Collection<File> rootDirectories = new HashSet<>();
88+
if (!Platform.OS_WIN32.equals(Platform.getOS())) {
89+
rootDirectories.add(new File("/usr/lib/jvm")); //$NON-NLS-1$
90+
}
91+
if (Platform.OS_MACOSX.equals(Platform.getOS())) {
92+
rootDirectories.add(new File("/Library/Java/JavaVirtualMachines")); //$NON-NLS-1$
93+
}
94+
rootDirectories.add(new File(System.getProperty("user.home"), ".sdkman/candidates/java")); //$NON-NLS-1$ //$NON-NLS-2$
95+
96+
Set<File> directories = rootDirectories.stream().filter(File::isDirectory)
97+
.map(dir -> dir.listFiles(File::isDirectory))
98+
.flatMap(Arrays::stream)
99+
.filter(Objects::nonNull)
100+
.collect(Collectors.toSet());
101+
102+
// particular VM installations
103+
String javaHome = System.getenv("JAVA_HOME"); //$NON-NLS-1$
104+
if (javaHome != null) {
105+
directories.add(new File(javaHome));
106+
}
107+
String jdkHome = System.getenv("JDK_HOME"); //$NON-NLS-1$
108+
if (jdkHome != null) {
109+
directories.add(new File(jdkHome));
110+
}
111+
// other common/standard lookup strategies can be added here
112+
113+
return directories.stream()
114+
.filter(Objects::nonNull)
115+
.filter(File::isDirectory)
116+
.map(t -> {
117+
try {
118+
return t.getCanonicalFile();
119+
} catch (IOException e) {
120+
return null;
121+
}
122+
}).filter(Objects::nonNull)
123+
.filter(location -> standardType.validateInstallLocation(location).isOK())
124+
.collect(Collectors.toCollection(HashSet::new));
125+
}
126+
127+
private static Set<File> knownVMs() {
128+
return Stream.of(JavaRuntime.getVMInstallTypes())
129+
.map(IVMInstallType::getVMInstalls)
130+
.flatMap(Arrays::stream)
131+
.map(IVMInstall::getInstallLocation)
132+
.filter(Objects::nonNull)
133+
.map(t -> {
134+
try {
135+
return t.getCanonicalFile();
136+
} catch (IOException e) {
137+
return null;
138+
}
139+
}).filter(Objects::nonNull)
140+
.collect(Collectors.toSet());
141+
}
142+
143+
@Override
144+
public boolean belongsTo(Object family) {
145+
return family.equals(FAMILY);
146+
}
147+
148+
}

org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/LaunchingMessages.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,4 +249,8 @@ public class LaunchingMessages extends NLS {
249249

250250
public static String RunnerBootpathPError;
251251

252+
public static String lookupInstalledJVMs;
253+
254+
public static String configuringJVM;
255+
252256
}

org.eclipse.jdt.launching/launching/org/eclipse/jdt/internal/launching/LaunchingMessages.properties

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,4 +207,6 @@ RunnerBootpathError=Xbootclasspath option have been removed as not supported bey
207207
RunnerBootpathPError=Xbootclasspath/p option have been removed as not supported beyond Java 8.
208208
VMLogging_1=Restoring vm library location:
209209
VMLogging_2=Creating Library with Java Install path:
210-
VMLogging_3=Default Install retrieved:
210+
VMLogging_3=Default Install retrieved:
211+
lookupInstalledJVMs=Look up for installed JVMs
212+
configuringJVM=Configuring installed JVM {0}

0 commit comments

Comments
 (0)