Skip to content

Commit

Permalink
Detect JVM installs at startup
Browse files Browse the repository at this point in the history
Fixes #230
  • Loading branch information
mickaelistria committed Jun 14, 2023
1 parent 4613cf2 commit 8a23f4f
Show file tree
Hide file tree
Showing 13 changed files with 236 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -364,9 +364,9 @@ private void testOutput(String mainTypeName, String vmArgs, String programArgs,
workingCopy.setAttribute(ILaunchManager.ATTR_ENVIRONMENT_VARIABLES, env);

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ public void testJRELibResolution() throws CoreException {
assertNotNull("no default JRE", vm);
LibraryLocation[] libs = JavaRuntime.getLibraryLocations(vm);
assertTrue("no default libs", libs.length > 0);
assertEquals("Should resolve to location of local JRE", libs[0].getSystemLibraryPath().toOSString().toLowerCase(), resolved[0].getPath().toOSString().toLowerCase());
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@
*******************************************************************************/
package org.eclipse.jdt.debug.tests.sourcelookup;

import java.io.File;
import java.util.Arrays;
import java.util.function.Predicate;
import java.util.Objects;
import java.util.stream.Stream;

import org.eclipse.core.resources.IncrementalProjectBuilder;
import org.eclipse.core.runtime.IPath;
Expand All @@ -29,6 +33,10 @@
import org.eclipse.jdt.debug.tests.AbstractDebugTest;
import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
import org.eclipse.jdt.internal.launching.JavaSourceLookupDirector;
import org.eclipse.jdt.launching.IVMInstall;
import org.eclipse.jdt.launching.JavaRuntime;
import org.eclipse.jdt.launching.environments.IExecutionEnvironment;
import org.junit.After;

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

private IExecutionEnvironment javaSE11;
private IVMInstall defaultJavaSE11VM;

public Bug565462Tests(String name) {
super(name);
}

@Override
protected void setUp() throws Exception {
super.setUp();
this.javaSE11 = JavaRuntime.getExecutionEnvironmentsManager().getEnvironment("JavaSE-11");
this.defaultJavaSE11VM = javaSE11.getDefaultVM();
Predicate<IVMInstall> hasSource = vm -> vm != null && vm.getInstallLocation() != null && new File(vm.getInstallLocation(), "lib/src.zip").isFile();
if (!hasSource.test(defaultJavaSE11VM)) {
Stream.of(javaSE11.getCompatibleVMs()).filter(Objects::nonNull).filter(hasSource)
.findAny()
.ifPresent(javaSE11::setDefaultVM);
}
assertTrue("Default VM doesn't have source", hasSource.test(javaSE11.getDefaultVM()));
}

@After
public void tearDown() throws Exception {
javaSE11.setDefaultVM(defaultJavaSE11VM);
super.tearDown();
}

/**
* Test for bug 565462.
*
Expand Down Expand Up @@ -78,8 +109,11 @@ public void testFindDuplicatesBug565462() throws Exception {
director.setFindDuplicates(true);

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

}

private static void removeModuleAttribute(IJavaProject javaProject) throws JavaModelException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -210,4 +210,6 @@ public class JREMessages extends NLS {
public static String LibraryLabelProvider_0;

public static String VMDetailsDialog_0;

public static String detectJREsAtStartup;
}
Original file line number Diff line number Diff line change
Expand Up @@ -142,3 +142,5 @@ VMExternalAnnsBlock_1=External annotations:
VMExternalAnnsBlock_2=(none)
VMExternalAnnsBlock_3=E&xternal annotations...
VMExternalAnnsBlock_4=Select to add the external annotations file or directory to the selected library

detectJREsAtStartup=Detect available JVM installations at startup
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import org.eclipse.jdt.debug.ui.IJavaDebugUIConstants;
import org.eclipse.jdt.internal.debug.ui.IJavaDebugHelpContextIds;
import org.eclipse.jdt.internal.debug.ui.JDIDebugUIPlugin;
import org.eclipse.jdt.internal.launching.LaunchingPlugin;
import org.eclipse.jdt.internal.launching.StandardVMType;
import org.eclipse.jdt.launching.AbstractVMInstall;
import org.eclipse.jdt.launching.IVMInstall;
Expand All @@ -49,13 +50,15 @@
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Link;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchPreferencePage;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.preferences.IWorkbenchPreferenceContainer;
import org.eclipse.ui.preferences.ScopedPreferenceStore;

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

private Button detectAtStartupCheckbox;

/**
* Constructor
*/
Expand All @@ -87,6 +92,8 @@ public JREsPreferencePage() {
*/
@Override
public void init(IWorkbench workbench) {
setPreferenceStore(new ScopedPreferenceStore(InstanceScope.INSTANCE,
LaunchingPlugin.getDefault().getBundle().getSymbolicName()));
}

/**
Expand Down Expand Up @@ -124,6 +131,9 @@ protected Control createContents(Composite ancestor) {
SWTFactory.createWrapLabel(ancestor, JREMessages.JREsPreferencePage_2, 1, 300);
SWTFactory.createVerticalSpacer(ancestor, 1);

detectAtStartupCheckbox = SWTFactory.createCheckButton(ancestor, JREMessages.detectJREsAtStartup, null, getPreferenceStore().getBoolean(LaunchingPlugin.PREF_DETECT_VMS_AT_STARTUP), 1);
SWTFactory.createVerticalSpacer(ancestor, 1);

fJREBlock = new InstalledJREsBlock();
fJREBlock.createControl(ancestor);
Control control = fJREBlock.getControl();
Expand Down Expand Up @@ -167,6 +177,7 @@ public void selectionChanged(SelectionChangedEvent event) {
}
}
});

applyDialogFont(ancestor);
return ancestor;
}
Expand Down Expand Up @@ -295,6 +306,7 @@ public void run() {
}
}
});
getPreferenceStore().setValue(LaunchingPlugin.PREF_DETECT_VMS_AT_STARTUP, detectAtStartupCheckbox.getSelection());

if(canceled[0]) {
return false;
Expand Down
2 changes: 1 addition & 1 deletion org.eclipse.jdt.launching/META-INF/MANIFEST.MF
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: %pluginName
Bundle-SymbolicName: org.eclipse.jdt.launching; singleton:=true
Bundle-Version: 3.20.0.qualifier
Bundle-Version: 3.20.100.qualifier
Bundle-Activator: org.eclipse.jdt.internal.launching.LaunchingPlugin
Bundle-Vendor: %providerName
Bundle-Localization: plugin
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
/*******************************************************************************
* Copyright (c) 2023 Red Hat, Inc. and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/
package org.eclipse.jdt.internal.launching;

import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jdt.launching.IVMInstall;
import org.eclipse.jdt.launching.IVMInstall2;
import org.eclipse.jdt.launching.IVMInstallType;
import org.eclipse.jdt.launching.JavaRuntime;
import org.eclipse.jdt.launching.VMStandin;

/**
* Lookup for VMs installed in standard or usual locations; and add the existing ones that
* are not yet known by JDT to the VM registry (usually visible in the "Installed JREs"
* preference page)
*/
class DetectVMInstallationsJob extends Job {

private static final Object FAMILY = DetectVMInstallationsJob.class;

private final StandardVMType standardType;

DetectVMInstallationsJob() {
super(LaunchingMessages.lookupInstalledJVMs);
this.standardType = (StandardVMType)JavaRuntime.getVMInstallType(StandardVMType.ID_STANDARD_VM_TYPE);
}

@Override
protected IStatus run(IProgressMonitor monitor) {
Collection<File> candidates = computeCandidateVMs();
if (monitor.isCanceled()) {
return Status.CANCEL_STATUS;
}
Set<File> knownVMs = knownVMs();
candidates.removeIf(knownVMs::contains);
monitor.beginTask(LaunchingMessages.lookupInstalledJVMs, candidates.size());
for (File f : candidates) {
if (monitor.isCanceled()) {
return Status.CANCEL_STATUS;
}
SubMonitor subMon = SubMonitor.convert(monitor, f.getAbsolutePath(), 1);
VMStandin workingCopy = new VMStandin(standardType, f.getAbsolutePath());
workingCopy.setInstallLocation(f);
String name = f.getName();
int i = 1;
while (isDuplicateName(name)) {
name = f.getName() + '(' + i++ + ')';
}
workingCopy.setName(name);
IVMInstall install = workingCopy.convertToRealVM();
if (!(install instanceof IVMInstall2 vm && vm.getJavaVersion() != null)) {
// worksaround: such VMs may cause issue later
// https://github.com/eclipse-jdt/eclipse.jdt.debug/issues/248
standardType.disposeVMInstall(install.getId());
}
subMon.done();
}
return Status.OK_STATUS;
}

private boolean isDuplicateName(String name) {
return Stream.of(JavaRuntime.getVMInstallTypes()) //
.flatMap(vmType -> Arrays.stream(vmType.getVMInstalls())) //
.map(IVMInstall::getName) //
.anyMatch(name::equals);
}

private Collection<File> computeCandidateVMs() {
// parent directories containing a collection of VM installations
Collection<File> rootDirectories = new HashSet<>();
if (!Platform.OS_WIN32.equals(Platform.getOS())) {
rootDirectories.add(new File("/usr/lib/jvm")); //$NON-NLS-1$
}
if (Platform.OS_MACOSX.equals(Platform.getOS())) {
rootDirectories.add(new File("/Library/Java/JavaVirtualMachines")); //$NON-NLS-1$
}
rootDirectories.add(new File(System.getProperty("user.home"), ".sdkman/candidates/java")); //$NON-NLS-1$ //$NON-NLS-2$

Set<File> directories = rootDirectories.stream().filter(File::isDirectory)
.map(dir -> dir.listFiles(File::isDirectory))
.flatMap(Arrays::stream)
.filter(Objects::nonNull)
.collect(Collectors.toSet());

// particular VM installations
String javaHome = System.getenv("JAVA_HOME"); //$NON-NLS-1$
if (javaHome != null) {
directories.add(new File(javaHome));
}
String jdkHome = System.getenv("JDK_HOME"); //$NON-NLS-1$
if (jdkHome != null) {
directories.add(new File(jdkHome));
}
// other common/standard lookup strategies can be added here

return directories.stream()
.filter(Objects::nonNull)
.filter(File::isDirectory)
.map(t -> {
try {
return t.getCanonicalFile();
} catch (IOException e) {
return null;
}
}).filter(Objects::nonNull)
.filter(location -> standardType.validateInstallLocation(location).isOK())
.collect(Collectors.toCollection(HashSet::new));
}

private static Set<File> knownVMs() {
return Stream.of(JavaRuntime.getVMInstallTypes())
.map(IVMInstallType::getVMInstalls)
.flatMap(Arrays::stream)
.map(IVMInstall::getInstallLocation)
.filter(Objects::nonNull)
.map(t -> {
try {
return t.getCanonicalFile();
} catch (IOException e) {
return null;
}
}).filter(Objects::nonNull)
.collect(Collectors.toSet());
}

@Override
public boolean belongsTo(Object family) {
return family.equals(FAMILY);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -249,4 +249,8 @@ public class LaunchingMessages extends NLS {

public static String RunnerBootpathPError;

public static String lookupInstalledJVMs;

public static String configuringJVM;

}
Original file line number Diff line number Diff line change
Expand Up @@ -207,4 +207,6 @@ RunnerBootpathError=Xbootclasspath option have been removed as not supported bey
RunnerBootpathPError=Xbootclasspath/p option have been removed as not supported beyond Java 8.
VMLogging_1=Restoring vm library location:
VMLogging_2=Creating Library with Java Install path:
VMLogging_3=Default Install retrieved:
VMLogging_3=Default Install retrieved:
lookupInstalledJVMs=Look up for installed JVMs
configuringJVM=Configuring installed JVM {0}
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
import org.eclipse.core.runtime.Plugin;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.core.runtime.preferences.DefaultScope;
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
import org.eclipse.core.runtime.preferences.IEclipsePreferences.PreferenceChangeEvent;
import org.eclipse.core.runtime.preferences.InstanceScope;
Expand Down Expand Up @@ -153,6 +154,7 @@ public class LaunchingPlugin extends Plugin implements DebugOptionsListener, IEc
private boolean fIgnoreVMDefPropertyChangeEvents = false;

private static final String EMPTY_STRING = ""; //$NON-NLS-1$
public static final String PREF_DETECT_VMS_AT_STARTUP = "detectVMsAtStartup"; //$NON-NLS-1$

/**
* Mapping of top-level VM installation directories to library info for that
Expand Down Expand Up @@ -582,6 +584,13 @@ public void saving(ISaveContext context1) throws CoreException {
ResourcesPlugin.getWorkspace().addResourceChangeListener(this, IResourceChangeEvent.PRE_DELETE | IResourceChangeEvent.PRE_CLOSE);
DebugPlugin.getDefault().getLaunchManager().addLaunchListener(this);
DebugPlugin.getDefault().addDebugEventListener(this);
boolean forcedDisableVMDetection = Boolean.getBoolean(DetectVMInstallationsJob.class.getSimpleName() + ".disabled"); //$NON-NLS-1$
IEclipsePreferences instanceNode = InstanceScope.INSTANCE.getNode(getBundle().getSymbolicName());
IEclipsePreferences defaultNode = DefaultScope.INSTANCE.getNode(getBundle().getSymbolicName());
boolean defaultValue = defaultNode.getBoolean(PREF_DETECT_VMS_AT_STARTUP, true);
if (!forcedDisableVMDetection && instanceNode.getBoolean(PREF_DETECT_VMS_AT_STARTUP, defaultValue)) {
new DetectVMInstallationsJob().schedule();
}

AdvancedSourceLookupSupport.start();
}
Expand Down Expand Up @@ -1351,4 +1360,5 @@ public static void trace(String option, String message, Throwable throwable) {
public static void trace(String message) {
trace(null, message, null);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ public void initializeDefaultPreferences() {
dnode.put(JavaRuntime.PREF_STRICTLY_COMPATIBLE_JRE_NOT_AVAILABLE, JavaCore.WARNING);
dnode.put(JavaRuntime.PREF_COMPILER_COMPLIANCE_DOES_NOT_MATCH_JRE, JavaCore.WARNING);
dnode.putBoolean(JavaRuntime.PREF_ONLY_INCLUDE_EXPORTED_CLASSPATH_ENTRIES, false);
dnode.putBoolean(LaunchingPlugin.PREF_DETECT_VMS_AT_STARTUP, true);
try {
dnode.flush();
} catch (BackingStoreException e) {
Expand Down
Loading

0 comments on commit 8a23f4f

Please sign in to comment.