< prev index next >
src/java.base/share/classes/java/lang/module/Resolver.java
Print this page
@@ -1,7 +1,7 @@
/*
- * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
@@ -47,12 +47,12 @@
import jdk.internal.module.ModuleHashes;
import jdk.internal.module.ModuleReferenceImpl;
/**
- * The resolver used by {@link Configuration#resolveRequires} and
- * {@link Configuration#resolveRequiresAndUses}.
+ * The resolver used by {@link Configuration#resolve} and {@link
+ * Configuration#resolveAndBind}.
*
* @implNote The resolver is used at VM startup and so deliberately avoids
* using lambda and stream usages in code paths used during startup.
*/
@@ -64,28 +64,79 @@
private final PrintStream traceOutput;
// maps module name to module reference
private final Map<String, ModuleReference> nameToReference = new HashMap<>();
+ // module constraints on target platform
+ private String osName;
+ private String osArch;
+ private String osVersion;
+
+ String osName() { return osName; }
+ String osArch() { return osArch; }
+ String osVersion() { return osVersion; }
+ /**
+ * @throws IllegalArgumentException if there are more than one parent and
+ * the constraints on the target platform conflict
+ */
Resolver(ModuleFinder beforeFinder,
List<Configuration> parents,
ModuleFinder afterFinder,
PrintStream traceOutput) {
this.beforeFinder = beforeFinder;
this.parents = parents;
this.afterFinder = afterFinder;
this.traceOutput = traceOutput;
+
+ // record constraints on target platform, checking that they don't conflict
+ for (Configuration parent : parents) {
+ String value = parent.osName();
+ if (value != null) {
+ if (osName == null) {
+ osName = value;
+ } else {
+ if (!value.equals(osName)) {
+ failParentConflict("Operating System", osName, value);
+ }
+ }
+ }
+ value = parent.osArch();
+ if (value != null) {
+ if (osArch == null) {
+ osArch = value;
+ } else {
+ if (!value.equals(osArch)) {
+ failParentConflict("OS architecture", osArch, value);
+ }
+ }
+ }
+ value = parent.osVersion();
+ if (value != null) {
+ if (osVersion == null) {
+ osVersion = value;
+ } else {
+ if (!value.equals(osVersion)) {
+ failParentConflict("OS version", osVersion, value);
+ }
+ }
+ }
+ }
}
+ private void failParentConflict(String constraint, String s1, String s2) {
+ String msg = "Parents have conflicting constraints on target "
+ + constraint + ": " + s1 + ", " + s2;
+ throw new IllegalArgumentException(msg);
+ }
/**
* Resolves the given named modules.
*
* @throws ResolutionException
*/
- Resolver resolveRequires(Collection<String> roots) {
+ Resolver resolve(Collection<String> roots) {
// create the visit stack to get us started
Deque<ModuleDescriptor> q = new ArrayDeque<>();
for (String root : roots) {
@@ -98,21 +149,20 @@
continue;
}
mref = findWithAfterFinder(root);
if (mref == null) {
- fail("Module %s not found", root);
+ findFail("Module %s not found", root);
}
}
if (isTracing()) {
trace("Root module %s located", root);
mref.location().ifPresent(uri -> trace(" (%s)", uri));
}
- assert mref.descriptor().name().equals(root);
- nameToReference.put(root, mref);
+ addFoundModule(mref);
q.push(mref.descriptor());
}
resolve(q);
@@ -150,17 +200,17 @@
continue;
}
mref = findWithAfterFinder(dn);
if (mref == null) {
- fail("Module %s not found, required by %s",
+ findFail("Module %s not found, required by %s",
dn, descriptor.name());
}
}
if (!nameToReference.containsKey(dn)) {
- nameToReference.put(dn, mref);
+ addFoundModule(mref);
q.offer(mref.descriptor());
resolved.add(mref.descriptor());
if (isTracing()) {
trace("Module %s located, required by %s",
@@ -179,11 +229,11 @@
/**
* Augments the set of resolved modules with modules induced by the
* service-use relation.
*/
- Resolver resolveUses() {
+ Resolver bind() {
// Scan the finders for all available service provider modules. As
// java.base uses services then then module finders will be scanned
// anyway.
Map<String, Set<ModuleReference>> availableProviders = new HashMap<>();
@@ -244,11 +294,11 @@
if (!nameToReference.containsKey(pn)) {
if (isTracing()) {
mref.location()
.ifPresent(uri -> trace(" (%s)", uri));
}
- nameToReference.put(pn, mref);
+ addFoundModule(mref);
q.push(provider);
}
}
}
}
@@ -262,10 +312,85 @@
return this;
}
/**
+ * Add the module to the nameToReference map. Also check any constraints on
+ * the target platform with the constraints of other modules.
+ */
+ private void addFoundModule(ModuleReference mref) {
+ ModuleDescriptor descriptor = mref.descriptor();
+ nameToReference.put(descriptor.name(), mref);
+
+ if (descriptor.osName().isPresent()
+ || descriptor.osArch().isPresent()
+ || descriptor.osVersion().isPresent())
+ checkTargetConstraints(descriptor);
+ }
+
+ /**
+ * Check that the module's constraints on the target platform do not
+ * conflict with the constraints of other modules resolved so far or
+ * modules in parent configurations.
+ */
+ private void checkTargetConstraints(ModuleDescriptor descriptor) {
+ String value = descriptor.osName().orElse(null);
+ if (value != null) {
+ if (osName == null) {
+ osName = value;
+ } else {
+ if (!value.equals(osName)) {
+ failTargetConstraint(descriptor);
+ }
+ }
+ }
+ value = descriptor.osArch().orElse(null);
+ if (value != null) {
+ if (osArch == null) {
+ osArch = value;
+ } else {
+ if (!value.equals(osArch)) {
+ failTargetConstraint(descriptor);
+ }
+ }
+ }
+ value = descriptor.osVersion().orElse(null);
+ if (value != null) {
+ if (osVersion == null) {
+ osVersion = value;
+ } else {
+ if (!value.equals(osVersion)) {
+ failTargetConstraint(descriptor);
+ }
+ }
+ }
+ }
+
+ private void failTargetConstraint(ModuleDescriptor md) {
+ String s1 = targetAsString(osName, osArch, osVersion);
+ String s2 = targetAsString(md);
+ findFail("Module %s has constraints on target platform that conflict" +
+ " with other modules: %s, %s", md.name(), s1, s2);
+ }
+
+ private String targetAsString(ModuleDescriptor descriptor) {
+ String osName = descriptor.osName().orElse(null);
+ String osArch = descriptor.osArch().orElse(null);
+ String osVersion = descriptor.osVersion().orElse(null);
+ return targetAsString(osName, osArch, osVersion);
+ }
+
+ private String targetAsString(String osName, String osArch, String osVersion) {
+ return new StringJoiner("-")
+ .add(Objects.toString(osName, "*"))
+ .add(Objects.toString(osArch, "*"))
+ .add(Objects.toString(osVersion, "*"))
+ .toString();
+ }
+
+
+ /**
* Execute post-resolution checks and returns the module graph of resolved
* modules as {@code Map}. The resolved modules will be in the given
* configuration.
*
* @param check {@true} to execute the post resolution checks
@@ -279,11 +404,10 @@
names.stream().sorted().forEach(name -> trace(" %s", name));
}
if (check) {
detectCycles();
- checkPlatformConstraints();
checkHashes();
}
Map<ResolvedModule, Set<ResolvedModule>> graph = makeGraph(cf);
@@ -317,12 +441,11 @@
private void visit(ModuleDescriptor descriptor) {
if (!visited.contains(descriptor)) {
boolean added = visitPath.add(descriptor);
if (!added) {
- throw new ResolutionException("Cycle detected: " +
- cycleAsString(descriptor));
+ resolveFail("Cycle detected: %s", cycleAsString(descriptor));
}
for (ModuleDescriptor.Requires requires : descriptor.requires()) {
String dn = requires.name();
ModuleReference mref = nameToReference.get(dn);
@@ -352,90 +475,10 @@
.collect(Collectors.joining(" -> "));
}
/**
- * If there are platform specific modules then check that the OS name,
- * architecture and version match.
- *
- * @apiNote This method does not currently check if the OS matches
- * platform specific modules in parent configurations.
- */
- private void checkPlatformConstraints() {
-
- // first module encountered that is platform specific
- String savedModuleName = null;
- String savedOsName = null;
- String savedOsArch = null;
- String savedOsVersion = null;
-
- for (ModuleReference mref : nameToReference.values()) {
- ModuleDescriptor descriptor = mref.descriptor();
-
- String osName = descriptor.osName().orElse(null);
- String osArch = descriptor.osArch().orElse(null);
- String osVersion = descriptor.osVersion().orElse(null);
-
- if (osName != null || osArch != null || osVersion != null) {
-
- if (savedModuleName == null) {
-
- savedModuleName = descriptor.name();
- savedOsName = osName;
- savedOsArch = osArch;
- savedOsVersion = osVersion;
-
- } else {
-
- boolean matches = platformMatches(osName, savedOsName)
- && platformMatches(osArch, savedOsArch)
- && platformMatches(osVersion, savedOsVersion);
-
- if (!matches) {
- String s1 = platformAsString(savedOsName,
- savedOsArch,
- savedOsVersion);
-
- String s2 = platformAsString(osName, osArch, osVersion);
- fail("Mismatching constraints on target platform: "
- + savedModuleName + ": " + s1
- + ", " + descriptor.name() + ": " + s2);
- }
-
- }
-
- }
- }
-
- }
-
- /**
- * Returns true if the s1 and s2 are equal or one of them is null.
- */
- private boolean platformMatches(String s1, String s2) {
- if (s1 == null || s2 == null)
- return true;
- else
- return Objects.equals(s1, s2);
- }
-
- /**
- * Return a string that encodes the OS name/arch/version.
- */
- private String platformAsString(String osName,
- String osArch,
- String osVersion) {
-
- return new StringJoiner("-")
- .add(Objects.toString(osName, "*"))
- .add(Objects.toString(osArch, "*"))
- .add(Objects.toString(osVersion, "*"))
- .toString();
-
- }
-
- /**
* Checks the hashes in the module descriptor to ensure that they match
* any recorded hashes.
*/
private void checkHashes() {
for (ModuleReference mref : nameToReference.values()) {
@@ -458,22 +501,22 @@
}
if (mref2 == null)
continue;
if (!(mref2 instanceof ModuleReferenceImpl)) {
- fail("Unable to compute the hash of module %s", dn);
+ findFail("Unable to compute the hash of module %s", dn);
}
// skip checking the hash if the module has been patched
ModuleReferenceImpl other = (ModuleReferenceImpl)mref2;
if (other != null && !other.isPatched()) {
byte[] recordedHash = hashes.hashFor(dn);
byte[] actualHash = other.computeHash(algorithm);
if (actualHash == null)
- fail("Unable to compute the hash of module %s", dn);
+ findFail("Unable to compute the hash of module %s", dn);
if (!Arrays.equals(recordedHash, actualHash)) {
- fail("Hash of %s (%s) differs to expected hash (%s)" +
+ findFail("Hash of %s (%s) differs to expected hash (%s)" +
" recorded in %s", dn, toHexString(actualHash),
toHexString(recordedHash), descriptor.name());
}
}
}
@@ -692,78 +735,108 @@
// descriptor1 reads descriptor2
Set<ResolvedModule> reads = e.getValue();
for (ResolvedModule endpoint : reads) {
ModuleDescriptor descriptor2 = endpoint.descriptor();
- for (ModuleDescriptor.Exports export : descriptor2.exports()) {
+ if (descriptor2.isAutomatic()) {
+ // automatic modules read self and export all packages
+ if (descriptor2 != descriptor1){
+ for (String source : descriptor2.packages()) {
+ ModuleDescriptor supplier
+ = packageToExporter.putIfAbsent(source, descriptor2);
+ // descriptor2 and 'supplier' export source to descriptor1
+ if (supplier != null) {
+ failTwoSuppliers(descriptor1, source, descriptor2, supplier);
+ }
+ }
+
+ }
+ } else {
+ for (ModuleDescriptor.Exports export : descriptor2.exports()) {
if (export.isQualified()) {
if (!export.targets().contains(descriptor1.name()))
continue;
}
- // source is exported to descriptor2
+ // source is exported by descriptor2
String source = export.source();
- ModuleDescriptor other
+ ModuleDescriptor supplier
= packageToExporter.putIfAbsent(source, descriptor2);
- if (other != null && other != descriptor2) {
- // package might be local to descriptor1
- if (other == descriptor1) {
- fail("Module %s contains package %s"
- + ", module %s exports package %s to %s",
- descriptor1.name(),
- source,
- descriptor2.name(),
- source,
- descriptor1.name());
- } else {
- fail("Modules %s and %s export package %s to module %s",
- descriptor2.name(),
- other.name(),
- source,
- descriptor1.name());
+ // descriptor2 and 'supplier' export source to descriptor1
+ if (supplier != null) {
+ failTwoSuppliers(descriptor1, source, descriptor2, supplier);
}
-
}
+
}
}
// uses/provides checks not applicable to automatic modules
if (!descriptor1.isAutomatic()) {
// uses S
for (String service : descriptor1.uses()) {
String pn = packageName(service);
if (!packageToExporter.containsKey(pn)) {
- fail("Module %s does not read a module that exports %s",
+ resolveFail("Module %s does not read a module that exports %s",
descriptor1.name(), pn);
}
}
// provides S
for (ModuleDescriptor.Provides provides : descriptor1.provides()) {
String pn = packageName(provides.service());
if (!packageToExporter.containsKey(pn)) {
- fail("Module %s does not read a module that exports %s",
+ resolveFail("Module %s does not read a module that exports %s",
descriptor1.name(), pn);
}
+ }
- for (String provider : provides.providers()) {
- if (!packages.contains(packageName(provider))) {
- fail("Provider %s not in module %s",
- provider, descriptor1.name());
}
+
}
+
}
+ /**
+ * Fail because a module in the configuration exports the same package to
+ * a module that reads both. This includes the case where a module M
+ * containing a package p reads another module that exports p to at least
+ * module M.
+ */
+ private void failTwoSuppliers(ModuleDescriptor descriptor,
+ String source,
+ ModuleDescriptor supplier1,
+ ModuleDescriptor supplier2) {
+
+ if (supplier2 == descriptor) {
+ ModuleDescriptor tmp = supplier1;
+ supplier1 = supplier2;
+ supplier2 = tmp;
}
+ if (supplier1 == descriptor) {
+ resolveFail("Module %s contains package %s"
+ + ", module %s exports package %s to %s",
+ descriptor.name(),
+ source,
+ supplier2.name(),
+ source,
+ descriptor.name());
+ } else {
+ resolveFail("Modules %s and %s export package %s to module %s",
+ supplier1.name(),
+ supplier2.name(),
+ source,
+ descriptor.name());
}
}
+
/**
* Find a module of the given name in the parent configurations
*/
private ResolvedModule findInParent(String mn) {
for (Configuration parent : parents) {
@@ -777,37 +850,27 @@
/**
* Invokes the beforeFinder to find method to find the given module.
*/
private ModuleReference findWithBeforeFinder(String mn) {
- try {
+
return beforeFinder.find(mn).orElse(null);
- } catch (FindException e) {
- // unwrap
- throw new ResolutionException(e.getMessage(), e.getCause());
- }
+
}
/**
* Invokes the afterFinder to find method to find the given module.
*/
private ModuleReference findWithAfterFinder(String mn) {
- try {
return afterFinder.find(mn).orElse(null);
- } catch (FindException e) {
- // unwrap
- throw new ResolutionException(e.getMessage(), e.getCause());
- }
}
/**
* Returns the set of all modules that are observable with the before
* and after ModuleFinders.
*/
private Set<ModuleReference> findAll() {
- try {
-
Set<ModuleReference> beforeModules = beforeFinder.findAll();
Set<ModuleReference> afterModules = afterFinder.findAll();
if (afterModules.isEmpty())
return beforeModules;
@@ -825,15 +888,10 @@
result.add(mref);
}
}
return result;
-
- } catch (FindException e) {
- // unwrap
- throw new ResolutionException(e.getMessage(), e.getCause());
- }
}
/**
* Returns the package name
*/
@@ -841,13 +899,21 @@
int index = cn.lastIndexOf(".");
return (index == -1) ? "" : cn.substring(0, index);
}
/**
+ * Throw FindException with the given format string and arguments
+ */
+ private static void findFail(String fmt, Object ... args) {
+ String msg = String.format(fmt, args);
+ throw new FindException(msg);
+ }
+
+ /**
* Throw ResolutionException with the given format string and arguments
*/
- private static void fail(String fmt, Object ... args) {
+ private static void resolveFail(String fmt, Object ... args) {
String msg = String.format(fmt, args);
throw new ResolutionException(msg);
}
/**
< prev index next >