Bug 551480 - java.lang.module.FindException: Module jdk.crypto.ec not
found, required by jdk.crypto.cryptoki
Change-Id: Iaaa2588fcce02f4b776d5af2140115c5f5751a57
diff --git a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/AutomatedSuite.java b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/AutomatedSuite.java
index 7c73eb5..8ed5ac9 100644
--- a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/AutomatedSuite.java
+++ b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/AutomatedSuite.java
@@ -77,6 +77,7 @@
import org.eclipse.jdt.debug.tests.core.LineTrackerTests;
import org.eclipse.jdt.debug.tests.core.LiteralTests17;
import org.eclipse.jdt.debug.tests.core.LocalVariableTests;
+import org.eclipse.jdt.debug.tests.core.ModuleOptionsTests;
import org.eclipse.jdt.debug.tests.core.ProcessTests;
import org.eclipse.jdt.debug.tests.core.RuntimeClasspathEntryTests;
import org.eclipse.jdt.debug.tests.core.StaticVariableTests;
@@ -233,6 +234,9 @@
addTest(new TestSuite(BootpathTests.class));
}
addTest(new TestSuite(EEDefinitionTests.class));
+ if (JavaProjectHelper.isJava9Compatible()) {
+ addTest(new TestSuite(ModuleOptionsTests.class));
+ }
//VM Install/Environment tests
addTest(new TestSuite(VMInstallTests.class));
diff --git a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/core/ModuleOptionsTests.java b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/core/ModuleOptionsTests.java
new file mode 100644
index 0000000..4aadcbd
--- /dev/null
+++ b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/core/ModuleOptionsTests.java
@@ -0,0 +1,182 @@
+/*******************************************************************************
+ * Copyright (c) 2019 GK Software SE 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
+ *
+ * Contributors:
+ * Stephan Herrmann - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.debug.tests.core;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.eclipse.debug.core.ILaunchConfiguration;
+import org.eclipse.jdt.core.IClasspathAttribute;
+import org.eclipse.jdt.core.IClasspathEntry;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.debug.tests.AbstractDebugTest;
+import org.eclipse.jdt.launching.JavaRuntime;
+
+public class ModuleOptionsTests extends AbstractDebugTest {
+
+ private static final String ASSUMED_DEFAULT_MODULES_9 = "java.se," //
+ + "javafx.base,javafx.controls,javafx.fxml,javafx.graphics,javafx.media,javafx.swing,javafx.web," // REMOVED in 10
+ + "jdk.accessibility,jdk.attach,jdk.compiler,jdk.dynalink,jdk.httpserver,"//
+ + "jdk.incubator.httpclient," //
+ + "jdk.jartool,jdk.javadoc,jdk.jconsole,jdk.jdi,"//
+ + "jdk.jfr," // intermittent
+ + "jdk.jshell,jdk.jsobject,jdk.management,"//
+ + "jdk.management.cmm,jdk.management.jfr,jdk.management.resource," // REMOVED later
+ + "jdk.net," //
+ + "jdk.packager,jdk.packager.services,jdk.plugin.dom," // NOT in openjdk
+ + "jdk.scripting.nashorn,jdk.sctp,jdk.security.auth,jdk.security.jgss,jdk.unsupported," //
+ + "jdk.xml.dom,"//
+ + "oracle.desktop,oracle.net"; // NOT in openjdk
+ private static final String ASSUMED_DEFAULT_MODULES_10 = "java.se," //
+ + "jdk.accessibility,jdk.attach,jdk.compiler,jdk.dynalink,jdk.httpserver," //
+ + "jdk.incubator.httpclient," // REMOVED later
+ + "jdk.jartool,jdk.javadoc,jdk.jconsole,jdk.jdi," //
+ + "jdk.jshell,jdk.jsobject,jdk.management," //
+ + "jdk.net," //
+ + "jdk.scripting.nashorn,jdk.sctp,jdk.security.auth,jdk.security.jgss,jdk.unsupported," //
+ + "jdk.xml.dom";
+ private static final String ASSUMED_DEFAULT_MODULES_12 = "java.se," //
+ + "jdk.accessibility,jdk.attach,jdk.compiler,jdk.dynalink,jdk.httpserver," //
+ + "jdk.jartool,jdk.javadoc,jdk.jconsole,jdk.jdi," //
+ + "jdk.jfr," // intermittent
+ + "jdk.jshell,jdk.jsobject,jdk.management," //
+ + "jdk.management.jfr," // intermittent
+ + "jdk.net," //
+ + "jdk.scripting.nashorn,jdk.sctp,jdk.security.auth,jdk.security.jgss,jdk.unsupported," //
+ + "jdk.unsupported.desktop," // NEW
+ + "jdk.xml.dom";
+
+ public ModuleOptionsTests(String name) {
+ super(name);
+ }
+
+ @Override
+ protected IJavaProject getProjectContext() {
+ return get9Project();
+ }
+
+ protected void addClasspathAttributesToSystemLibrary(IJavaProject project, IClasspathAttribute[] extraAttributes) throws JavaModelException {
+ IClasspathEntry[] rawClasspath = project.getRawClasspath();
+ int i = indexOfJREContainer(rawClasspath);
+ rawClasspath[i] = JavaCore.newContainerEntry(rawClasspath[i].getPath(), null, extraAttributes, false);
+ project.setRawClasspath(rawClasspath, null);
+ waitForBuild();
+ }
+
+ protected void removeClasspathAttributesFromSystemLibrary(IJavaProject project) throws JavaModelException {
+ IClasspathEntry[] rawClasspath = project.getRawClasspath();
+ int i = indexOfJREContainer(rawClasspath);
+ rawClasspath[i] = JavaCore.newContainerEntry(rawClasspath[i].getPath(), null, new IClasspathAttribute[0], false);
+ project.setRawClasspath(rawClasspath, null);
+ waitForBuild();
+ }
+
+ private List<String> getDefaultModules(IJavaProject javaProject) throws JavaModelException {
+ IClasspathEntry[] rawClasspath = javaProject.getRawClasspath();
+ int i = indexOfJREContainer(rawClasspath);
+ List<String> list = JavaCore.defaultRootModules(Arrays.asList(javaProject.findUnfilteredPackageFragmentRoots(rawClasspath[i])));
+ list.sort(String::compareTo);
+ return list;
+ }
+
+ private int indexOfJREContainer(IClasspathEntry[] rawClasspath) {
+ for (int i = 0; i < rawClasspath.length; i++) {
+ IClasspathEntry classpathEntry = rawClasspath[i];
+ if (classpathEntry.getEntryKind() == IClasspathEntry.CPE_CONTAINER
+ && JavaRuntime.JRE_CONTAINER.equals(classpathEntry.getPath().segment(0))) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ public void testAddModules1() throws JavaModelException {
+ IJavaProject javaProject = getProjectContext();
+ List<String> defaultModules = getDefaultModules(javaProject);
+ defaultModules.add("jdk.crypto.cryptoki"); // requires jdk.crypto.ec
+ try {
+ IClasspathAttribute[] attributes = {
+ JavaCore.newClasspathAttribute(IClasspathAttribute.LIMIT_MODULES, String.join(",", defaultModules))
+ };
+ addClasspathAttributesToSystemLibrary(javaProject, attributes);
+
+ ILaunchConfiguration launchConfiguration = getLaunchConfiguration(javaProject, "LogicalStructures");
+ String cliOptions = JavaRuntime.getModuleCLIOptions(launchConfiguration);
+ assertEquals("Unexpectd cli options", "--add-modules jdk.crypto.cryptoki,jdk.crypto.ec", cliOptions);
+ } finally {
+ removeClasspathAttributesFromSystemLibrary(javaProject);
+ }
+ }
+
+ public void testLimitModules1() throws JavaModelException {
+ IJavaProject javaProject = getProjectContext();
+ List<String> defaultModules = getDefaultModules(javaProject);
+ String expectedModules;
+ switch (String.join(",", defaultModules)) {
+ case ASSUMED_DEFAULT_MODULES_9:
+ expectedModules = "java.se," //
+ + "javafx.fxml,javafx.swing,javafx.web," //
+ + "jdk.accessibility,jdk.httpserver,jdk.incubator.httpclient,"
+ + "jdk.jartool,jdk.jconsole,jdk.jshell," //
+ + "jdk.management.cmm,jdk.management.jfr,jdk.management.resource," //
+ + "jdk.net," //
+ + "jdk.packager,jdk.packager.services,jdk.plugin.dom," //
+ + "jdk.scripting.nashorn,jdk.sctp,"
+ + "jdk.security.auth,jdk.security.jgss,jdk.unsupported," //
+ + "oracle.desktop,oracle.net";
+ break;
+ case ASSUMED_DEFAULT_MODULES_10:
+ expectedModules = "java.se," //
+ + "jdk.accessibility,jdk.httpserver,jdk.incubator.httpclient," //
+ + "jdk.jartool,jdk.jconsole,jdk.jshell," //
+ + "jdk.jsobject," // previously pulled in via javafx.*
+ + "jdk.net," //
+ + "jdk.scripting.nashorn,jdk.sctp," //
+ + "jdk.security.auth,jdk.security.jgss,jdk.unsupported," //
+ + "jdk.xml.dom";
+ break;
+ case ASSUMED_DEFAULT_MODULES_12:
+ expectedModules = "java.se," //
+ + "jdk.accessibility,jdk.httpserver," //
+ + "jdk.jartool,jdk.jconsole,jdk.jshell," //
+ + "jdk.jsobject," //
+ + "jdk.management.jfr," // intermittent
+ + "jdk.net," //
+ + "jdk.scripting.nashorn,jdk.sctp," //
+ + "jdk.security.auth,jdk.security.jgss,jdk.unsupported," //
+ + "jdk.unsupported.desktop," // NEW
+ + "jdk.xml.dom";
+ break;
+ default:
+ fail("Unknown set of default modules " + String.join(",", defaultModules));
+ return;
+ }
+ if (!defaultModules.remove("jdk.javadoc")) { // requires java.compiler and jdk.compiler but is required by no default module
+ fail("expected module was not in defaultModules");
+ }
+ try {
+ IClasspathAttribute[] attributes = {
+ JavaCore.newClasspathAttribute(IClasspathAttribute.LIMIT_MODULES, String.join(",", defaultModules)) };
+ addClasspathAttributesToSystemLibrary(javaProject, attributes);
+
+ ILaunchConfiguration launchConfiguration = getLaunchConfiguration(javaProject, "LogicalStructures");
+ String cliOptions = JavaRuntime.getModuleCLIOptions(launchConfiguration);
+ assertEquals("Unexpectd cli options", "--limit-modules " + expectedModules, cliOptions);
+ } finally {
+ removeClasspathAttributesFromSystemLibrary(javaProject);
+ }
+ }
+}
diff --git a/org.eclipse.jdt.launching/launching/org/eclipse/jdt/launching/JavaRuntime.java b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/launching/JavaRuntime.java
index 8b4d0cb..c9c9ec8 100644
--- a/org.eclipse.jdt.launching/launching/org/eclipse/jdt/launching/JavaRuntime.java
+++ b/org.eclipse.jdt.launching/launching/org/eclipse/jdt/launching/JavaRuntime.java
@@ -35,7 +35,9 @@
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
+import java.util.stream.Collectors;
import javax.xml.parsers.DocumentBuilder;
@@ -3460,7 +3462,7 @@
}
}
catch (CoreException e) {
- e.printStackTrace();
+ LaunchingPlugin.log(e);
}
return cliOptionString.toString().trim();
}
@@ -3541,7 +3543,7 @@
try {
absPaths[i] = toAbsolutePath(resource, root);
} catch (JavaModelException e) {
- // JavaPlugin.log(e);
+ LaunchingPlugin.log(e);
}
if (absPaths[i] == null) {
absPaths[i] = paths[i];
@@ -3576,18 +3578,25 @@
Set<String> selected = new HashSet<>(Arrays.asList(modules));
List<IPackageFragmentRoot> allSystemRoots = Arrays.asList(prj.findUnfilteredPackageFragmentRoots(systemLibrary));
Set<String> defaultModules = getDefaultModules(allSystemRoots);
- Set<String> limit = new HashSet<>(defaultModules);
- if (limit.retainAll(selected)) { // limit = selected ∩ default -- only add the option, if limit ⊂ default
+ Set<String> limit = new HashSet<>(defaultModules); // contains some redundancy, but is no full closure
+
+ // selected contains the minimal representation, now compute the transitive closure for comparison with semi-closed defaultModules:
+ Map<String, IModuleDescription> allModules = allSystemRoots.stream() //
+ .map(r -> r.getModuleDescription()) //
+ .filter(Objects::nonNull) //
+ .collect(Collectors.toMap(IModuleDescription::getElementName, module -> module));
+ Set<String> selectedClosure = closure(selected, new HashSet<>(), allModules);
+
+ if (limit.retainAll(selectedClosure)) { // limit = selected ∩ default -- only add the option, if limit ⊂ default
if (limit.isEmpty()) {
throw new IllegalArgumentException("Cannot hide all modules, at least java.base is required"); //$NON-NLS-1$
}
- buf.append(LIMIT_MODULES).append(joinedSortedList(limit)).append(BLANK);
+ buf.append(LIMIT_MODULES).append(joinedSortedList(reduceNames(limit, allModules.values()))).append(BLANK);
}
- Set<String> add = new HashSet<>(selected);
- add.removeAll(defaultModules);
- if (!add.isEmpty()) { // add = selected \ default
- buf.append(ADD_MODULES).append(joinedSortedList(add)).append(BLANK);
+ selectedClosure.removeAll(defaultModules);
+ if (!selectedClosure.isEmpty()) { // add = selected \ default
+ buf.append(ADD_MODULES).append(joinedSortedList(selectedClosure)).append(BLANK);
}
} else {
Arrays.sort(modules);
@@ -3595,6 +3604,58 @@
}
}
+ private static Set<String> closure(Collection<String> moduleNames, Set<String> collected, Map<String, IModuleDescription> allModules) {
+ for (String name : moduleNames) {
+ if (collected.add(name)) {
+ IModuleDescription module = allModules.get(name);
+ if (module != null) {
+ try {
+ closure(Arrays.asList(module.getRequiredModuleNames()), collected, allModules);
+ } catch (JavaModelException e) {
+ LaunchingPlugin.log(e);
+ }
+ }
+ }
+ }
+ return collected;
+ }
+
+ private static Collection<String> reduceNames(Collection<String> names, Collection<IModuleDescription> allModules) {
+ // build a reverse dependency tree:
+ Map<String, List<String>> moduleRequiredByModules = new HashMap<>();
+ for (IModuleDescription module : allModules) {
+ if (!names.contains(module.getElementName())) {
+ continue;
+ }
+ try {
+ for (String required : module.getRequiredModuleNames()) {
+ List<String> dominators = moduleRequiredByModules.get(required);
+ if (dominators == null) {
+ moduleRequiredByModules.put(required, dominators = new ArrayList<>());
+ }
+ dominators.add(module.getElementName());
+ }
+ } catch (CoreException e) {
+ LaunchingPlugin.log(e);
+ return names; // unreduced
+ }
+ }
+ // use the tree to find and eliminate redundancy:
+ List<String> reduced = new ArrayList<>();
+ outer: for (String name : names) {
+ List<String> dominators = moduleRequiredByModules.get(name);
+ if (dominators != null) {
+ for (String dominator : dominators) {
+ if (names.contains(dominator)) {
+ continue outer;
+ }
+ }
+ }
+ reduced.add(name);
+ }
+ return reduced;
+ }
+
private static Set<String> getDefaultModules(List<IPackageFragmentRoot> allSystemRoots) throws JavaModelException {
HashMap<String, String[]> moduleDescriptions = new HashMap<>();
for (IPackageFragmentRoot packageFragmentRoot : allSystemRoots) {