Merge
This commit is contained in:
commit
44790114ee
@ -96,7 +96,13 @@ TOOL_CHARSETMAPPING = $(JAVA_SMALL) -cp $(BUILDTOOLS_OUTPUTDIR)/jdk_tools_classe
|
||||
TOOL_SPP = $(JAVA_SMALL) -cp $(BUILDTOOLS_OUTPUTDIR)/jdk_tools_classes build.tools.spp.Spp
|
||||
|
||||
# Nimbus is used somewhere in the swing build.
|
||||
|
||||
ifeq ($(BOOT_JDK_MODULAR), true)
|
||||
COMPILENIMBUS_ADD_MODS := -addmods java.xml.bind
|
||||
endif
|
||||
|
||||
TOOL_GENERATENIMBUS = $(JAVA_SMALL) -cp $(BUILDTOOLS_OUTPUTDIR)/jdk_tools_classes \
|
||||
$(COMPILENIMBUS_ADD_MODS) \
|
||||
build.tools.generatenimbus.Generator
|
||||
|
||||
TOOL_WRAPPERGENERATOR = $(JAVA_SMALL) -cp $(BUILDTOOLS_OUTPUTDIR)/jdk_tools_classes \
|
||||
|
@ -62,10 +62,13 @@ $(eval $(call SetupJavaCompilation,BUILD_BREAKITERATOR_LD, \
|
||||
BIN := $(BREAK_ITERATOR_CLASSES)/jdk.localedata))
|
||||
|
||||
ifeq ($(BOOT_JDK_MODULAR), true)
|
||||
BREAK_ITERATOR_BOOTCLASSPATH := -Xpatch:$(BREAK_ITERATOR_CLASSES) \
|
||||
-XaddExports:java.base/sun.text=ALL-UNNAMED \
|
||||
-XaddExports:java.base/sun.text.resources=ALL-UNNAMED \
|
||||
-XaddExports:jdk.localedata/sun.text.resources.ext=ALL-UNNAMED
|
||||
BREAK_ITERATOR_BOOTCLASSPATH := \
|
||||
-Xpatch:java.base=$(BREAK_ITERATOR_CLASSES)/java.base \
|
||||
-Xpatch:jdk.localedata=$(BREAK_ITERATOR_CLASSES)/jdk.localedata \
|
||||
-XaddExports:java.base/sun.text=ALL-UNNAMED \
|
||||
-XaddExports:java.base/sun.text.resources=ALL-UNNAMED \
|
||||
-XaddExports:jdk.localedata/sun.text.resources.ext=ALL-UNNAMED \
|
||||
#
|
||||
else
|
||||
BREAK_ITERATOR_BOOTCLASSPATH := -Xbootclasspath/p:$(call PathList, \
|
||||
$(BREAK_ITERATOR_CLASSES)/java.base \
|
||||
|
@ -31,7 +31,7 @@ $(eval $(call IncludeCustomExtension, jdk, launcher/Launcher-java.desktop.gmk))
|
||||
ifndef BUILD_HEADLESS_ONLY
|
||||
$(eval $(call SetupBuildLauncher, appletviewer, \
|
||||
MAIN_CLASS := sun.applet.Main, \
|
||||
JAVA_ARGS := -addmods ALL-SYSTEM, \
|
||||
JAVA_ARGS := -addmods ALL-DEFAULT, \
|
||||
LIBS_unix := $(X_LIBS), \
|
||||
))
|
||||
endif
|
||||
|
@ -27,4 +27,5 @@ include LauncherCommon.gmk
|
||||
|
||||
$(eval $(call SetupBuildLauncher, jrunscript, \
|
||||
MAIN_CLASS := com.sun.tools.script.shell.Main, \
|
||||
JAVA_ARGS := -addmods ALL-DEFAULT, \
|
||||
))
|
||||
|
@ -27,7 +27,8 @@ include LauncherCommon.gmk
|
||||
|
||||
$(eval $(call SetupBuildLauncher, javac, \
|
||||
MAIN_CLASS := com.sun.tools.javac.Main, \
|
||||
CFLAGS := -DEXPAND_CLASSPATH_WILDCARDS \
|
||||
JAVA_ARGS := -addmods ALL-DEFAULT, \
|
||||
CFLAGS := -DEXPAND_CLASSPATH_WILDCARDS \
|
||||
-DNEVER_ACT_AS_SERVER_CLASS_MACHINE, \
|
||||
))
|
||||
|
||||
|
@ -27,6 +27,7 @@ include LauncherCommon.gmk
|
||||
|
||||
$(eval $(call SetupBuildLauncher, javadoc, \
|
||||
MAIN_CLASS := jdk.javadoc.internal.tool.Main, \
|
||||
JAVA_ARGS := -addmods ALL-DEFAULT, \
|
||||
CFLAGS := -DEXPAND_CLASSPATH_WILDCARDS \
|
||||
-DNEVER_ACT_AS_SERVER_CLASS_MACHINE, \
|
||||
))
|
||||
|
@ -32,6 +32,7 @@ $(eval $(call SetupBuildLauncher, jimage,\
|
||||
|
||||
$(eval $(call SetupBuildLauncher, jlink,\
|
||||
MAIN_CLASS := jdk.tools.jlink.internal.Main, \
|
||||
JAVA_ARGS := -addmods ALL-DEFAULT, \
|
||||
CFLAGS := -DENABLE_ARG_FILES \
|
||||
-DEXPAND_CLASSPATH_WILDCARDS \
|
||||
-DNEVER_ACT_AS_SERVER_CLASS_MACHINE, \
|
||||
|
@ -27,6 +27,6 @@ include LauncherCommon.gmk
|
||||
|
||||
$(eval $(call SetupBuildLauncher, jjs, \
|
||||
MAIN_CLASS := jdk.nashorn.tools.jjs.Main, \
|
||||
JAVA_ARGS := -addmods ALL-SYSTEM, \
|
||||
JAVA_ARGS := -addmods ALL-DEFAULT, \
|
||||
CFLAGS := -DENABLE_ARG_FILES, \
|
||||
))
|
||||
|
@ -2615,7 +2615,7 @@ public abstract class ClassLoader {
|
||||
ServicesCatalog createOrGetServicesCatalog() {
|
||||
ServicesCatalog catalog = servicesCatalog;
|
||||
if (catalog == null) {
|
||||
catalog = new ServicesCatalog();
|
||||
catalog = ServicesCatalog.create();
|
||||
boolean set = trySetObjectField("servicesCatalog", catalog);
|
||||
if (!set) {
|
||||
// beaten by someone else
|
||||
|
@ -69,7 +69,6 @@ import jdk.internal.logger.LazyLoggers;
|
||||
import jdk.internal.logger.LocalizedLoggerWrapper;
|
||||
|
||||
import jdk.internal.module.ModuleBootstrap;
|
||||
import jdk.internal.module.Modules;
|
||||
import jdk.internal.module.ServicesCatalog;
|
||||
|
||||
/**
|
||||
@ -1924,10 +1923,6 @@ public final class System {
|
||||
// initialize the module system
|
||||
System.bootLayer = ModuleBootstrap.boot();
|
||||
|
||||
// base module needs to be loose (CODETOOLS-7901619)
|
||||
Module base = Object.class.getModule();
|
||||
Modules.addReads(base, null);
|
||||
|
||||
// module system initialized
|
||||
VM.initLevel(2);
|
||||
}
|
||||
|
@ -25,6 +25,7 @@
|
||||
|
||||
package java.lang.module;
|
||||
|
||||
import java.io.PrintStream;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
@ -183,17 +184,20 @@ public final class Configuration {
|
||||
this.nameToModule = Collections.emptyMap();
|
||||
}
|
||||
|
||||
private Configuration(Configuration parent, Resolver resolver) {
|
||||
Map<ResolvedModule, Set<ResolvedModule>> graph = resolver.finish(this);
|
||||
private Configuration(Configuration parent,
|
||||
Resolver resolver,
|
||||
boolean check)
|
||||
{
|
||||
Map<ResolvedModule, Set<ResolvedModule>> g = resolver.finish(this, check);
|
||||
|
||||
Map<String, ResolvedModule> nameToModule = new HashMap<>();
|
||||
for (ResolvedModule resolvedModule : graph.keySet()) {
|
||||
for (ResolvedModule resolvedModule : g.keySet()) {
|
||||
nameToModule.put(resolvedModule.name(), resolvedModule);
|
||||
}
|
||||
|
||||
this.parent = parent;
|
||||
this.graph = graph;
|
||||
this.modules = Collections.unmodifiableSet(graph.keySet());
|
||||
this.graph = g;
|
||||
this.modules = Collections.unmodifiableSet(g.keySet());
|
||||
this.nameToModule = Collections.unmodifiableMap(nameToModule);
|
||||
}
|
||||
|
||||
@ -283,10 +287,10 @@ public final class Configuration {
|
||||
Objects.requireNonNull(after);
|
||||
Objects.requireNonNull(roots);
|
||||
|
||||
Resolver resolver = new Resolver(before, this, after);
|
||||
Resolver resolver = new Resolver(before, this, after, null);
|
||||
resolver.resolveRequires(roots);
|
||||
|
||||
return new Configuration(this, resolver);
|
||||
return new Configuration(this, resolver, true);
|
||||
}
|
||||
|
||||
|
||||
@ -340,10 +344,32 @@ public final class Configuration {
|
||||
Objects.requireNonNull(after);
|
||||
Objects.requireNonNull(roots);
|
||||
|
||||
Resolver resolver = new Resolver(before, this, after);
|
||||
Resolver resolver = new Resolver(before, this, after, null);
|
||||
resolver.resolveRequires(roots).resolveUses();
|
||||
|
||||
return new Configuration(this, resolver);
|
||||
return new Configuration(this, resolver, true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Resolves a collection of root modules, with service binding, and with
|
||||
* the empty configuration as its parent. The post resolution checks
|
||||
* are optionally run.
|
||||
*
|
||||
* This method is used to create the configuration for the boot layer.
|
||||
*/
|
||||
static Configuration resolveRequiresAndUses(ModuleFinder finder,
|
||||
Collection<String> roots,
|
||||
boolean check,
|
||||
PrintStream traceOutput)
|
||||
{
|
||||
Configuration parent = empty();
|
||||
|
||||
Resolver resolver
|
||||
= new Resolver(finder, parent, ModuleFinder.empty(), traceOutput);
|
||||
resolver.resolveRequires(roots).resolveUses();
|
||||
|
||||
return new Configuration(parent, resolver, check);
|
||||
}
|
||||
|
||||
|
||||
|
@ -27,13 +27,17 @@ package java.lang.module;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.net.URI;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
@ -45,7 +49,7 @@ import static jdk.internal.module.Checks.*;
|
||||
import static java.util.Objects.*;
|
||||
|
||||
import jdk.internal.module.Checks;
|
||||
import jdk.internal.module.Hasher.DependencyHashes;
|
||||
import jdk.internal.module.ModuleHashes;
|
||||
|
||||
|
||||
/**
|
||||
@ -372,8 +376,9 @@ public class ModuleDescriptor
|
||||
|
||||
private Provides(String service, Set<String> providers, boolean check) {
|
||||
this.service = check ? requireServiceTypeName(service) : service;
|
||||
providers = check ? Collections.unmodifiableSet(new HashSet<>(providers))
|
||||
: Collections.unmodifiableSet(providers);
|
||||
providers = check
|
||||
? Collections.unmodifiableSet(new LinkedHashSet<>(providers))
|
||||
: Collections.unmodifiableSet(providers);
|
||||
if (providers.isEmpty())
|
||||
throw new IllegalArgumentException("Empty providers set");
|
||||
if (check)
|
||||
@ -787,7 +792,7 @@ public class ModuleDescriptor
|
||||
private final String osVersion;
|
||||
private final Set<String> conceals;
|
||||
private final Set<String> packages;
|
||||
private final DependencyHashes hashes;
|
||||
private final ModuleHashes hashes;
|
||||
|
||||
private ModuleDescriptor(String name,
|
||||
boolean automatic,
|
||||
@ -802,7 +807,7 @@ public class ModuleDescriptor
|
||||
String osArch,
|
||||
String osVersion,
|
||||
Set<String> conceals,
|
||||
DependencyHashes hashes)
|
||||
ModuleHashes hashes)
|
||||
{
|
||||
|
||||
this.name = name;
|
||||
@ -878,7 +883,8 @@ public class ModuleDescriptor
|
||||
String osArch,
|
||||
String osVersion,
|
||||
Set<String> conceals,
|
||||
Set<String> packages) {
|
||||
Set<String> packages,
|
||||
ModuleHashes hashes) {
|
||||
this.name = name;
|
||||
this.automatic = automatic;
|
||||
this.synthetic = synthetic;
|
||||
@ -894,7 +900,7 @@ public class ModuleDescriptor
|
||||
this.osName = osName;
|
||||
this.osArch = osArch;
|
||||
this.osVersion = osVersion;
|
||||
this.hashes = null;
|
||||
this.hashes = hashes;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1063,9 +1069,9 @@ public class ModuleDescriptor
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the object with the hashes of the dependences.
|
||||
* Returns the object with the hashes of other modules
|
||||
*/
|
||||
Optional<DependencyHashes> hashes() {
|
||||
Optional<ModuleHashes> hashes() {
|
||||
return Optional.ofNullable(hashes);
|
||||
}
|
||||
|
||||
@ -1103,7 +1109,7 @@ public class ModuleDescriptor
|
||||
String osArch;
|
||||
String osVersion;
|
||||
String mainClass;
|
||||
DependencyHashes hashes;
|
||||
ModuleHashes hashes;
|
||||
|
||||
/**
|
||||
* Initializes a new builder with the given module name.
|
||||
@ -1580,7 +1586,7 @@ public class ModuleDescriptor
|
||||
return this;
|
||||
}
|
||||
|
||||
/* package */ Builder hashes(DependencyHashes hashes) {
|
||||
/* package */ Builder hashes(ModuleHashes hashes) {
|
||||
this.hashes = hashes;
|
||||
return this;
|
||||
}
|
||||
@ -1719,7 +1725,9 @@ public class ModuleDescriptor
|
||||
hc = hc * 43 + Objects.hashCode(osVersion);
|
||||
hc = hc * 43 + Objects.hashCode(conceals);
|
||||
hc = hc * 43 + Objects.hashCode(hashes);
|
||||
if (hc != 0) hash = hc;
|
||||
if (hc == 0)
|
||||
hc = -1;
|
||||
hash = hc;
|
||||
}
|
||||
return hc;
|
||||
}
|
||||
@ -1925,11 +1933,12 @@ public class ModuleDescriptor
|
||||
|
||||
static {
|
||||
/**
|
||||
* Setup the shared secret to allow code in other packages create
|
||||
* ModuleDescriptor and associated objects directly.
|
||||
* Setup the shared secret to allow code in other packages access
|
||||
* private package methods in java.lang.module.
|
||||
*/
|
||||
jdk.internal.misc.SharedSecrets
|
||||
.setJavaLangModuleAccess(new jdk.internal.misc.JavaLangModuleAccess() {
|
||||
|
||||
@Override
|
||||
public Requires newRequires(Set<Requires.Modifier> ms, String mn) {
|
||||
return new Requires(ms, mn, false);
|
||||
@ -1974,7 +1983,8 @@ public class ModuleDescriptor
|
||||
String osArch,
|
||||
String osVersion,
|
||||
Set<String> conceals,
|
||||
Set<String> packages) {
|
||||
Set<String> packages,
|
||||
ModuleHashes hashes) {
|
||||
return new ModuleDescriptor(name,
|
||||
automatic,
|
||||
synthetic,
|
||||
@ -1988,7 +1998,29 @@ public class ModuleDescriptor
|
||||
osArch,
|
||||
osVersion,
|
||||
conceals,
|
||||
packages);
|
||||
packages,
|
||||
hashes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Configuration resolveRequiresAndUses(ModuleFinder finder,
|
||||
Collection<String> roots,
|
||||
boolean check,
|
||||
PrintStream traceOutput)
|
||||
{
|
||||
return Configuration.resolveRequiresAndUses(finder, roots, check, traceOutput);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModuleReference newPatchedModule(ModuleDescriptor descriptor,
|
||||
URI location,
|
||||
Supplier<ModuleReader> s) {
|
||||
return new ModuleReference(descriptor, location, s, true, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<ModuleHashes> hashes(ModuleDescriptor descriptor) {
|
||||
return descriptor.hashes();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -37,11 +37,12 @@ import java.nio.BufferUnderflowException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import jdk.internal.module.Hasher.DependencyHashes;
|
||||
import jdk.internal.module.ModuleHashes;
|
||||
|
||||
import static jdk.internal.module.ClassFileConstants.*;
|
||||
|
||||
@ -337,7 +338,7 @@ final class ModuleInfo {
|
||||
// computeIfAbsent
|
||||
Set<String> providers = pm.get(sn);
|
||||
if (providers == null) {
|
||||
providers = new HashSet<>();
|
||||
providers = new LinkedHashSet<>(); // preserve order
|
||||
pm.put(sn, providers);
|
||||
}
|
||||
providers.add(cn);
|
||||
@ -425,7 +426,7 @@ final class ModuleInfo {
|
||||
map.put(dn, hash);
|
||||
}
|
||||
|
||||
builder.hashes(new DependencyHashes(algorithm, map));
|
||||
builder.hashes(new ModuleHashes(algorithm, map));
|
||||
}
|
||||
|
||||
|
||||
|
@ -40,7 +40,7 @@ import java.nio.file.Path;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
@ -52,7 +52,6 @@ import java.util.jar.Manifest;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
@ -190,18 +189,16 @@ class ModulePath implements ConfigurableModuleFinder {
|
||||
}
|
||||
}
|
||||
|
||||
if (attrs.isRegularFile() || attrs.isDirectory()) {
|
||||
// packaged or exploded module
|
||||
ModuleReference mref = readModule(entry, attrs);
|
||||
if (mref != null) {
|
||||
String name = mref.descriptor().name();
|
||||
return Collections.singletonMap(name, mref);
|
||||
}
|
||||
// packaged or exploded module
|
||||
ModuleReference mref = readModule(entry, attrs);
|
||||
if (mref != null) {
|
||||
String name = mref.descriptor().name();
|
||||
return Collections.singletonMap(name, mref);
|
||||
} else {
|
||||
// skipped
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
// not recognized
|
||||
throw new FindException("Unrecognized module: " + entry);
|
||||
|
||||
} catch (IOException ioe) {
|
||||
throw new FindException(ioe);
|
||||
}
|
||||
@ -238,16 +235,13 @@ class ModulePath implements ConfigurableModuleFinder {
|
||||
|
||||
// module found
|
||||
if (mref != null) {
|
||||
|
||||
// can have at most one version of a module in the directory
|
||||
String name = mref.descriptor().name();
|
||||
if (nameToReference.put(name, mref) != null) {
|
||||
throw new FindException("Two versions of module "
|
||||
+ name + " found in " + dir);
|
||||
+ name + " found in " + dir);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -257,28 +251,40 @@ class ModulePath implements ConfigurableModuleFinder {
|
||||
|
||||
/**
|
||||
* Locates a packaged or exploded module, returning a {@code ModuleReference}
|
||||
* to the module. Returns {@code null} if the module is not recognized
|
||||
* as a packaged or exploded module.
|
||||
* to the module. Returns {@code null} if the entry is skipped because it is
|
||||
* to a directory that does not contain a module-info.class or it's a hidden
|
||||
* file.
|
||||
*
|
||||
* @throws IOException if an I/O error occurs
|
||||
* @throws FindException if an error occurs parsing the module descriptor
|
||||
* @throws FindException if the file is not recognized as a module or an
|
||||
* error occurs parsing its module descriptor
|
||||
*/
|
||||
private ModuleReference readModule(Path entry, BasicFileAttributes attrs)
|
||||
throws IOException
|
||||
{
|
||||
try {
|
||||
|
||||
ModuleReference mref = null;
|
||||
if (attrs.isDirectory()) {
|
||||
mref = readExplodedModule(entry);
|
||||
} if (attrs.isRegularFile()) {
|
||||
if (entry.toString().endsWith(".jar")) {
|
||||
mref = readJar(entry);
|
||||
} else if (isLinkPhase && entry.toString().endsWith(".jmod")) {
|
||||
mref = readJMod(entry);
|
||||
return readExplodedModule(entry); // may return null
|
||||
}
|
||||
|
||||
String fn = entry.getFileName().toString();
|
||||
if (attrs.isRegularFile()) {
|
||||
if (fn.endsWith(".jar")) {
|
||||
return readJar(entry);
|
||||
} else if (fn.endsWith(".jmod")) {
|
||||
if (isLinkPhase)
|
||||
return readJMod(entry);
|
||||
throw new FindException("JMOD files not supported: " + entry);
|
||||
}
|
||||
}
|
||||
return mref;
|
||||
|
||||
// skip hidden files
|
||||
if (fn.startsWith(".") || Files.isHidden(entry)) {
|
||||
return null;
|
||||
} else {
|
||||
throw new FindException("Unrecognized module: " + entry);
|
||||
}
|
||||
|
||||
} catch (InvalidModuleDescriptorException e) {
|
||||
throw new FindException("Error reading module: " + entry, e);
|
||||
@ -292,15 +298,17 @@ class ModulePath implements ConfigurableModuleFinder {
|
||||
return zf.stream()
|
||||
.filter(e -> e.getName().startsWith("classes/") &&
|
||||
e.getName().endsWith(".class"))
|
||||
.map(e -> toPackageName(e))
|
||||
.map(e -> toPackageName(e.getName().substring(8)))
|
||||
.filter(pkg -> pkg.length() > 0) // module-info
|
||||
.distinct()
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@code ModuleReference} to a module in jmod file on the
|
||||
* file system.
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws InvalidModuleDescriptorException
|
||||
*/
|
||||
private ModuleReference readJMod(Path file) throws IOException {
|
||||
try (ZipFile zf = new ZipFile(file.toString())) {
|
||||
@ -419,13 +427,12 @@ class ModulePath implements ConfigurableModuleFinder {
|
||||
|
||||
// scan the entries in the JAR file to locate the .class and service
|
||||
// configuration file
|
||||
Stream<String> stream = jf.stream()
|
||||
.map(e -> e.getName())
|
||||
.filter(e -> (e.endsWith(".class") || e.startsWith(SERVICES_PREFIX)))
|
||||
.distinct();
|
||||
Map<Boolean, Set<String>> map
|
||||
= stream.collect(Collectors.partitioningBy(s -> s.endsWith(".class"),
|
||||
Collectors.toSet()));
|
||||
Map<Boolean, Set<String>> map =
|
||||
jf.stream()
|
||||
.map(JarEntry::getName)
|
||||
.filter(s -> (s.endsWith(".class") ^ s.startsWith(SERVICES_PREFIX)))
|
||||
.collect(Collectors.partitioningBy(s -> s.endsWith(".class"),
|
||||
Collectors.toSet()));
|
||||
Set<String> classFiles = map.get(Boolean.TRUE);
|
||||
Set<String> configFiles = map.get(Boolean.FALSE);
|
||||
|
||||
@ -433,19 +440,18 @@ class ModulePath implements ConfigurableModuleFinder {
|
||||
classFiles.stream()
|
||||
.map(c -> toPackageName(c))
|
||||
.distinct()
|
||||
.forEach(p -> builder.exports(p));
|
||||
.forEach(builder::exports);
|
||||
|
||||
// map names of service configuration files to service names
|
||||
Set<String> serviceNames = configFiles.stream()
|
||||
.map(this::toServiceName)
|
||||
.filter(Optional::isPresent)
|
||||
.map(Optional::get)
|
||||
.flatMap(Optional::stream)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
// parse each service configuration file
|
||||
for (String sn : serviceNames) {
|
||||
JarEntry entry = jf.getJarEntry(SERVICES_PREFIX + sn);
|
||||
Set<String> providerClasses = new HashSet<>();
|
||||
Set<String> providerClasses = new LinkedHashSet<>();
|
||||
try (InputStream in = jf.getInputStream(entry)) {
|
||||
BufferedReader reader
|
||||
= new BufferedReader(new InputStreamReader(in, "UTF-8"));
|
||||
@ -475,19 +481,25 @@ class ModulePath implements ConfigurableModuleFinder {
|
||||
private Set<String> jarPackages(JarFile jf) {
|
||||
return jf.stream()
|
||||
.filter(e -> e.getName().endsWith(".class"))
|
||||
.map(e -> toPackageName(e))
|
||||
.map(e -> toPackageName(e.getName()))
|
||||
.filter(pkg -> pkg.length() > 0) // module-info
|
||||
.distinct()
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@code ModuleReference} to a module in modular JAR file on
|
||||
* the file system.
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws FindException
|
||||
* @throws InvalidModuleDescriptorException
|
||||
*/
|
||||
private ModuleReference readJar(Path file) throws IOException {
|
||||
try (JarFile jf = new JarFile(file.toString())) {
|
||||
|
||||
try (JarFile jf = new JarFile(file.toFile(),
|
||||
true, // verify
|
||||
ZipFile.OPEN_READ,
|
||||
JarFile.Release.RUNTIME))
|
||||
{
|
||||
ModuleDescriptor md;
|
||||
JarEntry entry = jf.getJarEntry(MODULE_INFO);
|
||||
if (entry == null) {
|
||||
@ -520,7 +532,6 @@ class ModulePath implements ConfigurableModuleFinder {
|
||||
path.toString().endsWith(".class")))
|
||||
.map(path -> toPackageName(dir.relativize(path)))
|
||||
.filter(pkg -> pkg.length() > 0) // module-info
|
||||
.distinct()
|
||||
.collect(Collectors.toSet());
|
||||
} catch (IOException x) {
|
||||
throw new UncheckedIOException(x);
|
||||
@ -530,6 +541,9 @@ class ModulePath implements ConfigurableModuleFinder {
|
||||
/**
|
||||
* Returns a {@code ModuleReference} to an exploded module on the file
|
||||
* system or {@code null} if {@code module-info.class} not found.
|
||||
*
|
||||
* @throws IOException
|
||||
* @throws InvalidModuleDescriptorException
|
||||
*/
|
||||
private ModuleReference readExplodedModule(Path dir) throws IOException {
|
||||
Path mi = dir.resolve(MODULE_INFO);
|
||||
@ -559,19 +573,6 @@ class ModulePath implements ConfigurableModuleFinder {
|
||||
}
|
||||
}
|
||||
|
||||
private String toPackageName(ZipEntry entry) {
|
||||
String name = entry.getName();
|
||||
assert name.endsWith(".class");
|
||||
// jmod classes in classes/, jar in /
|
||||
int start = name.startsWith("classes/") ? 8 : 0;
|
||||
int index = name.lastIndexOf("/");
|
||||
if (index > start) {
|
||||
return name.substring(start, index).replace('/', '.');
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
private String toPackageName(Path path) {
|
||||
String name = path.toString();
|
||||
assert name.endsWith(".class");
|
||||
|
@ -142,10 +142,11 @@ public interface ModuleReader extends Closeable {
|
||||
* @see ClassLoader#defineClass(String, ByteBuffer, java.security.ProtectionDomain)
|
||||
*/
|
||||
default Optional<ByteBuffer> read(String name) throws IOException {
|
||||
Optional<InputStream> in = open(name);
|
||||
if (in.isPresent()) {
|
||||
byte[] bytes = in.get().readAllBytes();
|
||||
return Optional.of(ByteBuffer.wrap(bytes));
|
||||
Optional<InputStream> oin = open(name);
|
||||
if (oin.isPresent()) {
|
||||
try (InputStream in = oin.get()) {
|
||||
return Optional.of(ByteBuffer.wrap(in.readAllBytes()));
|
||||
}
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2014, 2016, 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
|
||||
@ -32,7 +32,7 @@ import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import jdk.internal.module.Hasher.HashSupplier;
|
||||
import jdk.internal.module.ModuleHashes.HashSupplier;
|
||||
|
||||
|
||||
/**
|
||||
@ -54,12 +54,33 @@ public final class ModuleReference {
|
||||
private final URI location;
|
||||
private final Supplier<ModuleReader> readerSupplier;
|
||||
|
||||
// true if this is a reference to a patched module
|
||||
private boolean patched;
|
||||
|
||||
// the function that computes the hash of this module reference
|
||||
private final HashSupplier hasher;
|
||||
|
||||
// cached hash string to avoid needing to compute it many times
|
||||
private String cachedHash;
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a new instance of this class.
|
||||
*/
|
||||
ModuleReference(ModuleDescriptor descriptor,
|
||||
URI location,
|
||||
Supplier<ModuleReader> readerSupplier,
|
||||
boolean patched,
|
||||
HashSupplier hasher)
|
||||
|
||||
{
|
||||
this.descriptor = Objects.requireNonNull(descriptor);
|
||||
this.location = location;
|
||||
this.readerSupplier = Objects.requireNonNull(readerSupplier);
|
||||
this.patched = patched;
|
||||
this.hasher = hasher;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new instance of this class.
|
||||
*/
|
||||
@ -67,11 +88,9 @@ public final class ModuleReference {
|
||||
URI location,
|
||||
Supplier<ModuleReader> readerSupplier,
|
||||
HashSupplier hasher)
|
||||
|
||||
{
|
||||
this.descriptor = Objects.requireNonNull(descriptor);
|
||||
this.location = location;
|
||||
this.readerSupplier = Objects.requireNonNull(readerSupplier);
|
||||
this.hasher = hasher;
|
||||
this(descriptor, location, readerSupplier, false, hasher);
|
||||
}
|
||||
|
||||
|
||||
@ -96,10 +115,9 @@ public final class ModuleReference {
|
||||
URI location,
|
||||
Supplier<ModuleReader> readerSupplier)
|
||||
{
|
||||
this(descriptor, location, readerSupplier, null);
|
||||
this(descriptor, location, readerSupplier, false, null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the module descriptor.
|
||||
*
|
||||
@ -150,6 +168,20 @@ public final class ModuleReference {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns {@code true} if this module has been patched via -Xpatch.
|
||||
*/
|
||||
boolean isPatched() {
|
||||
return patched;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the hash supplier for this module.
|
||||
*/
|
||||
HashSupplier hasher() {
|
||||
return hasher;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the hash of this module, returning it as a hex string.
|
||||
* Returns {@code null} if the hash cannot be computed.
|
||||
@ -166,8 +198,6 @@ public final class ModuleReference {
|
||||
return result;
|
||||
}
|
||||
|
||||
private int hash;
|
||||
|
||||
/**
|
||||
* Computes a hash code for this module reference.
|
||||
*
|
||||
@ -181,12 +211,17 @@ public final class ModuleReference {
|
||||
public int hashCode() {
|
||||
int hc = hash;
|
||||
if (hc == 0) {
|
||||
hc = Objects.hash(descriptor, location, readerSupplier, hasher);
|
||||
if (hc != 0) hash = hc;
|
||||
hc = Objects.hash(descriptor, location, readerSupplier, hasher,
|
||||
Boolean.valueOf(patched));
|
||||
if (hc == 0)
|
||||
hc = -1;
|
||||
hash = hc;
|
||||
}
|
||||
return hc;
|
||||
}
|
||||
|
||||
private int hash;
|
||||
|
||||
/**
|
||||
* Tests this module reference for equality with the given object.
|
||||
*
|
||||
@ -214,7 +249,8 @@ public final class ModuleReference {
|
||||
return Objects.equals(this.descriptor, that.descriptor)
|
||||
&& Objects.equals(this.location, that.location)
|
||||
&& Objects.equals(this.readerSupplier, that.readerSupplier)
|
||||
&& Objects.equals(this.hasher, that.hasher);
|
||||
&& Objects.equals(this.hasher, that.hasher)
|
||||
&& this.patched == that.patched;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -48,8 +48,8 @@ import java.util.zip.ZipFile;
|
||||
|
||||
import jdk.internal.misc.JavaLangAccess;
|
||||
import jdk.internal.misc.SharedSecrets;
|
||||
import jdk.internal.module.Hasher;
|
||||
import jdk.internal.module.Hasher.HashSupplier;
|
||||
import jdk.internal.module.ModuleHashes;
|
||||
import jdk.internal.module.ModuleHashes.HashSupplier;
|
||||
import jdk.internal.module.ModulePatcher;
|
||||
import sun.net.www.ParseUtil;
|
||||
|
||||
@ -89,7 +89,7 @@ class ModuleReferences {
|
||||
static ModuleReference newJarModule(ModuleDescriptor md, Path file) {
|
||||
URI uri = file.toUri();
|
||||
Supplier<ModuleReader> supplier = () -> new JarModuleReader(file, uri);
|
||||
HashSupplier hasher = (algorithm) -> Hasher.generate(file, algorithm);
|
||||
HashSupplier hasher = (a) -> ModuleHashes.computeHashAsString(file, a);
|
||||
return newModule(md, uri, supplier, hasher);
|
||||
}
|
||||
|
||||
@ -99,7 +99,7 @@ class ModuleReferences {
|
||||
static ModuleReference newJModModule(ModuleDescriptor md, Path file) {
|
||||
URI uri = file.toUri();
|
||||
Supplier<ModuleReader> supplier = () -> new JModModuleReader(file, uri);
|
||||
HashSupplier hasher = (algorithm) -> Hasher.generate(file, algorithm);
|
||||
HashSupplier hasher = (a) -> ModuleHashes.computeHashAsString(file, a);
|
||||
return newModule(md, file.toUri(), supplier, hasher);
|
||||
}
|
||||
|
||||
@ -122,7 +122,7 @@ class ModuleReferences {
|
||||
private final ReadWriteLock lock = new ReentrantReadWriteLock();
|
||||
private final Lock readLock = lock.readLock();
|
||||
private final Lock writeLock = lock.writeLock();
|
||||
private volatile boolean closed;
|
||||
private boolean closed;
|
||||
|
||||
SafeCloseModuleReader() { }
|
||||
|
||||
@ -198,7 +198,10 @@ class ModuleReferences {
|
||||
|
||||
static JarFile newJarFile(Path path) {
|
||||
try {
|
||||
return new JarFile(path.toFile());
|
||||
return new JarFile(path.toFile(),
|
||||
true, // verify
|
||||
ZipFile.OPEN_READ,
|
||||
JarFile.Release.RUNTIME);
|
||||
} catch (IOException ioe) {
|
||||
throw new UncheckedIOException(ioe);
|
||||
}
|
||||
@ -219,6 +222,8 @@ class ModuleReferences {
|
||||
if (je != null) {
|
||||
String encodedPath = ParseUtil.encodePath(name, false);
|
||||
String uris = "jar:" + uri + "!/" + encodedPath;
|
||||
if (jf.isMultiRelease())
|
||||
uris += "#runtime";
|
||||
return Optional.of(URI.create(uris));
|
||||
} else {
|
||||
return Optional.empty();
|
||||
|
@ -25,8 +25,8 @@
|
||||
|
||||
package java.lang.module;
|
||||
|
||||
import java.io.PrintStream;
|
||||
import java.lang.module.ModuleDescriptor.Requires.Modifier;
|
||||
import java.lang.reflect.Layer;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
@ -43,7 +43,7 @@ import java.util.Set;
|
||||
import java.util.StringJoiner;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import jdk.internal.module.Hasher;
|
||||
import jdk.internal.module.ModuleHashes;
|
||||
|
||||
/**
|
||||
* The resolver used by {@link Configuration#resolveRequires} and
|
||||
@ -55,6 +55,7 @@ final class Resolver {
|
||||
private final ModuleFinder beforeFinder;
|
||||
private final Configuration parent;
|
||||
private final ModuleFinder afterFinder;
|
||||
private final PrintStream traceOutput;
|
||||
|
||||
// maps module name to module reference
|
||||
private final Map<String, ModuleReference> nameToReference = new HashMap<>();
|
||||
@ -62,10 +63,12 @@ final class Resolver {
|
||||
|
||||
Resolver(ModuleFinder beforeFinder,
|
||||
Configuration parent,
|
||||
ModuleFinder afterFinder) {
|
||||
ModuleFinder afterFinder,
|
||||
PrintStream traceOutput) {
|
||||
this.beforeFinder = beforeFinder;
|
||||
this.parent = parent;
|
||||
this.afterFinder = afterFinder;
|
||||
this.traceOutput = traceOutput;
|
||||
}
|
||||
|
||||
|
||||
@ -76,8 +79,6 @@ final class Resolver {
|
||||
*/
|
||||
Resolver resolveRequires(Collection<String> roots) {
|
||||
|
||||
long start = trace_start("Resolve");
|
||||
|
||||
// create the visit stack to get us started
|
||||
Deque<ModuleDescriptor> q = new ArrayDeque<>();
|
||||
for (String root : roots) {
|
||||
@ -95,10 +96,9 @@ final class Resolver {
|
||||
}
|
||||
}
|
||||
|
||||
if (TRACE) {
|
||||
if (isTracing()) {
|
||||
trace("Root module %s located", root);
|
||||
if (mref.location().isPresent())
|
||||
trace(" (%s)", mref.location().get());
|
||||
mref.location().ifPresent(uri -> trace(" (%s)", uri));
|
||||
}
|
||||
|
||||
assert mref.descriptor().name().equals(root);
|
||||
@ -108,13 +108,6 @@ final class Resolver {
|
||||
|
||||
resolve(q);
|
||||
|
||||
if (TRACE) {
|
||||
long duration = System.currentTimeMillis() - start;
|
||||
Set<String> names = nameToReference.keySet();
|
||||
trace("Resolver completed in %s ms", duration);
|
||||
names.stream().sorted().forEach(name -> trace(" %s", name));
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -153,11 +146,10 @@ final class Resolver {
|
||||
q.offer(mref.descriptor());
|
||||
resolved.add(mref.descriptor());
|
||||
|
||||
if (TRACE) {
|
||||
if (isTracing()) {
|
||||
trace("Module %s located, required by %s",
|
||||
dn, descriptor.name());
|
||||
if (mref.location().isPresent())
|
||||
trace(" (%s)", mref.location().get());
|
||||
mref.location().ifPresent(uri -> trace(" (%s)", uri));
|
||||
}
|
||||
}
|
||||
|
||||
@ -175,8 +167,6 @@ final class Resolver {
|
||||
*/
|
||||
Resolver resolveUses() {
|
||||
|
||||
long start = trace_start("Bind");
|
||||
|
||||
// Scan the finders for all available service provider modules. As
|
||||
// java.base uses services then then module finders will be scanned
|
||||
// anyway.
|
||||
@ -230,10 +220,10 @@ final class Resolver {
|
||||
|
||||
String pn = provider.name();
|
||||
if (!nameToReference.containsKey(pn)) {
|
||||
|
||||
if (TRACE && mref.location().isPresent())
|
||||
trace(" (%s)", mref.location().get());
|
||||
|
||||
if (isTracing()) {
|
||||
mref.location()
|
||||
.ifPresent(uri -> trace(" (%s)", uri));
|
||||
}
|
||||
nameToReference.put(pn, mref);
|
||||
q.push(provider);
|
||||
}
|
||||
@ -248,14 +238,6 @@ final class Resolver {
|
||||
|
||||
} while (!candidateConsumers.isEmpty());
|
||||
|
||||
|
||||
if (TRACE) {
|
||||
long duration = System.currentTimeMillis() - start;
|
||||
Set<String> names = nameToReference.keySet();
|
||||
trace("Bind completed in %s ms", duration);
|
||||
names.stream().sorted().forEach(name -> trace(" %s", name));
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -264,23 +246,33 @@ final class Resolver {
|
||||
* 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
|
||||
*/
|
||||
Map<ResolvedModule, Set<ResolvedModule>> finish(Configuration cf) {
|
||||
Map<ResolvedModule, Set<ResolvedModule>> finish(Configuration cf,
|
||||
boolean check)
|
||||
{
|
||||
if (isTracing()) {
|
||||
trace("Result:");
|
||||
Set<String> names = nameToReference.keySet();
|
||||
names.stream().sorted().forEach(name -> trace(" %s", name));
|
||||
}
|
||||
|
||||
detectCycles();
|
||||
|
||||
checkPlatformConstraints();
|
||||
|
||||
checkHashes();
|
||||
if (check) {
|
||||
detectCycles();
|
||||
checkPlatformConstraints();
|
||||
checkHashes();
|
||||
}
|
||||
|
||||
Map<ResolvedModule, Set<ResolvedModule>> graph = makeGraph(cf);
|
||||
|
||||
checkExportSuppliers(graph);
|
||||
if (check) {
|
||||
checkExportSuppliers(graph);
|
||||
}
|
||||
|
||||
return graph;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks the given module graph for cycles.
|
||||
*
|
||||
@ -420,52 +412,44 @@ final class Resolver {
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks the hashes in the module descriptor to ensure that they match
|
||||
* the hash of the dependency's module reference.
|
||||
* any recorded hashes.
|
||||
*/
|
||||
private void checkHashes() {
|
||||
|
||||
for (ModuleReference mref : nameToReference.values()) {
|
||||
ModuleDescriptor descriptor = mref.descriptor();
|
||||
|
||||
// get map of module names to hash
|
||||
Optional<Hasher.DependencyHashes> ohashes = descriptor.hashes();
|
||||
// get map of module hashes
|
||||
Optional<ModuleHashes> ohashes = descriptor.hashes();
|
||||
if (!ohashes.isPresent())
|
||||
continue;
|
||||
Hasher.DependencyHashes hashes = ohashes.get();
|
||||
|
||||
// check dependences
|
||||
for (ModuleDescriptor.Requires d : descriptor.requires()) {
|
||||
String dn = d.name();
|
||||
String recordedHash = hashes.hashFor(dn);
|
||||
|
||||
if (recordedHash != null) {
|
||||
|
||||
ModuleReference other = nameToReference.get(dn);
|
||||
if (other == null) {
|
||||
other = parent.findModule(dn)
|
||||
.map(ResolvedModule::reference)
|
||||
.orElse(null);
|
||||
}
|
||||
if (other == null)
|
||||
throw new InternalError(dn + " not found");
|
||||
|
||||
String actualHash = other.computeHash(hashes.algorithm());
|
||||
if (actualHash == null)
|
||||
fail("Unable to compute the hash of module %s", dn);
|
||||
|
||||
if (!recordedHash.equals(actualHash)) {
|
||||
fail("Hash of %s (%s) differs to expected hash (%s)",
|
||||
dn, actualHash, recordedHash);
|
||||
}
|
||||
ModuleHashes hashes = ohashes.get();
|
||||
|
||||
String algorithm = hashes.algorithm();
|
||||
for (String dn : hashes.names()) {
|
||||
ModuleReference other = nameToReference.get(dn);
|
||||
if (other == null) {
|
||||
other = parent.findModule(dn)
|
||||
.map(ResolvedModule::reference)
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
// skip checking the hash if the module has been patched
|
||||
if (other != null && !other.isPatched()) {
|
||||
String recordedHash = hashes.hashFor(dn);
|
||||
String actualHash = other.computeHash(algorithm);
|
||||
if (actualHash == null)
|
||||
fail("Unable to compute the hash of module %s", dn);
|
||||
if (!recordedHash.equals(actualHash)) {
|
||||
fail("Hash of %s (%s) differs to expected hash (%s)" +
|
||||
" recorded in %s", dn, actualHash, recordedHash,
|
||||
descriptor.name());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -666,7 +650,7 @@ final class Resolver {
|
||||
// source is exported to descriptor2
|
||||
String source = export.source();
|
||||
ModuleDescriptor other
|
||||
= packageToExporter.put(source, descriptor2);
|
||||
= packageToExporter.put(source, descriptor2);
|
||||
|
||||
if (other != null && other != descriptor2) {
|
||||
// package might be local to descriptor1
|
||||
@ -690,33 +674,38 @@ final class Resolver {
|
||||
}
|
||||
}
|
||||
|
||||
// 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",
|
||||
descriptor1.name(), pn);
|
||||
}
|
||||
}
|
||||
// uses/provides checks not applicable to automatic modules
|
||||
if (!descriptor1.isAutomatic()) {
|
||||
|
||||
// provides S
|
||||
for (Map.Entry<String, ModuleDescriptor.Provides> entry :
|
||||
descriptor1.provides().entrySet()) {
|
||||
String service = entry.getKey();
|
||||
ModuleDescriptor.Provides provides = entry.getValue();
|
||||
|
||||
String pn = packageName(service);
|
||||
if (!packageToExporter.containsKey(pn)) {
|
||||
fail("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());
|
||||
// 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",
|
||||
descriptor1.name(), pn);
|
||||
}
|
||||
}
|
||||
|
||||
// provides S
|
||||
for (Map.Entry<String, ModuleDescriptor.Provides> entry :
|
||||
descriptor1.provides().entrySet()) {
|
||||
String service = entry.getKey();
|
||||
ModuleDescriptor.Provides provides = entry.getValue();
|
||||
|
||||
String pn = packageName(service);
|
||||
if (!packageToExporter.containsKey(pn)) {
|
||||
fail("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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -796,27 +785,18 @@ final class Resolver {
|
||||
throw new ResolutionException(msg);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tracing support, limited to boot layer for now.
|
||||
* Tracing support
|
||||
*/
|
||||
|
||||
private final static boolean TRACE
|
||||
= Boolean.getBoolean("jdk.launcher.traceResolver")
|
||||
&& (Layer.boot() == null);
|
||||
|
||||
private String op;
|
||||
|
||||
private long trace_start(String op) {
|
||||
this.op = op;
|
||||
return System.currentTimeMillis();
|
||||
private boolean isTracing() {
|
||||
return traceOutput != null;
|
||||
}
|
||||
|
||||
private void trace(String fmt, Object ... args) {
|
||||
if (TRACE) {
|
||||
System.out.print("[" + op + "] ");
|
||||
System.out.format(fmt, args);
|
||||
System.out.println();
|
||||
if (traceOutput != null) {
|
||||
traceOutput.format("[Resolver] " + fmt, args);
|
||||
traceOutput.println();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -44,6 +44,7 @@ import java.util.function.Supplier;
|
||||
import jdk.internal.jimage.ImageLocation;
|
||||
import jdk.internal.jimage.ImageReader;
|
||||
import jdk.internal.jimage.ImageReaderFactory;
|
||||
import jdk.internal.module.ModuleHashes;
|
||||
import jdk.internal.module.SystemModules;
|
||||
import jdk.internal.module.ModulePatcher;
|
||||
import jdk.internal.perf.PerfCounter;
|
||||
@ -101,13 +102,16 @@ class SystemModuleFinder implements ModuleFinder {
|
||||
for (int i = 0; i < n; i++) {
|
||||
String mn = moduleNames[i];
|
||||
ModuleDescriptor md;
|
||||
String hash;
|
||||
if (fastLoad) {
|
||||
md = descriptors[i];
|
||||
hash = SystemModules.MODULES_TO_HASH[i];
|
||||
} else {
|
||||
// fallback to read module-info.class
|
||||
// if fast loading of ModuleDescriptors is disabled
|
||||
ImageLocation location = imageReader.findLocation(mn, "module-info.class");
|
||||
md = ModuleDescriptor.read(imageReader.getResourceBuffer(location));
|
||||
hash = null;
|
||||
}
|
||||
if (!md.name().equals(mn))
|
||||
throw new InternalError();
|
||||
@ -123,7 +127,8 @@ class SystemModuleFinder implements ModuleFinder {
|
||||
}
|
||||
};
|
||||
|
||||
ModuleReference mref = new ModuleReference(md, uri, readerSupplier);
|
||||
ModuleReference mref =
|
||||
new ModuleReference(md, uri, readerSupplier, hashSupplier(hash));
|
||||
|
||||
// may need a reference to a patched module if -Xpatch specified
|
||||
mref = ModulePatcher.interposeIfNeeded(mref);
|
||||
@ -142,6 +147,18 @@ class SystemModuleFinder implements ModuleFinder {
|
||||
initTime.addElapsedTimeFrom(t0);
|
||||
}
|
||||
|
||||
private static ModuleHashes.HashSupplier hashSupplier(String hash) {
|
||||
if (hash == null)
|
||||
return null;
|
||||
|
||||
return new ModuleHashes.HashSupplier() {
|
||||
@Override
|
||||
public String generate(String algorithm) {
|
||||
return hash;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
SystemModuleFinder() { }
|
||||
|
||||
@Override
|
||||
|
@ -27,6 +27,7 @@ package java.lang.reflect;
|
||||
|
||||
import java.lang.module.Configuration;
|
||||
import java.lang.module.ModuleDescriptor;
|
||||
import java.lang.module.ModuleDescriptor.Provides;
|
||||
import java.lang.module.ResolvedModule;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
@ -41,6 +42,8 @@ import java.util.stream.Collectors;
|
||||
import jdk.internal.loader.Loader;
|
||||
import jdk.internal.loader.LoaderPool;
|
||||
import jdk.internal.misc.SharedSecrets;
|
||||
import jdk.internal.module.ServicesCatalog;
|
||||
import jdk.internal.module.ServicesCatalog.ServiceProvider;
|
||||
import sun.security.util.SecurityConstants;
|
||||
|
||||
|
||||
@ -549,4 +552,55 @@ public final class Layer {
|
||||
public static Layer boot() {
|
||||
return SharedSecrets.getJavaLangAccess().getBootLayer();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the ServicesCatalog for this Layer, creating it if not
|
||||
* already created.
|
||||
*/
|
||||
ServicesCatalog getServicesCatalog() {
|
||||
ServicesCatalog servicesCatalog = this.servicesCatalog;
|
||||
if (servicesCatalog != null)
|
||||
return servicesCatalog;
|
||||
|
||||
Map<String, Set<ServiceProvider>> map = new HashMap<>();
|
||||
for (Module m : nameToModule.values()) {
|
||||
ModuleDescriptor descriptor = m.getDescriptor();
|
||||
for (Provides provides : descriptor.provides().values()) {
|
||||
String service = provides.service();
|
||||
Set<ServiceProvider> providers
|
||||
= map.computeIfAbsent(service, k -> new HashSet<>());
|
||||
for (String pn : provides.providers()) {
|
||||
providers.add(new ServiceProvider(m, pn));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ServicesCatalog catalog = new ServicesCatalog() {
|
||||
@Override
|
||||
public void register(Module module) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
@Override
|
||||
public Set<ServiceProvider> findServices(String service) {
|
||||
Set<ServiceProvider> providers = map.get(service);
|
||||
if (providers == null) {
|
||||
return Collections.emptySet();
|
||||
} else {
|
||||
return Collections.unmodifiableSet(providers);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
synchronized (this) {
|
||||
servicesCatalog = this.servicesCatalog;
|
||||
if (servicesCatalog == null) {
|
||||
this.servicesCatalog = servicesCatalog = catalog;
|
||||
}
|
||||
}
|
||||
|
||||
return servicesCatalog;
|
||||
}
|
||||
|
||||
private volatile ServicesCatalog servicesCatalog;
|
||||
}
|
||||
|
@ -43,11 +43,7 @@ import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReadWriteLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@ -142,9 +138,6 @@ public final class Module {
|
||||
this.name = null;
|
||||
this.loader = loader;
|
||||
this.descriptor = null;
|
||||
|
||||
// unnamed modules are loose
|
||||
this.loose = true;
|
||||
}
|
||||
|
||||
|
||||
@ -245,17 +238,27 @@ public final class Module {
|
||||
}
|
||||
|
||||
|
||||
// -- readability --
|
||||
// --
|
||||
|
||||
// true if this module reads all unnamed modules (a.k.a. loose module)
|
||||
private volatile boolean loose;
|
||||
// the special Module to mean reads or exported to "all unnamed modules"
|
||||
private static final Module ALL_UNNAMED_MODULE = new Module(null);
|
||||
|
||||
// special Module to mean exported to "everyone"
|
||||
private static final Module EVERYONE_MODULE = new Module(null);
|
||||
|
||||
// exported to all modules
|
||||
private static final Set<Module> EVERYONE = Collections.singleton(EVERYONE_MODULE);
|
||||
|
||||
|
||||
// -- readability --
|
||||
|
||||
// the modules that this module permanently reads
|
||||
// (will be final when the modules are defined in reverse topology order)
|
||||
private volatile Set<Module> reads;
|
||||
|
||||
// created lazily, additional modules that this module reflectively reads
|
||||
private volatile WeakSet<Module> transientReads;
|
||||
// additional module (2nd key) that some module (1st key) reflectively reads
|
||||
private static final WeakPairMap<Module, Module, Boolean> transientReads
|
||||
= new WeakPairMap<>();
|
||||
|
||||
|
||||
/**
|
||||
@ -284,22 +287,19 @@ public final class Module {
|
||||
|
||||
// check if this module reads other
|
||||
if (other.isNamed()) {
|
||||
|
||||
Set<Module> reads = this.reads; // volatile read
|
||||
if (reads != null && reads.contains(other))
|
||||
return true;
|
||||
|
||||
} else {
|
||||
|
||||
// loose modules read all unnamed modules
|
||||
if (this.loose)
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
// check if this module reads the other module reflectively
|
||||
WeakSet<Module> tr = this.transientReads; // volatile read
|
||||
if (tr != null && tr.contains(other))
|
||||
if (transientReads.containsKeyPair(this, other))
|
||||
return true;
|
||||
|
||||
// if other is an unnamed module then check if this module reads
|
||||
// all unnamed modules
|
||||
if (!other.isNamed()
|
||||
&& transientReads.containsKeyPair(this, ALL_UNNAMED_MODULE))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
@ -346,8 +346,7 @@ public final class Module {
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the given {@code Module} readable to this module without
|
||||
* notifying the VM.
|
||||
* Updates this module to read another module without notifying the VM.
|
||||
*
|
||||
* @apiNote This method is for VM white-box testing.
|
||||
*/
|
||||
@ -361,40 +360,28 @@ public final class Module {
|
||||
* If {@code syncVM} is {@code true} then the VM is notified.
|
||||
*/
|
||||
private void implAddReads(Module other, boolean syncVM) {
|
||||
Objects.requireNonNull(other);
|
||||
|
||||
// nothing to do
|
||||
if (other == this || !this.isNamed())
|
||||
return;
|
||||
|
||||
// if the other is null then change this module to be loose.
|
||||
if (other == null) {
|
||||
if (syncVM)
|
||||
addReads0(this, null);
|
||||
this.loose = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// check if we already read this module
|
||||
Set<Module> reads = this.reads;
|
||||
if (reads != null && reads.contains(other))
|
||||
return;
|
||||
|
||||
// update VM first, just in case it fails
|
||||
if (syncVM)
|
||||
addReads0(this, other);
|
||||
|
||||
// add reflective read
|
||||
WeakSet<Module> tr = this.transientReads;
|
||||
if (tr == null) {
|
||||
synchronized (this) {
|
||||
tr = this.transientReads;
|
||||
if (tr == null) {
|
||||
tr = new WeakSet<>();
|
||||
this.transientReads = tr;
|
||||
}
|
||||
if (syncVM) {
|
||||
if (other == ALL_UNNAMED_MODULE) {
|
||||
addReads0(this, null);
|
||||
} else {
|
||||
addReads0(this, other);
|
||||
}
|
||||
}
|
||||
tr.add(other);
|
||||
|
||||
// add reflective read
|
||||
transientReads.putIfAbsent(this, other, Boolean.TRUE);
|
||||
}
|
||||
|
||||
|
||||
@ -404,15 +391,10 @@ public final class Module {
|
||||
// (will be final when the modules are defined in reverse topology order)
|
||||
private volatile Map<String, Set<Module>> exports;
|
||||
|
||||
// created lazily, additional exports added at run-time
|
||||
private volatile Map<String, WeakSet<Module>> transientExports;
|
||||
|
||||
// the special Module to mean exported to all modules
|
||||
private static final Module EVERYONE_MODULE = new Module(null);
|
||||
private static final Set<Module> EVERYONE = Collections.singleton(EVERYONE_MODULE);
|
||||
|
||||
// the special Module to mean exported to all unnamed modules
|
||||
private static final Module ALL_UNNAMED_MODULE = new Module(null);
|
||||
// additional exports added at run-time
|
||||
// this module (1st key), other module (2nd key), exported packages (value)
|
||||
private static final WeakPairMap<Module, Module, Map<String, Boolean>>
|
||||
transientExports = new WeakPairMap<>();
|
||||
|
||||
|
||||
/**
|
||||
@ -489,23 +471,9 @@ public final class Module {
|
||||
if (exports != null) {
|
||||
Set<Module> targets = exports.get(pn);
|
||||
|
||||
if (targets != null) {
|
||||
|
||||
// exported to all modules
|
||||
if (targets.contains(EVERYONE_MODULE))
|
||||
return true;
|
||||
|
||||
if (other != EVERYONE_MODULE) {
|
||||
// exported to other
|
||||
if (targets.contains(other))
|
||||
return true;
|
||||
|
||||
// other is an unnamed module && exported to all unnamed
|
||||
if (!other.isNamed() && targets.contains(ALL_UNNAMED_MODULE))
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
if ((targets != null)
|
||||
&& (targets.contains(other) || targets.contains(EVERYONE_MODULE)))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -515,29 +483,27 @@ public final class Module {
|
||||
* package package to the given module.
|
||||
*/
|
||||
private boolean isExportedReflectively(String pn, Module other) {
|
||||
Map<String, WeakSet<Module>> te = this.transientExports;
|
||||
if (te != null) {
|
||||
WeakSet<Module> targets = te.get(pn);
|
||||
// exported to all modules
|
||||
Map<String, ?> exports = transientExports.get(this, EVERYONE_MODULE);
|
||||
if (exports != null && exports.containsKey(pn))
|
||||
return true;
|
||||
|
||||
if (targets != null) {
|
||||
if (other != EVERYONE_MODULE) {
|
||||
|
||||
// exported to all modules
|
||||
if (targets.contains(EVERYONE_MODULE))
|
||||
// exported to other
|
||||
exports = transientExports.get(this, other);
|
||||
if (exports != null && exports.containsKey(pn))
|
||||
return true;
|
||||
|
||||
// other is an unnamed module && exported to all unnamed
|
||||
if (!other.isNamed()) {
|
||||
exports = transientExports.get(this, ALL_UNNAMED_MODULE);
|
||||
if (exports != null && exports.containsKey(pn))
|
||||
return true;
|
||||
|
||||
if (other != EVERYONE_MODULE) {
|
||||
|
||||
// exported to other
|
||||
if (targets.contains(other))
|
||||
return true;
|
||||
|
||||
// other is an unnamed module && exported to all unnamed
|
||||
if (!other.isNamed() && targets.contains(ALL_UNNAMED_MODULE))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -638,34 +604,19 @@ public final class Module {
|
||||
}
|
||||
}
|
||||
|
||||
// create transientExports if needed
|
||||
Map<String, WeakSet<Module>> te = this.transientExports; // read
|
||||
if (te == null) {
|
||||
synchronized (this) {
|
||||
te = this.transientExports;
|
||||
if (te == null) {
|
||||
te = new ConcurrentHashMap<>();
|
||||
this.transientExports = te; // volatile write
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// add package name to transientExports if absent
|
||||
WeakSet<Module> s = te.get(pn);
|
||||
if (s == null) {
|
||||
s = new WeakSet<>();
|
||||
WeakSet<Module> prev = te.putIfAbsent(pn, s);
|
||||
if (prev != null)
|
||||
s = prev;
|
||||
}
|
||||
s.add(other);
|
||||
transientExports
|
||||
.computeIfAbsent(this, other,
|
||||
(_this, _other) -> new ConcurrentHashMap<>())
|
||||
.putIfAbsent(pn, Boolean.TRUE);
|
||||
}
|
||||
|
||||
|
||||
// -- services --
|
||||
|
||||
// created lazily, additional service types that this module uses
|
||||
private volatile WeakSet<Class<?>> transientUses;
|
||||
// additional service type (2nd key) that some module (1st key) uses
|
||||
private static final WeakPairMap<Module, Class<?>, Boolean> transientUses
|
||||
= new WeakPairMap<>();
|
||||
|
||||
/**
|
||||
* If the caller's module is this module then update this module to add a
|
||||
@ -702,17 +653,7 @@ public final class Module {
|
||||
}
|
||||
|
||||
if (!canUse(st)) {
|
||||
WeakSet<Class<?>> uses = this.transientUses;
|
||||
if (uses == null) {
|
||||
synchronized (this) {
|
||||
uses = this.transientUses;
|
||||
if (uses == null) {
|
||||
uses = new WeakSet<>();
|
||||
this.transientUses = uses;
|
||||
}
|
||||
}
|
||||
}
|
||||
uses.add(st);
|
||||
transientUses.putIfAbsent(this, st, Boolean.TRUE);
|
||||
}
|
||||
|
||||
}
|
||||
@ -746,11 +687,7 @@ public final class Module {
|
||||
return true;
|
||||
|
||||
// uses added via addUses
|
||||
WeakSet<Class<?>> uses = this.transientUses;
|
||||
if (uses != null && uses.contains(st))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
return transientUses.containsKeyPair(this, st);
|
||||
}
|
||||
|
||||
|
||||
@ -885,7 +822,7 @@ public final class Module {
|
||||
// -- creating Module objects --
|
||||
|
||||
/**
|
||||
* Find the runtime Module corresponding to the given ReadDependence
|
||||
* Find the runtime Module corresponding to the given ResolvedModule
|
||||
* in the given parent Layer (or its parents).
|
||||
*/
|
||||
private static Module find(ResolvedModule resolvedModule, Layer layer) {
|
||||
@ -969,7 +906,7 @@ public final class Module {
|
||||
|
||||
// automatic modules reads all unnamed modules
|
||||
if (descriptor.isAutomatic()) {
|
||||
m.implAddReads(null, true);
|
||||
m.implAddReads(ALL_UNNAMED_MODULE, true);
|
||||
}
|
||||
|
||||
// exports
|
||||
@ -1097,7 +1034,7 @@ public final class Module {
|
||||
* the representation is the string {@code "module"}, followed by a space,
|
||||
* and then the module name. For an unnamed module, the representation is
|
||||
* the string {@code "unnamed module"}, followed by a space, and then an
|
||||
* implementation specific identifier for the unnamed module.
|
||||
* implementation specific string that identifies the unnamed module.
|
||||
*
|
||||
* @return The string representation of this module
|
||||
*/
|
||||
@ -1112,46 +1049,6 @@ public final class Module {
|
||||
}
|
||||
|
||||
|
||||
// -- supporting classes --
|
||||
|
||||
|
||||
/**
|
||||
* A "not-a-Set" set of weakly referenced objects that supports concurrent
|
||||
* access.
|
||||
*/
|
||||
private static class WeakSet<E> {
|
||||
private final ReadWriteLock lock = new ReentrantReadWriteLock();
|
||||
private final Lock readLock = lock.readLock();
|
||||
private final Lock writeLock = lock.writeLock();
|
||||
|
||||
private final WeakHashMap<E, Boolean> map = new WeakHashMap<>();
|
||||
|
||||
/**
|
||||
* Adds the specified element to the set.
|
||||
*/
|
||||
void add(E e) {
|
||||
writeLock.lock();
|
||||
try {
|
||||
map.put(e, Boolean.TRUE);
|
||||
} finally {
|
||||
writeLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if this set contains the specified element.
|
||||
*/
|
||||
boolean contains(E e) {
|
||||
readLock.lock();
|
||||
try {
|
||||
return map.containsKey(e);
|
||||
} finally {
|
||||
readLock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// -- native methods --
|
||||
|
||||
// JVM_DefineModule
|
||||
@ -1196,8 +1093,12 @@ public final class Module {
|
||||
m1.implAddReads(m2, true);
|
||||
}
|
||||
@Override
|
||||
public void addReadsAllUnnamed(Module m) {
|
||||
m.implAddReads(Module.ALL_UNNAMED_MODULE);
|
||||
}
|
||||
@Override
|
||||
public void addExports(Module m, String pn, Module other) {
|
||||
m.implAddExports(pn, Objects.requireNonNull(other), true);
|
||||
m.implAddExports(pn, other, true);
|
||||
}
|
||||
@Override
|
||||
public void addExportsToAll(Module m, String pn) {
|
||||
@ -1211,6 +1112,10 @@ public final class Module {
|
||||
public void addPackage(Module m, String pn) {
|
||||
m.implAddPackage(pn, true);
|
||||
}
|
||||
@Override
|
||||
public ServicesCatalog getServicesCatalog(Layer layer) {
|
||||
return layer.getServicesCatalog();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,354 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 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
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package java.lang.reflect;
|
||||
|
||||
import java.lang.ref.Reference;
|
||||
import java.lang.ref.ReferenceQueue;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Collection;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
/**
|
||||
* A WeakHashMap-like data structure that uses a pair of weakly-referenced keys
|
||||
* with identity equality semantics to associate a strongly-referenced value.
|
||||
* Unlike WeakHashMap, this data structure is thread-safe.
|
||||
*
|
||||
* @param <K1> the type of 1st key in key pair
|
||||
* @param <K2> the type of 2nd key in key pair
|
||||
* @param <V> the type of value
|
||||
* @author Peter Levart
|
||||
*/
|
||||
final class WeakPairMap<K1, K2, V> {
|
||||
|
||||
private final ConcurrentHashMap<Pair<K1, K2>, V> map = new ConcurrentHashMap<>();
|
||||
private final ReferenceQueue<Object> queue = new ReferenceQueue<>();
|
||||
|
||||
/**
|
||||
* Tests if the specified pair of keys are associated with a value
|
||||
* in the WeakPairMap.
|
||||
*
|
||||
* @param k1 the 1st of the pair of keys
|
||||
* @param k2 the 2nd of the pair of keys
|
||||
* @return true if and only if the specified key pair is in this WeakPairMap,
|
||||
* as determined by the identity comparison; false otherwise
|
||||
* @throws NullPointerException if any of the specified keys is null
|
||||
*/
|
||||
public boolean containsKeyPair(K1 k1, K2 k2) {
|
||||
expungeStaleAssociations();
|
||||
return map.containsKey(Pair.lookup(k1, k2));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value to which the specified pair of keys is mapped, or null
|
||||
* if this WeakPairMap contains no mapping for the key pair.
|
||||
* <p>More formally, if this WeakPairMap contains a mapping from a key pair
|
||||
* {@code (_k1, _k2)} to a value {@code v} such that
|
||||
* {@code k1 == _k1 && k2 == _k2}, then this method returns {@code v};
|
||||
* otherwise it returns {@code null}.
|
||||
* (There can be at most one such mapping.)
|
||||
*
|
||||
* @param k1 the 1st of the pair of keys for which the mapped value is to
|
||||
* be returned
|
||||
* @param k2 the 2nd of the pair of keys for which the mapped value is to
|
||||
* be returned
|
||||
* @return the value to which the specified key pair is mapped, or null if
|
||||
* this map contains no mapping for the key pair
|
||||
* @throws NullPointerException if any of the specified keys is null
|
||||
*/
|
||||
public V get(K1 k1, K2 k2) {
|
||||
expungeStaleAssociations();
|
||||
return map.get(Pair.lookup(k1, k2));
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps the specified key pair to the specified value in this WeakPairMap.
|
||||
* Neither the keys nor the value can be null.
|
||||
* <p>The value can be retrieved by calling the {@link #get} method
|
||||
* with the the same keys (compared by identity).
|
||||
*
|
||||
* @param k1 the 1st of the pair of keys with which the specified value is to
|
||||
* be associated
|
||||
* @param k2 the 2nd of the pair of keys with which the specified value is to
|
||||
* be associated
|
||||
* @param v value to be associated with the specified key pair
|
||||
* @return the previous value associated with key pair, or {@code null} if
|
||||
* there was no mapping for key pair
|
||||
* @throws NullPointerException if any of the specified keys or value is null
|
||||
*/
|
||||
public V put(K1 k1, K2 k2, V v) {
|
||||
expungeStaleAssociations();
|
||||
return map.put(Pair.weak(k1, k2, queue), v);
|
||||
}
|
||||
|
||||
/**
|
||||
* If the specified key pair is not already associated with a value,
|
||||
* associates it with the given value and returns {@code null}, else does
|
||||
* nothing and returns the currently associated value.
|
||||
*
|
||||
* @param k1 the 1st of the pair of keys with which the specified value is to
|
||||
* be associated
|
||||
* @param k2 the 2nd of the pair of keys with which the specified value is to
|
||||
* be associated
|
||||
* @param v value to be associated with the specified key pair
|
||||
* @return the previous value associated with key pair, or {@code null} if
|
||||
* there was no mapping for key pair
|
||||
* @throws NullPointerException if any of the specified keys or value is null
|
||||
*/
|
||||
public V putIfAbsent(K1 k1, K2 k2, V v) {
|
||||
expungeStaleAssociations();
|
||||
return map.putIfAbsent(Pair.weak(k1, k2, queue), v);
|
||||
}
|
||||
|
||||
/**
|
||||
* If the specified key pair is not already associated with a value,
|
||||
* attempts to compute its value using the given mapping function
|
||||
* and enters it into this WeakPairMap unless {@code null}. The entire
|
||||
* method invocation is performed atomically, so the function is
|
||||
* applied at most once per key pair. Some attempted update operations
|
||||
* on this WeakPairMap by other threads may be blocked while computation
|
||||
* is in progress, so the computation should be short and simple,
|
||||
* and must not attempt to update any other mappings of this WeakPairMap.
|
||||
*
|
||||
* @param k1 the 1st of the pair of keys with which the
|
||||
* computed value is to be associated
|
||||
* @param k2 the 2nd of the pair of keys with which the
|
||||
* computed value is to be associated
|
||||
* @param mappingFunction the function to compute a value
|
||||
* @return the current (existing or computed) value associated with
|
||||
* the specified key pair, or null if the computed value is null
|
||||
* @throws NullPointerException if any of the specified keys or
|
||||
* mappingFunction is null
|
||||
* @throws IllegalStateException if the computation detectably
|
||||
* attempts a recursive update to this map
|
||||
* that would otherwise never complete
|
||||
* @throws RuntimeException or Error if the mappingFunction does so, in
|
||||
* which case the mapping is left unestablished
|
||||
*/
|
||||
public V computeIfAbsent(K1 k1, K2 k2,
|
||||
BiFunction<? super K1, ? super K2, ? extends V>
|
||||
mappingFunction) {
|
||||
expungeStaleAssociations();
|
||||
try {
|
||||
return map.computeIfAbsent(
|
||||
Pair.weak(k1, k2, queue),
|
||||
pair -> mappingFunction.apply(pair.first(), pair.second()));
|
||||
} finally {
|
||||
Reference.reachabilityFence(k1);
|
||||
Reference.reachabilityFence(k2);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link Collection} view of the values contained in this
|
||||
* WeakPairMap. The collection is backed by the WeakPairMap, so changes to
|
||||
* the map are reflected in the collection, and vice-versa. The collection
|
||||
* supports element removal, which removes the corresponding
|
||||
* mapping from this map, via the {@code Iterator.remove},
|
||||
* {@code Collection.remove}, {@code removeAll},
|
||||
* {@code retainAll}, and {@code clear} operations. It does not
|
||||
* support the {@code add} or {@code addAll} operations.
|
||||
*
|
||||
* @return the collection view
|
||||
*/
|
||||
public Collection<V> values() {
|
||||
expungeStaleAssociations();
|
||||
return map.values();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes associations from this WeakPairMap for which at least one of the
|
||||
* keys in key pair has been found weakly-reachable and corresponding
|
||||
* WeakRefPeer(s) enqueued. Called as part of each public operation.
|
||||
*/
|
||||
private void expungeStaleAssociations() {
|
||||
WeakRefPeer<?> peer;
|
||||
while ((peer = (WeakRefPeer<?>) queue.poll()) != null) {
|
||||
map.remove(peer.weakPair());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Common interface of both {@link Weak} and {@link Lookup} key pairs.
|
||||
*/
|
||||
private interface Pair<K1, K2> {
|
||||
|
||||
static <K1, K2> Pair<K1, K2> weak(K1 k1, K2 k2,
|
||||
ReferenceQueue<Object> queue) {
|
||||
return new Weak<>(k1, k2, queue);
|
||||
}
|
||||
|
||||
static <K1, K2> Pair<K1, K2> lookup(K1 k1, K2 k2) {
|
||||
return new Lookup<>(k1, k2);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The 1st of the pair of keys (may be null for {@link Weak}
|
||||
* when it gets cleared)
|
||||
*/
|
||||
K1 first();
|
||||
|
||||
/**
|
||||
* @return The 2nd of the pair of keys (may be null for {@link Weak}
|
||||
* when it gets cleared)
|
||||
*/
|
||||
K2 second();
|
||||
|
||||
static int hashCode(Object first, Object second) {
|
||||
// assert first != null && second != null;
|
||||
return System.identityHashCode(first) ^
|
||||
System.identityHashCode(second);
|
||||
}
|
||||
|
||||
static boolean equals(Object first, Object second, Pair<?, ?> p) {
|
||||
return first != null && second != null &&
|
||||
first == p.first() && second == p.second();
|
||||
}
|
||||
|
||||
/**
|
||||
* A Pair where both keys are weakly-referenced.
|
||||
* It is composed of two instances of {@link WeakRefPeer}s:
|
||||
* <pre>{@code
|
||||
*
|
||||
* +-referent-> [K1] +-referent-> [K2]
|
||||
* | |
|
||||
* +----------------+ +----------------+
|
||||
* | Pair.Weak <: |-----peer----->| (anonymous) <: |
|
||||
* | WeakRefPeer, | | WeakRefPeer |
|
||||
* | Pair |<--weakPair()--| |
|
||||
* +----------------+ +----------------+
|
||||
* | ^
|
||||
* | |
|
||||
* +-weakPair()-+
|
||||
*
|
||||
* }</pre>
|
||||
* <p>
|
||||
* Pair.Weak is used for CHM keys. Both peers are associated with the
|
||||
* same {@link ReferenceQueue} so when either of their referents
|
||||
* becomes weakly-reachable, the corresponding entries can be
|
||||
* {@link #expungeStaleAssociations() expunged} from the map.
|
||||
*/
|
||||
final class Weak<K1, K2> extends WeakRefPeer<K1> implements Pair<K1, K2> {
|
||||
|
||||
// saved hash so it can be retrieved after the reference is cleared
|
||||
private final int hash;
|
||||
// link to <K2> peer
|
||||
private final WeakRefPeer<K2> peer;
|
||||
|
||||
Weak(K1 k1, K2 k2, ReferenceQueue<Object> queue) {
|
||||
super(k1, queue);
|
||||
hash = Pair.hashCode(k1, k2);
|
||||
peer = new WeakRefPeer<>(k2, queue) {
|
||||
// link back to <K1> peer
|
||||
@Override
|
||||
Weak<?, ?> weakPair() { return Weak.this; }
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
Weak<?, ?> weakPair() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public K1 first() {
|
||||
return get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public K2 second() {
|
||||
return peer.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return hash;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return this == obj ||
|
||||
(obj instanceof Pair &&
|
||||
Pair.equals(first(), second(), (Pair<?, ?>) obj));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Optimized lookup Pair, used as lookup key in methods like
|
||||
* {@link java.util.Map#get(Object)} or
|
||||
* {@link java.util.Map#containsKey(Object)}) where
|
||||
* there is a great chance its allocation is eliminated
|
||||
* by escape analysis when such lookups are inlined by JIT.
|
||||
* All its methods are purposely designed so that 'this' is never
|
||||
* passed to any other method or used as identity.
|
||||
*/
|
||||
final class Lookup<K1, K2> implements Pair<K1, K2> {
|
||||
private final K1 k1;
|
||||
private final K2 k2;
|
||||
|
||||
Lookup(K1 k1, K2 k2) {
|
||||
this.k1 = Objects.requireNonNull(k1);
|
||||
this.k2 = Objects.requireNonNull(k2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public K1 first() {
|
||||
return k1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public K2 second() {
|
||||
return k2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Pair.hashCode(k1, k2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof Pair &&
|
||||
Pair.equals(k1, k2, (Pair<?, ?>) obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Common abstract supertype of a pair of WeakReference peers.
|
||||
*/
|
||||
private static abstract class WeakRefPeer<K> extends WeakReference<K> {
|
||||
|
||||
WeakRefPeer(K k, ReferenceQueue<Object> queue) {
|
||||
super(Objects.requireNonNull(k), queue);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the {@link Pair.Weak} side of the pair of peers.
|
||||
*/
|
||||
abstract Pair.Weak<?, ?> weakPair();
|
||||
}
|
||||
}
|
@ -29,8 +29,6 @@ import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.lang.module.ModuleDescriptor;
|
||||
import java.lang.module.ModuleDescriptor.Provides;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Layer;
|
||||
@ -85,7 +83,7 @@ import jdk.internal.reflect.Reflection;
|
||||
* and deployed as a named module must have an appropriate <i>uses</i> clause
|
||||
* in its <i>module descriptor</i> to declare that the module uses
|
||||
* implementations of the service. A corresponding requirement is that a
|
||||
* provider deployed as a named modules must have an appropriate
|
||||
* provider deployed as a named module must have an appropriate
|
||||
* <i>provides</i> clause in its module descriptor to declare that the module
|
||||
* provides an implementation of the service. The <i>uses</i> and
|
||||
* <i>provides</i> allow consumers of a service to be <i>linked</i> to
|
||||
@ -550,35 +548,29 @@ public final class ServiceLoader<S>
|
||||
/**
|
||||
* Implements lazy service provider lookup of service providers that
|
||||
* are provided by modules in a module Layer.
|
||||
*
|
||||
* For now, this iterator examines all modules in each Layer. This will
|
||||
* be replaced once we decide on how the service-use graph is exposed
|
||||
* in the module API.
|
||||
*/
|
||||
private class LayerLookupIterator
|
||||
extends RestrictedIterator<S>
|
||||
{
|
||||
final String serviceName;
|
||||
Layer currentLayer;
|
||||
Iterator<ModuleDescriptor> descriptorIterator;
|
||||
Iterator<String> providersIterator;
|
||||
|
||||
Module nextModule;
|
||||
String nextProvider;
|
||||
Iterator<ServiceProvider> iterator;
|
||||
ServiceProvider nextProvider;
|
||||
|
||||
LayerLookupIterator() {
|
||||
serviceName = service.getName();
|
||||
currentLayer = layer;
|
||||
|
||||
// need to get us started
|
||||
descriptorIterator = descriptors(layer, serviceName);
|
||||
iterator = providers(currentLayer, serviceName);
|
||||
}
|
||||
|
||||
Iterator<ModuleDescriptor> descriptors(Layer layer, String service) {
|
||||
return layer.modules().stream()
|
||||
.map(Module::getDescriptor)
|
||||
.filter(d -> d.provides().get(service) != null)
|
||||
.iterator();
|
||||
Iterator<ServiceProvider> providers(Layer layer, String service) {
|
||||
ServicesCatalog catalog = SharedSecrets
|
||||
.getJavaLangReflectModuleAccess()
|
||||
.getServicesCatalog(layer);
|
||||
|
||||
return catalog.findServices(serviceName).iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -591,30 +583,18 @@ public final class ServiceLoader<S>
|
||||
while (true) {
|
||||
|
||||
// next provider
|
||||
if (providersIterator != null && providersIterator.hasNext()) {
|
||||
nextProvider = providersIterator.next();
|
||||
if (iterator != null && iterator.hasNext()) {
|
||||
nextProvider = iterator.next();
|
||||
return true;
|
||||
}
|
||||
|
||||
// next descriptor
|
||||
if (descriptorIterator.hasNext()) {
|
||||
ModuleDescriptor descriptor = descriptorIterator.next();
|
||||
|
||||
nextModule = currentLayer.findModule(descriptor.name()).get();
|
||||
|
||||
Provides provides = descriptor.provides().get(serviceName);
|
||||
providersIterator = provides.providers().iterator();
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// next layer
|
||||
Layer parent = currentLayer.parent().orElse(null);
|
||||
if (parent == null)
|
||||
return false;
|
||||
|
||||
currentLayer = parent;
|
||||
descriptorIterator = descriptors(currentLayer, serviceName);
|
||||
iterator = providers(currentLayer, serviceName);
|
||||
}
|
||||
}
|
||||
|
||||
@ -623,13 +603,14 @@ public final class ServiceLoader<S>
|
||||
if (!hasNextService())
|
||||
throw new NoSuchElementException();
|
||||
|
||||
assert nextModule != null && nextProvider != null;
|
||||
|
||||
String cn = nextProvider;
|
||||
ServiceProvider provider = nextProvider;
|
||||
nextProvider = null;
|
||||
|
||||
Module module = provider.module();
|
||||
String cn = provider.providerName();
|
||||
|
||||
// attempt to load the provider
|
||||
Class<?> c = loadClassInModule(nextModule, cn);
|
||||
Class<?> c = loadClassInModule(module, cn);
|
||||
if (c == null)
|
||||
fail(service, "Provider " + cn + " not found");
|
||||
if (!service.isAssignableFrom(c))
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2016, 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
|
||||
@ -68,7 +68,7 @@ public class BootLoader {
|
||||
}
|
||||
|
||||
// ServiceCatalog for the boot class loader
|
||||
private static final ServicesCatalog SERVICES_CATALOG = new ServicesCatalog();
|
||||
private static final ServicesCatalog SERVICES_CATALOG = ServicesCatalog.create();
|
||||
|
||||
// ClassLoaderValue map for boot class loader
|
||||
private static final ConcurrentHashMap<?, ?> CLASS_LOADER_VALUE_MAP =
|
||||
|
@ -104,7 +104,7 @@ public class BuiltinClassLoader
|
||||
* A module defined/loaded by a built-in class loader.
|
||||
*
|
||||
* A LoadedModule encapsulates a ModuleReference along with its CodeSource
|
||||
* URL to avoid needing to create this URL when define classes.
|
||||
* URL to avoid needing to create this URL when defining classes.
|
||||
*/
|
||||
private static class LoadedModule {
|
||||
private final BuiltinClassLoader loader;
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2016, 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
|
||||
@ -25,13 +25,24 @@
|
||||
|
||||
package jdk.internal.misc;
|
||||
|
||||
import java.io.PrintStream;
|
||||
import java.lang.module.Configuration;
|
||||
import jdk.internal.module.ModuleHashes;
|
||||
|
||||
import java.lang.module.ModuleDescriptor;
|
||||
import java.lang.module.ModuleDescriptor.Exports;
|
||||
import java.lang.module.ModuleDescriptor.Requires;
|
||||
import java.lang.module.ModuleDescriptor.Provides;
|
||||
import java.lang.module.ModuleDescriptor.Version;
|
||||
import java.lang.module.ModuleFinder;
|
||||
import java.util.Collection;
|
||||
import java.lang.module.ModuleReader;
|
||||
import java.lang.module.ModuleReference;
|
||||
import java.net.URI;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* Provides access to non-public methods in java.lang.module.
|
||||
@ -89,5 +100,29 @@ public interface JavaLangModuleAccess {
|
||||
String osArch,
|
||||
String osVersion,
|
||||
Set<String> conceals,
|
||||
Set<String> packages);
|
||||
Set<String> packages,
|
||||
ModuleHashes hashes);
|
||||
|
||||
/**
|
||||
* Resolves a collection of root modules, with service binding
|
||||
* and the empty configuration as the parent. The post resolution
|
||||
* checks are optionally run.
|
||||
*/
|
||||
Configuration resolveRequiresAndUses(ModuleFinder finder,
|
||||
Collection<String> roots,
|
||||
boolean check,
|
||||
PrintStream traceOutput);
|
||||
|
||||
/**
|
||||
* Creates a ModuleReference to a "patched" module.
|
||||
*/
|
||||
ModuleReference newPatchedModule(ModuleDescriptor descriptor,
|
||||
URI location,
|
||||
Supplier<ModuleReader> readerSupplier);
|
||||
|
||||
/**
|
||||
* Returns the object with the hashes of other modules
|
||||
*/
|
||||
Optional<ModuleHashes> hashes(ModuleDescriptor descriptor);
|
||||
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2014, 2016, 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
|
||||
@ -26,9 +26,12 @@
|
||||
package jdk.internal.misc;
|
||||
|
||||
import java.lang.module.ModuleDescriptor;
|
||||
import java.lang.reflect.Layer;
|
||||
import java.lang.reflect.Module;
|
||||
import java.net.URI;
|
||||
|
||||
import jdk.internal.module.ServicesCatalog;
|
||||
|
||||
/**
|
||||
* Provides access to non-public methods in java.lang.reflect.Module
|
||||
*/
|
||||
@ -56,6 +59,11 @@ public interface JavaLangReflectModuleAccess {
|
||||
*/
|
||||
void addReads(Module m1, Module m2);
|
||||
|
||||
/**
|
||||
* Updates module m to read all unnamed modules.
|
||||
*/
|
||||
void addReadsAllUnnamed(Module m);
|
||||
|
||||
/**
|
||||
* Updates module m1 to export a package to module m2. The export does
|
||||
* not result in a strong reference to m2 (m2 can be GC'ed).
|
||||
@ -76,4 +84,10 @@ public interface JavaLangReflectModuleAccess {
|
||||
* Add a package to the given module.
|
||||
*/
|
||||
void addPackage(Module m, String pkg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ServicesCatalog for the given Layer.
|
||||
*/
|
||||
ServicesCatalog getServicesCatalog(Layer layer);
|
||||
|
||||
}
|
@ -74,6 +74,8 @@ final class Builder {
|
||||
String osName;
|
||||
String osArch;
|
||||
String osVersion;
|
||||
String algorithm;
|
||||
Map<String, String> hashes;
|
||||
|
||||
Builder(String name, int reqs, int exports,
|
||||
int provides, int conceals, int packages) {
|
||||
@ -251,6 +253,25 @@ final class Builder {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the algorithm of the module hashes
|
||||
*/
|
||||
public Builder algorithm(String algorithm) {
|
||||
this.algorithm = algorithm;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the module hash for the given module name
|
||||
*/
|
||||
public Builder moduleHash(String mn, String hash) {
|
||||
if (hashes == null)
|
||||
hashes = new HashMap<>();
|
||||
|
||||
hashes.put(mn, hash);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the set of packages that is the union of the exported and
|
||||
* concealed packages.
|
||||
@ -273,6 +294,9 @@ final class Builder {
|
||||
public ModuleDescriptor build() {
|
||||
assert name != null;
|
||||
|
||||
ModuleHashes moduleHashes =
|
||||
hashes != null ? new ModuleHashes(algorithm, hashes) : null;
|
||||
|
||||
return jlma.newModuleDescriptor(name,
|
||||
false, // automatic
|
||||
false, // assume not synthetic for now
|
||||
@ -286,6 +310,7 @@ final class Builder {
|
||||
osArch,
|
||||
osVersion,
|
||||
conceals,
|
||||
computePackages(exports, conceals));
|
||||
computePackages(exports, conceals),
|
||||
moduleHashes);
|
||||
}
|
||||
}
|
||||
|
@ -34,6 +34,7 @@ import java.lang.module.ModuleDescriptor.Version;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@ -42,7 +43,6 @@ import jdk.internal.org.objectweb.asm.ByteVector;
|
||||
import jdk.internal.org.objectweb.asm.ClassReader;
|
||||
import jdk.internal.org.objectweb.asm.ClassWriter;
|
||||
import jdk.internal.org.objectweb.asm.Label;
|
||||
import jdk.internal.module.Hasher.DependencyHashes;
|
||||
import static jdk.internal.module.ClassFileConstants.*;
|
||||
|
||||
|
||||
@ -148,7 +148,7 @@ class ClassFileAttributes {
|
||||
for (int i=0; i<provides_count; i++) {
|
||||
String sn = cr.readClass(off, buf).replace('/', '.');
|
||||
String cn = cr.readClass(off + 2, buf).replace('/', '.');
|
||||
provides.computeIfAbsent(sn, k -> new HashSet<>()).add(cn);
|
||||
provides.computeIfAbsent(sn, k -> new LinkedHashSet<>()).add(cn);
|
||||
off += 4;
|
||||
}
|
||||
provides.entrySet().forEach(e -> builder.provides(e.getKey(),
|
||||
@ -281,10 +281,10 @@ class ClassFileAttributes {
|
||||
* u4 attribute_length;
|
||||
*
|
||||
* // the number of entries in the packages table
|
||||
* u2 package_count;
|
||||
* u2 packages_count;
|
||||
* { // index to CONSTANT_CONSTANT_utf8_info structure with the package name
|
||||
* u2 package_index
|
||||
* } package[package_count];
|
||||
* } packages[package_count];
|
||||
*
|
||||
* }</pre>
|
||||
*/
|
||||
@ -579,9 +579,9 @@ class ClassFileAttributes {
|
||||
* alternative is to store it as an array of u1.
|
||||
*/
|
||||
static class HashesAttribute extends Attribute {
|
||||
private final DependencyHashes hashes;
|
||||
private final ModuleHashes hashes;
|
||||
|
||||
HashesAttribute(DependencyHashes hashes) {
|
||||
HashesAttribute(ModuleHashes hashes) {
|
||||
super(HASHES);
|
||||
this.hashes = hashes;
|
||||
}
|
||||
@ -613,7 +613,7 @@ class ClassFileAttributes {
|
||||
map.put(dn, hash);
|
||||
}
|
||||
|
||||
DependencyHashes hashes = new DependencyHashes(algorithm, map);
|
||||
ModuleHashes hashes = new ModuleHashes(algorithm, map);
|
||||
|
||||
return new HashesAttribute(hashes);
|
||||
}
|
||||
|
@ -26,12 +26,15 @@
|
||||
package jdk.internal.module;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.PrintStream;
|
||||
import java.lang.module.Configuration;
|
||||
import java.lang.module.ModuleReference;
|
||||
import java.lang.module.ModuleDescriptor;
|
||||
import java.lang.module.ModuleFinder;
|
||||
import java.lang.module.ModuleReference;
|
||||
import java.lang.module.ResolvedModule;
|
||||
import java.lang.reflect.Layer;
|
||||
import java.lang.reflect.Module;
|
||||
import java.net.URI;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Collections;
|
||||
@ -41,10 +44,10 @@ import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import jdk.internal.loader.BootLoader;
|
||||
import jdk.internal.loader.BuiltinClassLoader;
|
||||
import jdk.internal.misc.SharedSecrets;
|
||||
import jdk.internal.perf.PerfCounter;
|
||||
|
||||
/**
|
||||
@ -54,10 +57,9 @@ import jdk.internal.perf.PerfCounter;
|
||||
* the module system. In summary, the boot method creates a Configuration by
|
||||
* resolving a set of module names specified via the launcher (or equivalent)
|
||||
* -m and -addmods options. The modules are located on a module path that is
|
||||
* constructed from the upgrade, system and application module paths. The
|
||||
* Configuration is reified by creating the boot Layer with each module in the
|
||||
* the configuration defined to one of the built-in class loaders. The mapping
|
||||
* of modules to class loaders is statically mapped in a helper class.
|
||||
* constructed from the upgrade module path, system modules, and application
|
||||
* module path. The Configuration is instantiated as the boot Layer with each
|
||||
* module in the the configuration defined to one of the built-in class loaders.
|
||||
*/
|
||||
|
||||
public final class ModuleBootstrap {
|
||||
@ -65,6 +67,11 @@ public final class ModuleBootstrap {
|
||||
|
||||
private static final String JAVA_BASE = "java.base";
|
||||
|
||||
private static final String JAVA_SE = "java.se";
|
||||
|
||||
// the token for "all default modules"
|
||||
private static final String ALL_DEFAULT = "ALL-DEFAULT";
|
||||
|
||||
// the token for "all unnamed modules"
|
||||
private static final String ALL_UNNAMED = "ALL-UNNAMED";
|
||||
|
||||
@ -94,47 +101,65 @@ public final class ModuleBootstrap {
|
||||
|
||||
long t0 = System.nanoTime();
|
||||
|
||||
// system module path
|
||||
ModuleFinder systemModulePath = ModuleFinder.ofSystem();
|
||||
// system modules
|
||||
ModuleFinder systemModules = ModuleFinder.ofSystem();
|
||||
|
||||
// Once we have the system module path then we define the base module.
|
||||
// We do this here so that java.base is defined to the VM as early as
|
||||
PerfCounters.systemModulesTime.addElapsedTimeFrom(t0);
|
||||
|
||||
|
||||
long t1 = System.nanoTime();
|
||||
|
||||
// Once we have the system modules then we define the base module to
|
||||
// the VM. We do this here so that java.base is defined as early as
|
||||
// possible and also that resources in the base module can be located
|
||||
// for error messages that may happen from here on.
|
||||
Optional<ModuleReference> obase = systemModulePath.find(JAVA_BASE);
|
||||
if (!obase.isPresent())
|
||||
ModuleReference base = systemModules.find(JAVA_BASE).orElse(null);
|
||||
if (base == null)
|
||||
throw new InternalError(JAVA_BASE + " not found");
|
||||
ModuleReference base = obase.get();
|
||||
URI baseUri = base.location().orElse(null);
|
||||
if (baseUri == null)
|
||||
throw new InternalError(JAVA_BASE + " does not have a location");
|
||||
BootLoader.loadModule(base);
|
||||
Modules.defineModule(null, base.descriptor(), base.location().orElse(null));
|
||||
Modules.defineModule(null, base.descriptor(), baseUri);
|
||||
|
||||
PerfCounters.defineBaseTime.addElapsedTimeFrom(t1);
|
||||
|
||||
|
||||
long t2 = System.nanoTime();
|
||||
|
||||
// -upgrademodulepath option specified to launcher
|
||||
ModuleFinder upgradeModulePath
|
||||
= createModulePathFinder("jdk.upgrade.module.path");
|
||||
if (upgradeModulePath != null)
|
||||
systemModules = ModuleFinder.compose(upgradeModulePath, systemModules);
|
||||
|
||||
// -modulepath option specified to the launcher
|
||||
ModuleFinder appModulePath = createModulePathFinder("jdk.module.path");
|
||||
|
||||
// The module finder: [-upgrademodulepath] system-module-path [-modulepath]
|
||||
ModuleFinder finder = systemModulePath;
|
||||
if (upgradeModulePath != null)
|
||||
finder = ModuleFinder.compose(upgradeModulePath, finder);
|
||||
// The module finder: [-upgrademodulepath] system [-modulepath]
|
||||
ModuleFinder finder = systemModules;
|
||||
if (appModulePath != null)
|
||||
finder = ModuleFinder.compose(finder, appModulePath);
|
||||
|
||||
// launcher -m option to specify the initial module
|
||||
// The root modules to resolve
|
||||
Set<String> roots = new HashSet<>();
|
||||
|
||||
// launcher -m option to specify the main/initial module
|
||||
String mainModule = System.getProperty("jdk.module.main");
|
||||
if (mainModule != null)
|
||||
roots.add(mainModule);
|
||||
|
||||
// additional module(s) specified by -addmods
|
||||
boolean addAllDefaultModules = false;
|
||||
boolean addAllSystemModules = false;
|
||||
boolean addAllApplicationModules = false;
|
||||
Set<String> addModules = null;
|
||||
String propValue = System.getProperty("jdk.launcher.addmods");
|
||||
if (propValue != null) {
|
||||
addModules = new HashSet<>();
|
||||
for (String mod: propValue.split(",")) {
|
||||
switch (mod) {
|
||||
case ALL_DEFAULT:
|
||||
addAllDefaultModules = true;
|
||||
break;
|
||||
case ALL_SYSTEM:
|
||||
addAllSystemModules = true;
|
||||
break;
|
||||
@ -142,28 +167,12 @@ public final class ModuleBootstrap {
|
||||
addAllApplicationModules = true;
|
||||
break;
|
||||
default :
|
||||
addModules.add(mod);
|
||||
roots.add(mod);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The root modules to resolve
|
||||
Set<String> roots = new HashSet<>();
|
||||
|
||||
// main/initial module
|
||||
if (mainModule != null) {
|
||||
roots.add(mainModule);
|
||||
if (addAllApplicationModules)
|
||||
fail(ALL_MODULE_PATH + " not allowed with initial module");
|
||||
}
|
||||
|
||||
// If -addmods is specified then those modules need to be resolved
|
||||
if (addModules != null)
|
||||
roots.addAll(addModules);
|
||||
|
||||
|
||||
// -limitmods
|
||||
boolean limitmods = false;
|
||||
propValue = System.getProperty("jdk.launcher.limitmods");
|
||||
if (propValue != null) {
|
||||
Set<String> mods = new HashSet<>();
|
||||
@ -171,62 +180,101 @@ public final class ModuleBootstrap {
|
||||
mods.add(mod);
|
||||
}
|
||||
finder = limitFinder(finder, mods, roots);
|
||||
limitmods = true;
|
||||
}
|
||||
|
||||
|
||||
// If there is no initial module specified then assume that the
|
||||
// initial module is the unnamed module of the application class
|
||||
// loader. By convention, and for compatibility, this is
|
||||
// implemented by putting the names of all modules on the system
|
||||
// module path into the set of modules to resolve.
|
||||
//
|
||||
// If `-addmods ALL-SYSTEM` is used then all modules on the system
|
||||
// module path will be resolved, irrespective of whether an initial
|
||||
// module is specified.
|
||||
//
|
||||
// If `-addmods ALL-MODULE-PATH` is used, and no initial module is
|
||||
// specified, then all modules on the application module path will
|
||||
// be resolved.
|
||||
//
|
||||
if (mainModule == null || addAllSystemModules) {
|
||||
Set<ModuleReference> mrefs;
|
||||
if (addAllApplicationModules) {
|
||||
assert mainModule == null;
|
||||
mrefs = finder.findAll();
|
||||
} else {
|
||||
mrefs = systemModulePath.findAll();
|
||||
if (limitmods) {
|
||||
ModuleFinder f = finder;
|
||||
mrefs = mrefs.stream()
|
||||
.filter(m -> f.find(m.descriptor().name()).isPresent())
|
||||
.collect(Collectors.toSet());
|
||||
// If there is no initial module specified then assume that the initial
|
||||
// module is the unnamed module of the application class loader. This
|
||||
// is implemented by resolving "java.se" and all (non-java.*) modules
|
||||
// that export an API. If "java.se" is not observable then all java.*
|
||||
// modules are resolved.
|
||||
if (mainModule == null || addAllDefaultModules) {
|
||||
boolean hasJava = false;
|
||||
if (systemModules.find(JAVA_SE).isPresent()) {
|
||||
// java.se is a system module
|
||||
if (finder == systemModules || finder.find(JAVA_SE).isPresent()) {
|
||||
// java.se is observable
|
||||
hasJava = true;
|
||||
roots.add(JAVA_SE);
|
||||
}
|
||||
}
|
||||
// map to module names
|
||||
for (ModuleReference mref : mrefs) {
|
||||
roots.add(mref.descriptor().name());
|
||||
|
||||
for (ModuleReference mref : systemModules.findAll()) {
|
||||
String mn = mref.descriptor().name();
|
||||
if (hasJava && mn.startsWith("java."))
|
||||
continue;
|
||||
|
||||
// add as root if observable and exports at least one package
|
||||
if ((finder == systemModules || finder.find(mn).isPresent())) {
|
||||
ModuleDescriptor descriptor = mref.descriptor();
|
||||
for (ModuleDescriptor.Exports e : descriptor.exports()) {
|
||||
if (!e.isQualified()) {
|
||||
roots.add(mn);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
long t1 = System.nanoTime();
|
||||
// If `-addmods ALL-SYSTEM` is specified then all observable system
|
||||
// modules will be resolved.
|
||||
if (addAllSystemModules) {
|
||||
ModuleFinder f = finder; // observable modules
|
||||
systemModules.findAll()
|
||||
.stream()
|
||||
.map(ModuleReference::descriptor)
|
||||
.map(ModuleDescriptor::name)
|
||||
.filter(mn -> f.find(mn).isPresent()) // observable
|
||||
.forEach(mn -> roots.add(mn));
|
||||
}
|
||||
|
||||
// If `-addmods ALL-MODULE-PATH` is specified then all observable
|
||||
// modules on the application module path will be resolved.
|
||||
if (appModulePath != null && addAllApplicationModules) {
|
||||
ModuleFinder f = finder; // observable modules
|
||||
appModulePath.findAll()
|
||||
.stream()
|
||||
.map(ModuleReference::descriptor)
|
||||
.map(ModuleDescriptor::name)
|
||||
.filter(mn -> f.find(mn).isPresent()) // observable
|
||||
.forEach(mn -> roots.add(mn));
|
||||
}
|
||||
|
||||
PerfCounters.optionsAndRootsTime.addElapsedTimeFrom(t2);
|
||||
|
||||
|
||||
long t3 = System.nanoTime();
|
||||
|
||||
// determine if post resolution checks are needed
|
||||
boolean needPostResolutionChecks = true;
|
||||
if (baseUri.getScheme().equals("jrt") // toLowerCase not needed here
|
||||
&& (upgradeModulePath == null)
|
||||
&& (appModulePath == null)
|
||||
&& (System.getProperty("jdk.launcher.patch.0") == null)) {
|
||||
needPostResolutionChecks = false;
|
||||
}
|
||||
|
||||
PrintStream traceOutput = null;
|
||||
if (Boolean.getBoolean("jdk.launcher.traceResolver"))
|
||||
traceOutput = System.out;
|
||||
|
||||
// run the resolver to create the configuration
|
||||
|
||||
Configuration cf = Configuration.empty()
|
||||
Configuration cf = SharedSecrets.getJavaLangModuleAccess()
|
||||
.resolveRequiresAndUses(finder,
|
||||
ModuleFinder.empty(),
|
||||
roots);
|
||||
roots,
|
||||
needPostResolutionChecks,
|
||||
traceOutput);
|
||||
|
||||
// time to create configuration
|
||||
PerfCounters.resolveTime.addElapsedTimeFrom(t1);
|
||||
PerfCounters.resolveTime.addElapsedTimeFrom(t3);
|
||||
|
||||
|
||||
// mapping of modules to class loaders
|
||||
Function<String, ClassLoader> clf = ModuleLoaderMap.mappingFunction(cf);
|
||||
|
||||
// check that all modules to be mapped to the boot loader will be
|
||||
// loaded from the system module path
|
||||
if (finder != systemModulePath) {
|
||||
// loaded from the runtime image
|
||||
if (needPostResolutionChecks) {
|
||||
for (ResolvedModule resolvedModule : cf.modules()) {
|
||||
ModuleReference mref = resolvedModule.reference();
|
||||
String name = mref.descriptor().name();
|
||||
@ -237,20 +285,22 @@ public final class ModuleBootstrap {
|
||||
&& upgradeModulePath.find(name).isPresent())
|
||||
fail(name + ": cannot be loaded from upgrade module path");
|
||||
|
||||
if (!systemModulePath.find(name).isPresent())
|
||||
if (!systemModules.find(name).isPresent())
|
||||
fail(name + ": cannot be loaded from application module path");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
long t2 = System.nanoTime();
|
||||
|
||||
long t4 = System.nanoTime();
|
||||
|
||||
// define modules to VM/runtime
|
||||
Layer bootLayer = Layer.empty().defineModules(cf, clf);
|
||||
|
||||
PerfCounters.layerCreateTime.addElapsedTimeFrom(t2);
|
||||
PerfCounters.layerCreateTime.addElapsedTimeFrom(t4);
|
||||
|
||||
long t3 = System.nanoTime();
|
||||
|
||||
long t5 = System.nanoTime();
|
||||
|
||||
// define the module to its class loader, except java.base
|
||||
for (ResolvedModule resolvedModule : cf.modules()) {
|
||||
@ -264,7 +314,8 @@ public final class ModuleBootstrap {
|
||||
}
|
||||
}
|
||||
|
||||
PerfCounters.loadModulesTime.addElapsedTimeFrom(t3);
|
||||
PerfCounters.loadModulesTime.addElapsedTimeFrom(t5);
|
||||
|
||||
|
||||
// -XaddReads and -XaddExports
|
||||
addExtraReads(bootLayer);
|
||||
@ -295,25 +346,21 @@ public final class ModuleBootstrap {
|
||||
|
||||
// module name -> reference
|
||||
Map<String, ModuleReference> map = new HashMap<>();
|
||||
|
||||
// root modules and their transitive dependences
|
||||
cf.modules().stream()
|
||||
.map(ResolvedModule::reference)
|
||||
.forEach(mref -> map.put(mref.descriptor().name(), mref));
|
||||
|
||||
// additional modules
|
||||
otherMods.stream()
|
||||
.map(finder::find)
|
||||
.flatMap(Optional::stream)
|
||||
.forEach(mref -> map.putIfAbsent(mref.descriptor().name(), mref));
|
||||
|
||||
// set of modules that are observable
|
||||
Set<ModuleReference> mrefs = new HashSet<>(map.values());
|
||||
|
||||
// add the other modules
|
||||
for (String mod : otherMods) {
|
||||
Optional<ModuleReference> omref = finder.find(mod);
|
||||
if (omref.isPresent()) {
|
||||
ModuleReference mref = omref.get();
|
||||
map.putIfAbsent(mod, mref);
|
||||
mrefs.add(mref);
|
||||
} else {
|
||||
// no need to fail
|
||||
}
|
||||
}
|
||||
|
||||
return new ModuleFinder() {
|
||||
@Override
|
||||
public Optional<ModuleReference> find(String name) {
|
||||
@ -369,15 +416,15 @@ public final class ModuleBootstrap {
|
||||
|
||||
Module other;
|
||||
if (ALL_UNNAMED.equals(name)) {
|
||||
other = null; // loose
|
||||
Modules.addReadsAllUnnamed(m);
|
||||
} else {
|
||||
om = bootLayer.findModule(name);
|
||||
if (!om.isPresent())
|
||||
fail("Unknown module: " + name);
|
||||
other = om.get();
|
||||
Modules.addReads(m, other);
|
||||
}
|
||||
|
||||
Modules.addReads(m, other);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -439,10 +486,6 @@ public final class ModuleBootstrap {
|
||||
* Decodes the values of -XaddReads or -XaddExports options
|
||||
*
|
||||
* The format of the options is: $KEY=$MODULE(,$MODULE)*
|
||||
*
|
||||
* For transition purposes, this method allows the first usage to be
|
||||
* $KEY=$MODULE(,$KEY=$MODULE)
|
||||
* This format will eventually be removed.
|
||||
*/
|
||||
private static Map<String, Set<String>> decode(String prefix) {
|
||||
int index = 0;
|
||||
@ -467,42 +510,15 @@ public final class ModuleBootstrap {
|
||||
if (rhs.isEmpty())
|
||||
fail("Unable to parse: " + value);
|
||||
|
||||
// new format $MODULE(,$MODULE)* or old format $(MODULE)=...
|
||||
pos = rhs.indexOf('=');
|
||||
|
||||
// old format only allowed in first -X option
|
||||
if (pos >= 0 && index > 0)
|
||||
fail("Unable to parse: " + value);
|
||||
// value is <module>(,<module>)*
|
||||
if (map.containsKey(key))
|
||||
fail(key + " specified more than once");
|
||||
|
||||
if (pos == -1) {
|
||||
|
||||
// new format: $KEY=$MODULE(,$MODULE)*
|
||||
|
||||
Set<String> values = map.get(key);
|
||||
if (values != null)
|
||||
fail(key + " specified more than once");
|
||||
|
||||
values = new HashSet<>();
|
||||
map.put(key, values);
|
||||
for (String s : rhs.split(",")) {
|
||||
if (s.length() > 0) values.add(s);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// old format: $KEY=$MODULE(,$KEY=$MODULE)*
|
||||
|
||||
assert index == 0; // old format only allowed in first usage
|
||||
|
||||
for (String expr : value.split(",")) {
|
||||
if (expr.length() > 0) {
|
||||
String[] s = expr.split("=");
|
||||
if (s.length != 2)
|
||||
fail("Unable to parse: " + expr);
|
||||
|
||||
map.computeIfAbsent(s[0], k -> new HashSet<>()).add(s[1]);
|
||||
}
|
||||
}
|
||||
Set<String> values = new HashSet<>();
|
||||
map.put(key, values);
|
||||
for (String s : rhs.split(",")) {
|
||||
if (s.length() > 0) values.add(s);
|
||||
}
|
||||
|
||||
index++;
|
||||
@ -521,6 +537,13 @@ public final class ModuleBootstrap {
|
||||
}
|
||||
|
||||
static class PerfCounters {
|
||||
|
||||
static PerfCounter systemModulesTime
|
||||
= PerfCounter.newPerfCounter("jdk.module.bootstrap.systemModulesTime");
|
||||
static PerfCounter defineBaseTime
|
||||
= PerfCounter.newPerfCounter("jdk.module.bootstrap.defineBaseTime");
|
||||
static PerfCounter optionsAndRootsTime
|
||||
= PerfCounter.newPerfCounter("jdk.module.bootstrap.optionsAndRootsTime");
|
||||
static PerfCounter resolveTime
|
||||
= PerfCounter.newPerfCounter("jdk.module.bootstrap.resolveTime");
|
||||
static PerfCounter layerCreateTime
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2016, 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
|
||||
@ -22,6 +22,7 @@
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.internal.module;
|
||||
|
||||
import java.io.IOException;
|
||||
@ -32,17 +33,16 @@ import java.nio.file.Path;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Base64;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Supporting class for computing, encoding and decoding hashes (message
|
||||
* digests).
|
||||
* The result of hashing the contents of a number of module artifacts.
|
||||
*/
|
||||
|
||||
public class Hasher {
|
||||
private Hasher() { }
|
||||
public final class ModuleHashes {
|
||||
|
||||
/**
|
||||
* A supplier of an encoded message digest.
|
||||
@ -51,43 +51,49 @@ public class Hasher {
|
||||
String generate(String algorithm);
|
||||
}
|
||||
|
||||
|
||||
private final String algorithm;
|
||||
private final Map<String, String> nameToHash;
|
||||
|
||||
/**
|
||||
* Encapsulates the result of hashing the contents of a number of module
|
||||
* artifacts.
|
||||
* Creates a {@code ModuleHashes}.
|
||||
*
|
||||
* @param algorithm the algorithm used to create the hashes
|
||||
* @param nameToHash the map of module name to hash value (in string form)
|
||||
*/
|
||||
public static class DependencyHashes {
|
||||
private final String algorithm;
|
||||
private final Map<String, String> nameToHash;
|
||||
|
||||
public DependencyHashes(String algorithm, Map<String, String> nameToHash) {
|
||||
this.algorithm = algorithm;
|
||||
this.nameToHash = nameToHash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the algorithm used to hash the dependences ("SHA-256" or
|
||||
* "MD5" for example).
|
||||
*/
|
||||
public String algorithm() {
|
||||
return algorithm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the set of module names for which hashes are recorded.
|
||||
*/
|
||||
public Set<String> names() {
|
||||
return nameToHash.keySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retruns the hash string for the given module name, {@code null}
|
||||
* if there is no hash recorded for the module.
|
||||
*/
|
||||
public String hashFor(String dn) {
|
||||
return nameToHash.get(dn);
|
||||
}
|
||||
public ModuleHashes(String algorithm, Map<String, String> nameToHash) {
|
||||
this.algorithm = algorithm;
|
||||
this.nameToHash = Collections.unmodifiableMap(nameToHash);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the algorithm used to hash the modules ("SHA-256" for example).
|
||||
*/
|
||||
public String algorithm() {
|
||||
return algorithm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the set of module names for which hashes are recorded.
|
||||
*/
|
||||
public Set<String> names() {
|
||||
return nameToHash.keySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the hash string for the given module name, {@code null}
|
||||
* if there is no hash recorded for the module.
|
||||
*/
|
||||
public String hashFor(String mn) {
|
||||
return nameToHash.get(mn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns unmodifiable map of module name to hash string.
|
||||
*/
|
||||
public Map<String, String> hashes() {
|
||||
return nameToHash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the hash for the given file with the given message digest
|
||||
@ -96,7 +102,7 @@ public class Hasher {
|
||||
* @throws UncheckedIOException if an I/O error occurs
|
||||
* @throws RuntimeException if the algorithm is not available
|
||||
*/
|
||||
public static String generate(Path file, String algorithm) {
|
||||
public static String computeHashAsString(Path file, String algorithm) {
|
||||
try {
|
||||
MessageDigest md = MessageDigest.getInstance(algorithm);
|
||||
|
||||
@ -104,8 +110,7 @@ public class Hasher {
|
||||
// memory when jlink is running concurrently on very large jmods
|
||||
try (FileChannel fc = FileChannel.open(file)) {
|
||||
ByteBuffer bb = ByteBuffer.allocate(32*1024);
|
||||
int nread;
|
||||
while ((nread = fc.read(bb)) > 0) {
|
||||
while (fc.read(bb) > 0) {
|
||||
bb.flip();
|
||||
md.update(bb);
|
||||
assert bb.remaining() == 0;
|
||||
@ -124,19 +129,19 @@ public class Hasher {
|
||||
|
||||
/**
|
||||
* Computes the hash for every entry in the given map, returning a
|
||||
* {@code DependencyHashes} to encapsulate the result. The map key is
|
||||
* {@code ModuleHashes} to encapsulate the result. The map key is
|
||||
* the entry name, typically the module name. The map value is the file
|
||||
* path to the entry (module artifact).
|
||||
*
|
||||
* @return DependencyHashes encapsulate the hashes
|
||||
* @return ModuleHashes encapsulate the hashes
|
||||
*/
|
||||
public static DependencyHashes generate(Map<String, Path> map, String algorithm) {
|
||||
public static ModuleHashes generate(Map<String, Path> map, String algorithm) {
|
||||
Map<String, String> nameToHash = new HashMap<>();
|
||||
for (Map.Entry<String, Path> entry: map.entrySet()) {
|
||||
String name = entry.getKey();
|
||||
Path path = entry.getValue();
|
||||
nameToHash.put(name, generate(path, algorithm));
|
||||
nameToHash.put(name, computeHashAsString(path, algorithm));
|
||||
}
|
||||
return new DependencyHashes(algorithm, nameToHash);
|
||||
return new ModuleHashes(algorithm, nameToHash);
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2014, 2016, 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
|
||||
@ -41,7 +41,6 @@ import jdk.internal.org.objectweb.asm.ClassReader;
|
||||
import jdk.internal.org.objectweb.asm.ClassVisitor;
|
||||
import jdk.internal.org.objectweb.asm.ClassWriter;
|
||||
import jdk.internal.org.objectweb.asm.Opcodes;
|
||||
import jdk.internal.module.Hasher.DependencyHashes;
|
||||
|
||||
import static jdk.internal.module.ClassFileAttributes.*;
|
||||
|
||||
@ -69,7 +68,7 @@ public final class ModuleInfoExtender {
|
||||
private String osVersion;
|
||||
|
||||
// the hashes for the Hashes attribute
|
||||
private DependencyHashes hashes;
|
||||
private ModuleHashes hashes;
|
||||
|
||||
private ModuleInfoExtender(InputStream in) {
|
||||
this.in = in;
|
||||
@ -113,10 +112,10 @@ public final class ModuleInfoExtender {
|
||||
|
||||
/**
|
||||
* The Hashes attribute will be emitted to the module-info with
|
||||
* the hashes encapsulated in the given {@code DependencyHashes}
|
||||
* the hashes encapsulated in the given {@code ModuleHashes}
|
||||
* object.
|
||||
*/
|
||||
public ModuleInfoExtender hashes(DependencyHashes hashes) {
|
||||
public ModuleInfoExtender hashes(ModuleHashes hashes) {
|
||||
this.hashes = hashes;
|
||||
return this;
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2016, 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
|
||||
@ -49,28 +49,22 @@ public final class ModuleInfoWriter {
|
||||
* Writes the given module descriptor to a module-info.class file,
|
||||
* returning it in a byte array.
|
||||
*/
|
||||
private static byte[] toModuleInfo(ModuleDescriptor descriptor) {
|
||||
private static byte[] toModuleInfo(ModuleDescriptor md) {
|
||||
|
||||
ClassWriter cw = new ClassWriter(0);
|
||||
|
||||
String name = descriptor.name().replace('.', '/') + "/module-info";
|
||||
String name = md.name().replace('.', '/') + "/module-info";
|
||||
cw.visit(Opcodes.V1_8, ACC_MODULE, name, null, null, null);
|
||||
|
||||
cw.visitAttribute(new ModuleAttribute(descriptor));
|
||||
cw.visitAttribute(new ConcealedPackagesAttribute(descriptor.conceals()));
|
||||
|
||||
Optional<Version> oversion = descriptor.version();
|
||||
if (oversion.isPresent())
|
||||
cw.visitAttribute(new VersionAttribute(oversion.get()));
|
||||
|
||||
Optional<String> omain = descriptor.mainClass();
|
||||
if (omain.isPresent())
|
||||
cw.visitAttribute(new MainClassAttribute(omain.get()));
|
||||
cw.visitAttribute(new ModuleAttribute(md));
|
||||
cw.visitAttribute(new ConcealedPackagesAttribute(md.conceals()));
|
||||
md.version().ifPresent(v -> cw.visitAttribute(new VersionAttribute(v)));
|
||||
md.mainClass().ifPresent(mc -> cw.visitAttribute(new MainClassAttribute(mc)));
|
||||
|
||||
// write the TargetPlatform attribute if have any of OS name/arch/version
|
||||
String osName = descriptor.osName().orElse(null);
|
||||
String osArch = descriptor.osArch().orElse(null);
|
||||
String osVersion = descriptor.osVersion().orElse(null);
|
||||
String osName = md.osName().orElse(null);
|
||||
String osArch = md.osArch().orElse(null);
|
||||
String osVersion = md.osVersion().orElse(null);
|
||||
if (osName != null || osArch != null || osVersion != null) {
|
||||
cw.visitAttribute(new TargetPlatformAttribute(osName,
|
||||
osArch,
|
||||
|
@ -91,56 +91,29 @@ public final class ModulePatcher {
|
||||
|
||||
Map<String, List<Path>> map = new HashMap<>();
|
||||
while (value != null) {
|
||||
|
||||
// <module>=<file>(:<file>)*
|
||||
|
||||
int pos = value.indexOf('=');
|
||||
|
||||
if (pos == -1 && index > 0)
|
||||
if (pos == -1)
|
||||
throwIAE("Unable to parse: " + value);
|
||||
|
||||
if (pos == 0)
|
||||
throwIAE("Missing module name: " + value);
|
||||
|
||||
if (pos > 0) {
|
||||
String mn = value.substring(0, pos);
|
||||
List<Path> list = map.get(mn);
|
||||
if (list != null)
|
||||
throwIAE("Module " + mn + " specified more than once");
|
||||
list = new ArrayList<>();
|
||||
map.put(mn, list);
|
||||
|
||||
// new format: <module>=<file>(:<file>)*
|
||||
|
||||
String mn = value.substring(0, pos);
|
||||
List<Path> list = map.get(mn);
|
||||
if (list != null)
|
||||
throwIAE("Module " + mn + " specified more than once");
|
||||
list = new ArrayList<>();
|
||||
map.put(mn, list);
|
||||
|
||||
String paths = value.substring(pos+1);
|
||||
for (String path : paths.split(File.pathSeparator)) {
|
||||
if (!path.isEmpty()) {
|
||||
list.add(Paths.get(path));
|
||||
}
|
||||
String paths = value.substring(pos+1);
|
||||
for (String path : paths.split(File.pathSeparator)) {
|
||||
if (!path.isEmpty()) {
|
||||
list.add(Paths.get(path));
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// old format: <dir>(:<dir>)*
|
||||
|
||||
assert index == 0; // old format only allowed in first -Xpatch
|
||||
|
||||
String[] dirs = value.split(File.pathSeparator);
|
||||
for (String d : dirs) {
|
||||
if (d.length() > 0) {
|
||||
Path top = Paths.get(d);
|
||||
try {
|
||||
Files.list(top).forEach(e -> {
|
||||
String mn = e.getFileName().toString();
|
||||
Path dir = top.resolve(mn);
|
||||
map.computeIfAbsent(mn, k -> new ArrayList<>())
|
||||
.add(dir);
|
||||
});
|
||||
} catch (IOException ignore) { }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
index++;
|
||||
value = System.getProperty(PATCH_PROPERTY_PREFIX + index);
|
||||
}
|
||||
@ -175,7 +148,8 @@ public final class ModulePatcher {
|
||||
for (Path file : paths) {
|
||||
if (Files.isRegularFile(file)) {
|
||||
|
||||
// JAR file
|
||||
// JAR file - do not open as a multi-release JAR as this
|
||||
// is not supported by the boot class loader
|
||||
try (JarFile jf = new JarFile(file.toFile())) {
|
||||
jf.stream()
|
||||
.filter(e -> e.getName().endsWith(".class"))
|
||||
@ -209,10 +183,11 @@ public final class ModulePatcher {
|
||||
descriptor = JLMA.newModuleDescriptor(descriptor, packages);
|
||||
}
|
||||
|
||||
// return a new module reference
|
||||
// return a module reference to the patched module
|
||||
URI location = mref.location().orElse(null);
|
||||
return new ModuleReference(descriptor, location,
|
||||
() -> new PatchedModuleReader(paths, mref));
|
||||
return JLMA.newPatchedModule(descriptor,
|
||||
location,
|
||||
() -> new PatchedModuleReader(paths, mref));
|
||||
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2016, 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
|
||||
@ -58,7 +58,7 @@ public class Modules {
|
||||
* Creates a new Module. The module has the given ModuleDescriptor and
|
||||
* is defined to the given class loader.
|
||||
*
|
||||
* The resulting Module is in a larva state in that it does not not read
|
||||
* The resulting Module is in a larval state in that it does not not read
|
||||
* any other module and does not have any exports.
|
||||
*
|
||||
* The URI is for information purposes only.
|
||||
@ -74,7 +74,7 @@ public class Modules {
|
||||
* Define a new module to the VM. The module has the given set of
|
||||
* concealed packages and is defined to the given class loader.
|
||||
*
|
||||
* The resulting Module is in a larva state in that it does not not read
|
||||
* The resulting Module is in a larval state in that it does not not read
|
||||
* any other module and does not have any exports.
|
||||
*/
|
||||
public static Module defineModule(ClassLoader loader,
|
||||
@ -95,6 +95,13 @@ public class Modules {
|
||||
JLRMA.addReads(m1, m2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update module {@code m} to read all unnamed modules.
|
||||
*/
|
||||
public static void addReadsAllUnnamed(Module m) {
|
||||
JLRMA.addReadsAllUnnamed(m);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates module m1 to export a package to module m2.
|
||||
* Same as m1.addExports(pkg, m2) but without a caller check.
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2014, 2016, 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
|
||||
@ -29,94 +29,105 @@ import java.lang.reflect.Module;
|
||||
import java.lang.module.ModuleDescriptor;
|
||||
import java.lang.module.ModuleDescriptor.Provides;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReadWriteLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* A services catalog. Each {@code ClassLoader} has an optional {@code
|
||||
* ServicesCatalog} for modules that provide services. This is to support
|
||||
* ClassLoader centric ServiceLoader.load methods.
|
||||
* A <em>services catalog</em>. Each {@code ClassLoader} and {@code Layer} has
|
||||
* an optional {@code ServicesCatalog} for modules that provide services.
|
||||
*
|
||||
* @see java.util.ServiceLoader
|
||||
*/
|
||||
public class ServicesCatalog {
|
||||
|
||||
// use RW locks as register is rare
|
||||
private final ReadWriteLock lock = new ReentrantReadWriteLock();
|
||||
private final Lock readLock = lock.readLock();
|
||||
private final Lock writeLock = lock.writeLock();
|
||||
public interface ServicesCatalog {
|
||||
|
||||
/**
|
||||
* Represents a service provider in the services catalog.
|
||||
*/
|
||||
public class ServiceProvider {
|
||||
public final class ServiceProvider {
|
||||
private final Module module;
|
||||
private final String providerName;
|
||||
ServiceProvider(Module module, String providerName) {
|
||||
|
||||
public ServiceProvider(Module module, String providerName) {
|
||||
this.module = module;
|
||||
this.providerName = providerName;
|
||||
}
|
||||
|
||||
public Module module() {
|
||||
return module;
|
||||
}
|
||||
|
||||
public String providerName() {
|
||||
return providerName;
|
||||
}
|
||||
}
|
||||
|
||||
// service providers
|
||||
private final Map<String, Set<ServiceProvider>> loaderServices = new HashMap<>();
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(module, providerName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new module catalog.
|
||||
*/
|
||||
public ServicesCatalog() { }
|
||||
|
||||
/**
|
||||
* Registers the module in this module catalog.
|
||||
*/
|
||||
public void register(Module m) {
|
||||
ModuleDescriptor descriptor = m.getDescriptor();
|
||||
|
||||
writeLock.lock();
|
||||
try {
|
||||
// extend the services map
|
||||
for (Provides ps : descriptor.provides().values()) {
|
||||
String service = ps.service();
|
||||
Set<String> providerNames = ps.providers();
|
||||
|
||||
// create a new set to replace the existing
|
||||
Set<ServiceProvider> result = new HashSet<>();
|
||||
Set<ServiceProvider> providers = loaderServices.get(service);
|
||||
if (providers != null) {
|
||||
result.addAll(providers);
|
||||
}
|
||||
for (String pn : providerNames) {
|
||||
result.add(new ServiceProvider(m, pn));
|
||||
}
|
||||
loaderServices.put(service, Collections.unmodifiableSet(result));
|
||||
}
|
||||
|
||||
} finally {
|
||||
writeLock.unlock();
|
||||
@Override
|
||||
public boolean equals(Object ob) {
|
||||
if (!(ob instanceof ServiceProvider))
|
||||
return false;
|
||||
ServiceProvider that = (ServiceProvider)ob;
|
||||
return Objects.equals(this.module, that.module)
|
||||
&& Objects.equals(this.providerName, that.providerName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the providers in the given module in this services catalog.
|
||||
*
|
||||
* @throws UnsupportedOperationException
|
||||
* If this services catalog is immutable
|
||||
*/
|
||||
void register(Module module);
|
||||
|
||||
/**
|
||||
* Returns the (possibly empty) set of service providers that implement the
|
||||
* given service type.
|
||||
*
|
||||
* @see java.util.ServiceLoader
|
||||
*/
|
||||
public Set<ServiceProvider> findServices(String service) {
|
||||
readLock.lock();
|
||||
try {
|
||||
return loaderServices.getOrDefault(service, Collections.emptySet());
|
||||
} finally {
|
||||
readLock.unlock();
|
||||
}
|
||||
Set<ServiceProvider> findServices(String service);
|
||||
|
||||
/**
|
||||
* Creates a ServicesCatalog that supports concurrent registration and
|
||||
* and lookup.
|
||||
*/
|
||||
static ServicesCatalog create() {
|
||||
return new ServicesCatalog() {
|
||||
|
||||
private Map<String, Set<ServiceProvider>> map = new ConcurrentHashMap<>();
|
||||
|
||||
@Override
|
||||
public void register(Module m) {
|
||||
ModuleDescriptor descriptor = m.getDescriptor();
|
||||
|
||||
for (Provides provides : descriptor.provides().values()) {
|
||||
String service = provides.service();
|
||||
Set<String> providerNames = provides.providers();
|
||||
|
||||
// create a new set to replace the existing
|
||||
Set<ServiceProvider> result = new HashSet<>();
|
||||
Set<ServiceProvider> providers = map.get(service);
|
||||
if (providers != null) {
|
||||
result.addAll(providers);
|
||||
}
|
||||
for (String pn : providerNames) {
|
||||
result.add(new ServiceProvider(m, pn));
|
||||
}
|
||||
map.put(service, Collections.unmodifiableSet(result));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ServiceProvider> findServices(String service) {
|
||||
return map.getOrDefault(service, Collections.emptySet());
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@ -40,21 +40,26 @@ import java.lang.module.ModuleDescriptor;
|
||||
*/
|
||||
public final class SystemModules {
|
||||
/**
|
||||
* Name of the installed modules.
|
||||
* Name of the system modules.
|
||||
*
|
||||
* This array provides a way for InstalledModuleFinder to fallback
|
||||
* This array provides a way for SystemModuleFinder to fallback
|
||||
* and read module-info.class from the run-time image instead of
|
||||
* the fastpath.
|
||||
*/
|
||||
public static final String[] MODULE_NAMES = new String[1];
|
||||
|
||||
/**
|
||||
* Hash of system modules.
|
||||
*/
|
||||
public static String[] MODULES_TO_HASH = new String[1];
|
||||
|
||||
/**
|
||||
* Number of packages in the boot layer from the installed modules.
|
||||
*
|
||||
* Don't make it final to avoid inlining during compile time as
|
||||
* the value will be changed at jlink time.
|
||||
*/
|
||||
public static final int PACKAGES_IN_BOOT_LAYER = 1024;
|
||||
public static int PACKAGES_IN_BOOT_LAYER = 1024;
|
||||
|
||||
/**
|
||||
* Returns a non-empty array of ModuleDescriptors in the run-time image.
|
||||
@ -64,4 +69,5 @@ public final class SystemModules {
|
||||
public static ModuleDescriptor[] modules() {
|
||||
return new ModuleDescriptor[0];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -166,6 +166,8 @@ module java.base {
|
||||
java.sql,
|
||||
java.xml,
|
||||
jdk.charsets,
|
||||
jdk.jartool,
|
||||
jdk.jlink,
|
||||
jdk.net,
|
||||
jdk.scripting.nashorn,
|
||||
jdk.unsupported,
|
||||
|
@ -27,7 +27,7 @@
|
||||
java.launcher.opt.header = Usage: {0} [options] class [args...]\n\
|
||||
\ (to execute a class)\n or {0} [options] -jar jarfile [args...]\n\
|
||||
\ (to execute a jar file)\n\
|
||||
\ or {0} [-options] -mp <modulepath> -m <modulename> | <modulename>/<mainclass>\n\
|
||||
\ or {0} [options] -mp <modulepath> -m <modulename>[/<mainclass>] [args...]\n\
|
||||
\ (to execute the main class in a module)\n\
|
||||
where options include:\n
|
||||
|
||||
@ -51,8 +51,9 @@ java.launcher.opt.footer =\ -cp <class search path of directories and zip
|
||||
\ A {0} separated list of directories, each directory\n\
|
||||
\ is a directory of modules that replace upgradeable\n\
|
||||
\ modules in the runtime image\n\
|
||||
\ -m <modulename> | <modulename>/<mainclass>\n\
|
||||
\ the initial or main module to resolve\n\
|
||||
\ -m <modulename>[/<mainclass>]\n\
|
||||
\ the initial module to resolve, and the name of the main class\n\
|
||||
\ to execute if not specified by the module\n\
|
||||
\ -addmods <modulename>[,<modulename>...]\n\
|
||||
\ root modules to resolve in addition to the initial module\n\
|
||||
\ -limitmods <modulename>[,<modulename>...]\n\
|
||||
|
@ -136,10 +136,10 @@ class GNUStyleOptions {
|
||||
jartool.moduleVersion = Version.parse(arg);
|
||||
}
|
||||
},
|
||||
new Option(true, OptionType.CREATE_UPDATE, "--hash-dependencies") {
|
||||
new Option(true, OptionType.CREATE_UPDATE, "--hash-modules") {
|
||||
void process(Main jartool, String opt, String arg) throws BadArgs {
|
||||
try {
|
||||
jartool.dependenciesToHash = Pattern.compile(arg);
|
||||
jartool.modulesToHash = Pattern.compile(arg);
|
||||
} catch (PatternSyntaxException e) {
|
||||
throw new BadArgs("err.badpattern", arg).showUsage(true);
|
||||
}
|
||||
|
@ -26,21 +26,25 @@
|
||||
package sun.tools.jar;
|
||||
|
||||
import java.io.*;
|
||||
import java.lang.module.Configuration;
|
||||
import java.lang.module.ModuleDescriptor;
|
||||
import java.lang.module.ModuleDescriptor.Exports;
|
||||
import java.lang.module.ModuleDescriptor.Provides;
|
||||
import java.lang.module.ModuleDescriptor.Requires;
|
||||
import java.lang.module.ModuleDescriptor.Version;
|
||||
import java.lang.module.ModuleFinder;
|
||||
import java.lang.module.ModuleReader;
|
||||
import java.lang.module.ModuleReference;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.module.ResolutionException;
|
||||
import java.lang.module.ResolvedModule;
|
||||
import java.net.URI;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.zip.*;
|
||||
@ -49,9 +53,12 @@ import java.util.jar.Pack200.*;
|
||||
import java.util.jar.Manifest;
|
||||
import java.text.MessageFormat;
|
||||
|
||||
import jdk.internal.module.Hasher;
|
||||
import jdk.internal.misc.JavaLangModuleAccess;
|
||||
import jdk.internal.misc.SharedSecrets;
|
||||
import jdk.internal.module.ModuleHashes;
|
||||
import jdk.internal.module.ModuleInfoExtender;
|
||||
import jdk.internal.util.jar.JarIndex;
|
||||
|
||||
import static jdk.internal.util.jar.JarIndex.INDEX_NAME;
|
||||
import static java.util.jar.JarFile.MANIFEST_NAME;
|
||||
import static java.util.stream.Collectors.joining;
|
||||
@ -117,7 +124,7 @@ class Main {
|
||||
/* Modular jar related options */
|
||||
boolean printModuleDescriptor;
|
||||
Version moduleVersion;
|
||||
Pattern dependenciesToHash;
|
||||
Pattern modulesToHash;
|
||||
ModuleFinder moduleFinder = ModuleFinder.empty();
|
||||
|
||||
private static final String MODULE_INFO = "module-info.class";
|
||||
@ -241,7 +248,7 @@ class Main {
|
||||
if (isModularJar()) {
|
||||
moduleInfoBytes = addExtendedModuleAttributes(
|
||||
readModuleInfo(moduleInfo));
|
||||
} else if (moduleVersion != null || dependenciesToHash != null) {
|
||||
} else if (moduleVersion != null || modulesToHash != null) {
|
||||
error(getMsg("error.module.options.without.info"));
|
||||
return false;
|
||||
}
|
||||
@ -801,7 +808,7 @@ class Main {
|
||||
}
|
||||
} else if (isModuleInfoEntry
|
||||
&& ((newModuleInfoBytes != null) || (ename != null)
|
||||
|| moduleVersion != null || dependenciesToHash != null)) {
|
||||
|| moduleVersion != null || modulesToHash != null)) {
|
||||
if (newModuleInfoBytes == null) {
|
||||
// Update existing module-info.class
|
||||
newModuleInfoBytes = readModuleInfo(zis);
|
||||
@ -861,7 +868,7 @@ class Main {
|
||||
if (!updateModuleInfo(newModuleInfoBytes, zos)) {
|
||||
updateOk = false;
|
||||
}
|
||||
} else if (moduleVersion != null || dependenciesToHash != null) {
|
||||
} else if (moduleVersion != null || modulesToHash != null) {
|
||||
error(getMsg("error.module.options.without.info"));
|
||||
updateOk = false;
|
||||
}
|
||||
@ -1642,70 +1649,60 @@ class Main {
|
||||
return false;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static <T> String toString(Set<T> set) {
|
||||
if (set.isEmpty()) { return ""; }
|
||||
return set.stream().map(e -> e.toString().toLowerCase(Locale.ROOT))
|
||||
.collect(joining(" "));
|
||||
}
|
||||
|
||||
private static final JavaLangModuleAccess JLMA = SharedSecrets.getJavaLangModuleAccess();
|
||||
|
||||
private void printModuleDescriptor(InputStream entryInputStream)
|
||||
throws IOException
|
||||
{
|
||||
ModuleDescriptor md = ModuleDescriptor.read(entryInputStream);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("\nName:\n " + md.toNameAndVersion());
|
||||
sb.append("\n").append(md.toNameAndVersion());
|
||||
|
||||
Set<Requires> requires = md.requires();
|
||||
if (!requires.isEmpty()) {
|
||||
sb.append("\nRequires:");
|
||||
requires.forEach(r ->
|
||||
sb.append("\n ").append(r.name())
|
||||
.append(toString(r.modifiers(), " [ ", " ]")));
|
||||
}
|
||||
md.requires().stream()
|
||||
.sorted(Comparator.comparing(Requires::name))
|
||||
.forEach(r -> {
|
||||
sb.append("\n requires ");
|
||||
if (!r.modifiers().isEmpty())
|
||||
sb.append(toString(r.modifiers())).append(" ");
|
||||
sb.append(r.name());
|
||||
});
|
||||
|
||||
Set<String> s = md.uses();
|
||||
if (!s.isEmpty()) {
|
||||
sb.append("\nUses: ");
|
||||
s.forEach(sv -> sb.append("\n ").append(sv));
|
||||
}
|
||||
md.uses().stream().sorted()
|
||||
.forEach(p -> sb.append("\n uses ").append(p));
|
||||
|
||||
Set<Exports> exports = md.exports();
|
||||
if (!exports.isEmpty()) {
|
||||
sb.append("\nExports:");
|
||||
exports.forEach(sv -> sb.append("\n ").append(sv));
|
||||
}
|
||||
md.exports().stream()
|
||||
.sorted(Comparator.comparing(Exports::source))
|
||||
.forEach(p -> sb.append("\n exports ").append(p));
|
||||
|
||||
Map<String,Provides> provides = md.provides();
|
||||
if (!provides.isEmpty()) {
|
||||
sb.append("\nProvides: ");
|
||||
provides.values().forEach(p ->
|
||||
sb.append("\n ").append(p.service())
|
||||
.append(" with ")
|
||||
.append(toString(p.providers(), "", "")));
|
||||
}
|
||||
md.conceals().stream().sorted()
|
||||
.forEach(p -> sb.append("\n conceals ").append(p));
|
||||
|
||||
Optional<String> mc = md.mainClass();
|
||||
if (mc.isPresent())
|
||||
sb.append("\nMain class:\n " + mc.get());
|
||||
md.provides().values().stream()
|
||||
.sorted(Comparator.comparing(Provides::service))
|
||||
.forEach(p -> sb.append("\n provides ").append(p.service())
|
||||
.append(" with ")
|
||||
.append(toString(p.providers())));
|
||||
|
||||
s = md.conceals();
|
||||
if (!s.isEmpty()) {
|
||||
sb.append("\nConceals:");
|
||||
s.forEach(p -> sb.append("\n ").append(p));
|
||||
}
|
||||
md.mainClass().ifPresent(v -> sb.append("\n main-class " + v));
|
||||
|
||||
try {
|
||||
Method m = ModuleDescriptor.class.getDeclaredMethod("hashes");
|
||||
m.setAccessible(true);
|
||||
Optional<Hasher.DependencyHashes> optHashes =
|
||||
(Optional<Hasher.DependencyHashes>) m.invoke(md);
|
||||
md.osName().ifPresent(v -> sb.append("\n operating-system-name " + v));
|
||||
|
||||
md.osArch().ifPresent(v -> sb.append("\n operating-system-architecture " + v));
|
||||
|
||||
md.osVersion().ifPresent(v -> sb.append("\n operating-system-version " + v));
|
||||
|
||||
JLMA.hashes(md).ifPresent(hashes ->
|
||||
hashes.names().stream().sorted().forEach(
|
||||
mod -> sb.append("\n hashes ").append(mod).append(" ")
|
||||
.append(hashes.algorithm()).append(" ")
|
||||
.append(hashes.hashFor(mod))));
|
||||
|
||||
if (optHashes.isPresent()) {
|
||||
Hasher.DependencyHashes hashes = optHashes.get();
|
||||
sb.append("\nHashes:");
|
||||
sb.append("\n Algorithm: " + hashes.algorithm());
|
||||
hashes.names().stream().forEach(mod ->
|
||||
sb.append("\n ").append(mod)
|
||||
.append(": ").append(hashes.hashFor(mod)));
|
||||
}
|
||||
} catch (ReflectiveOperationException x) {
|
||||
throw new InternalError(x);
|
||||
}
|
||||
output(sb.toString());
|
||||
}
|
||||
|
||||
@ -1751,7 +1748,6 @@ class Main {
|
||||
md = ModuleDescriptor.read(in);
|
||||
}
|
||||
String name = md.name();
|
||||
Set<ModuleDescriptor.Requires> dependences = md.requires();
|
||||
Set<String> exported = md.exports()
|
||||
.stream()
|
||||
.map(ModuleDescriptor.Exports::source)
|
||||
@ -1778,9 +1774,17 @@ class Main {
|
||||
if (moduleVersion != null)
|
||||
extender.version(moduleVersion);
|
||||
|
||||
// --hash-dependencies
|
||||
if (dependenciesToHash != null)
|
||||
extender.hashes(hashDependences(name, dependences));
|
||||
// --hash-modules
|
||||
if (modulesToHash != null) {
|
||||
Hasher hasher = new Hasher(md, fname);
|
||||
ModuleHashes moduleHashes = hasher.computeHashes(name);
|
||||
if (moduleHashes != null) {
|
||||
extender.hashes(moduleHashes);
|
||||
} else {
|
||||
// should it issue warning or silent?
|
||||
System.out.println("warning: no module is recorded in hash in " + name);
|
||||
}
|
||||
}
|
||||
|
||||
extender.write(baos);
|
||||
return baos.toByteArray();
|
||||
@ -1788,36 +1792,156 @@ class Main {
|
||||
}
|
||||
|
||||
/**
|
||||
* Examines the module dependences of the given module and computes the
|
||||
* hash of any module that matches the pattern {@code dependenciesToHash}.
|
||||
* Compute and record hashes
|
||||
*/
|
||||
private Hasher.DependencyHashes
|
||||
hashDependences(String name,
|
||||
Set<ModuleDescriptor.Requires> moduleDependences)
|
||||
throws IOException
|
||||
{
|
||||
Map<String, Path> map = new HashMap<>();
|
||||
Matcher matcher = dependenciesToHash.matcher("");
|
||||
for (ModuleDescriptor.Requires md: moduleDependences) {
|
||||
String dn = md.name();
|
||||
if (matcher.reset(dn).find()) {
|
||||
Optional<ModuleReference> omref = moduleFinder.find(dn);
|
||||
if (!omref.isPresent()) {
|
||||
throw new IOException(formatMsg2("error.hash.dep", name , dn));
|
||||
}
|
||||
map.put(dn, modRefToPath(omref.get()));
|
||||
private class Hasher {
|
||||
final ModuleFinder finder;
|
||||
final Map<String, Path> moduleNameToPath;
|
||||
final Set<String> modules;
|
||||
final Configuration configuration;
|
||||
Hasher(ModuleDescriptor descriptor, String fname) throws IOException {
|
||||
// Create a module finder that finds the modular JAR
|
||||
// being created/updated
|
||||
URI uri = Paths.get(fname).toUri();
|
||||
ModuleReference mref = new ModuleReference(descriptor, uri,
|
||||
new Supplier<>() {
|
||||
@Override
|
||||
public ModuleReader get() {
|
||||
throw new UnsupportedOperationException("should not reach here");
|
||||
}
|
||||
});
|
||||
|
||||
// Compose a module finder with the module path and
|
||||
// the modular JAR being created or updated
|
||||
this.finder = ModuleFinder.compose(moduleFinder,
|
||||
new ModuleFinder() {
|
||||
@Override
|
||||
public Optional<ModuleReference> find(String name) {
|
||||
if (descriptor.name().equals(name))
|
||||
return Optional.of(mref);
|
||||
else
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ModuleReference> findAll() {
|
||||
return Collections.singleton(mref);
|
||||
}
|
||||
});
|
||||
|
||||
// Determine the modules that matches the modulesToHash pattern
|
||||
this.modules = moduleFinder.findAll().stream()
|
||||
.map(moduleReference -> moduleReference.descriptor().name())
|
||||
.filter(mn -> modulesToHash.matcher(mn).find())
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
// a map from a module name to Path of the modular JAR
|
||||
this.moduleNameToPath = moduleFinder.findAll().stream()
|
||||
.map(ModuleReference::descriptor)
|
||||
.map(ModuleDescriptor::name)
|
||||
.collect(Collectors.toMap(Function.identity(), mn -> moduleToPath(mn)));
|
||||
|
||||
Configuration config = null;
|
||||
try {
|
||||
config = Configuration.empty()
|
||||
.resolveRequires(ModuleFinder.ofSystem(), finder, modules);
|
||||
} catch (ResolutionException e) {
|
||||
// should it throw an error? or emit a warning
|
||||
System.out.println("warning: " + e.getMessage());
|
||||
}
|
||||
this.configuration = config;
|
||||
}
|
||||
|
||||
if (map.size() == 0) {
|
||||
return null;
|
||||
} else {
|
||||
return Hasher.generate(map, "SHA-256");
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Compute hashes of the modules that depend upon the specified
|
||||
* module directly or indirectly.
|
||||
*/
|
||||
ModuleHashes computeHashes(String name) {
|
||||
// the transposed graph includes all modules in the resolved graph
|
||||
Map<String, Set<String>> graph = transpose();
|
||||
|
||||
private static Path modRefToPath(ModuleReference mref) {
|
||||
URI location = mref.location().get();
|
||||
return Paths.get(location);
|
||||
// find the modules that transitively depend upon the specified name
|
||||
Deque<String> deque = new ArrayDeque<>();
|
||||
deque.add(name);
|
||||
Set<String> mods = visitNodes(graph, deque);
|
||||
|
||||
// filter modules matching the pattern specified --hash-modules
|
||||
// as well as itself as the jmod file is being generated
|
||||
Map<String, Path> modulesForHash = mods.stream()
|
||||
.filter(mn -> !mn.equals(name) && modules.contains(mn))
|
||||
.collect(Collectors.toMap(Function.identity(), moduleNameToPath::get));
|
||||
|
||||
if (modulesForHash.isEmpty())
|
||||
return null;
|
||||
|
||||
return ModuleHashes.generate(modulesForHash, "SHA-256");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all nodes traversed from the given roots.
|
||||
*/
|
||||
private Set<String> visitNodes(Map<String, Set<String>> graph,
|
||||
Deque<String> roots) {
|
||||
Set<String> visited = new HashSet<>();
|
||||
while (!roots.isEmpty()) {
|
||||
String mn = roots.pop();
|
||||
if (!visited.contains(mn)) {
|
||||
visited.add(mn);
|
||||
|
||||
// the given roots may not be part of the graph
|
||||
if (graph.containsKey(mn)) {
|
||||
for (String dm : graph.get(mn)) {
|
||||
if (!visited.contains(dm))
|
||||
roots.push(dm);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return visited;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a transposed graph from the resolved module graph.
|
||||
*/
|
||||
private Map<String, Set<String>> transpose() {
|
||||
Map<String, Set<String>> transposedGraph = new HashMap<>();
|
||||
Deque<String> deque = new ArrayDeque<>(modules);
|
||||
|
||||
Set<String> visited = new HashSet<>();
|
||||
while (!deque.isEmpty()) {
|
||||
String mn = deque.pop();
|
||||
if (!visited.contains(mn)) {
|
||||
visited.add(mn);
|
||||
|
||||
// add an empty set
|
||||
transposedGraph.computeIfAbsent(mn, _k -> new HashSet<>());
|
||||
|
||||
ResolvedModule resolvedModule = configuration.findModule(mn).get();
|
||||
for (ResolvedModule dm : resolvedModule.reads()) {
|
||||
String name = dm.name();
|
||||
if (!visited.contains(name)) {
|
||||
deque.push(name);
|
||||
}
|
||||
// reverse edge
|
||||
transposedGraph.computeIfAbsent(name, _k -> new HashSet<>())
|
||||
.add(mn);
|
||||
}
|
||||
}
|
||||
}
|
||||
return transposedGraph;
|
||||
}
|
||||
|
||||
private Path moduleToPath(String name) {
|
||||
ModuleReference mref = moduleFinder.find(name).orElseThrow(
|
||||
() -> new InternalError(formatMsg2("error.hash.dep",name , name)));
|
||||
|
||||
URI uri = mref.location().get();
|
||||
Path path = Paths.get(uri);
|
||||
String fn = path.getFileName().toString();
|
||||
if (!fn.endsWith(".jar")) {
|
||||
throw new UnsupportedOperationException(path + " is not a modular JAR");
|
||||
}
|
||||
return path;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -57,7 +57,7 @@ error.create.tempfile=\
|
||||
error.hash.dep=\
|
||||
Hashing module {0} dependences, unable to find module {1} on module path
|
||||
error.module.options.without.info=\
|
||||
One of --module-version or --hash-dependencies without module-info.class
|
||||
One of --module-version or --hash-modules without module-info.class
|
||||
error.unexpected.module-info=\
|
||||
Unexpected module descriptor {0}
|
||||
error.module.descriptor.not.found=\
|
||||
@ -178,11 +178,11 @@ main.help.opt.create.update.no-manifest=\
|
||||
main.help.opt.create.update.module-version=\
|
||||
\ --module-version=VERSION The module version, when creating a modular\n\
|
||||
\ jar, or updating a non-modular jar
|
||||
main.help.opt.create.update.hash-dependencies=\
|
||||
\ --hash-dependencies=PATTERN Compute and record the hashes of module\n\
|
||||
\ dependencies matched by the given pattern, when\n\
|
||||
\ creating a modular jar, or updating a non-modular\n\
|
||||
\ jar
|
||||
main.help.opt.create.update.hash-modules=\
|
||||
\ --hash-modules=PATTERN Compute and record the hashes of modules \n\
|
||||
\ matched by the given pattern and that depend upon\n\
|
||||
\ directly or indirectly on a modular jar being\n\
|
||||
\ created or a non-modular jar being updated
|
||||
main.help.opt.create.update.modulepath=\
|
||||
\ --modulepath Location of module dependence for generating
|
||||
\ the hash
|
||||
@ -201,7 +201,7 @@ main.help.postopt=\
|
||||
\ located in the root of the given directories, or the root of the jar archive\n\
|
||||
\ itself. The following operations are only valid when creating a modular jar,\n\
|
||||
\ or updating an existing non-modular jar: '--module-version',\n\
|
||||
\ '--hash-dependencies', and '--modulepath'.\n\
|
||||
\ '--hash-modules', and '--modulepath'.\n\
|
||||
\n\
|
||||
\ Mandatory or optional arguments to long options are also mandatory or optional\n\
|
||||
\ for any corresponding short options.
|
@ -95,27 +95,23 @@ public class DefaultImageBuilder implements ImageBuilder {
|
||||
|
||||
private final Path root;
|
||||
private final Path mdir;
|
||||
private final boolean genBom;
|
||||
private final Set<String> modules = new HashSet<>();
|
||||
|
||||
/**
|
||||
* Default image builder constructor.
|
||||
*
|
||||
* @param genBom true, generates a bom file.
|
||||
* @param root The image root directory.
|
||||
* @throws IOException
|
||||
*/
|
||||
public DefaultImageBuilder(boolean genBom, Path root) throws IOException {
|
||||
public DefaultImageBuilder(Path root) throws IOException {
|
||||
Objects.requireNonNull(root);
|
||||
|
||||
this.genBom = genBom;
|
||||
|
||||
this.root = root;
|
||||
this.mdir = root.resolve("lib");
|
||||
Files.createDirectories(mdir);
|
||||
}
|
||||
|
||||
private void storeFiles(Set<String> modules, String bom, Properties release) throws IOException {
|
||||
private void storeFiles(Set<String> modules, Properties release) throws IOException {
|
||||
if (release != null) {
|
||||
addModules(release, modules);
|
||||
File r = new File(root.toFile(), "release");
|
||||
@ -123,11 +119,6 @@ public class DefaultImageBuilder implements ImageBuilder {
|
||||
release.store(fo, null);
|
||||
}
|
||||
}
|
||||
// Generate bom
|
||||
if (genBom) {
|
||||
File bomFile = new File(root.toFile(), "bom");
|
||||
createUtf8File(bomFile, bom);
|
||||
}
|
||||
}
|
||||
|
||||
private void addModules(Properties release, Set<String> modules) throws IOException {
|
||||
@ -144,7 +135,7 @@ public class DefaultImageBuilder implements ImageBuilder {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void storeFiles(Pool files, String bom, Properties release) {
|
||||
public void storeFiles(Pool files, Properties release) {
|
||||
try {
|
||||
for (ModuleData f : files.getContent()) {
|
||||
if (!f.getType().equals(Pool.ModuleDataType.CLASS_OR_RESOURCE)) {
|
||||
@ -161,7 +152,7 @@ public class DefaultImageBuilder implements ImageBuilder {
|
||||
modules.add(m.getName());
|
||||
}
|
||||
}
|
||||
storeFiles(modules, bom, release);
|
||||
storeFiles(modules, release);
|
||||
|
||||
if (Files.getFileStore(root).supportsFileAttributeView(PosixFileAttributeView.class)) {
|
||||
// launchers in the bin directory need execute permission
|
||||
@ -190,8 +181,8 @@ public class DefaultImageBuilder implements ImageBuilder {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void storeFiles(Pool files, String bom) {
|
||||
storeFiles(files, bom, new Properties());
|
||||
public void storeFiles(Pool files) {
|
||||
storeFiles(files, new Properties());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -213,28 +204,48 @@ public class DefaultImageBuilder implements ImageBuilder {
|
||||
mainClass = ModuleDescriptor.read(stream).mainClass();
|
||||
if (mainClass.isPresent()) {
|
||||
Path cmd = root.resolve("bin").resolve(module);
|
||||
if (!Files.exists(cmd)) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("#!/bin/sh")
|
||||
.append("\n");
|
||||
sb.append("JLINK_VM_OPTIONS=")
|
||||
.append("\n");
|
||||
sb.append("DIR=`dirname $0`")
|
||||
.append("\n");
|
||||
sb.append("$DIR/java $JLINK_VM_OPTIONS -m ")
|
||||
// generate shell script for Unix platforms
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("#!/bin/sh")
|
||||
.append("\n");
|
||||
sb.append("JLINK_VM_OPTIONS=")
|
||||
.append("\n");
|
||||
sb.append("DIR=`dirname $0`")
|
||||
.append("\n");
|
||||
sb.append("$DIR/java $JLINK_VM_OPTIONS -m ")
|
||||
.append(module).append('/')
|
||||
.append(mainClass.get())
|
||||
.append(" $@\n");
|
||||
|
||||
try (BufferedWriter writer = Files.newBufferedWriter(cmd,
|
||||
StandardCharsets.ISO_8859_1,
|
||||
StandardOpenOption.CREATE_NEW)) {
|
||||
writer.write(sb.toString());
|
||||
}
|
||||
if (Files.getFileStore(root.resolve("bin"))
|
||||
.supportsFileAttributeView(PosixFileAttributeView.class)) {
|
||||
setExecutable(cmd);
|
||||
}
|
||||
// generate .bat file for Windows
|
||||
if (isWindows()) {
|
||||
Path bat = root.resolve("bin").resolve(module + ".bat");
|
||||
sb = new StringBuilder();
|
||||
sb.append("@echo off")
|
||||
.append("\r\n");
|
||||
sb.append("set JLINK_VM_OPTIONS=")
|
||||
.append("\r\n");
|
||||
sb.append("set DIR=%~dp0")
|
||||
.append("\r\n");
|
||||
sb.append("\"%DIR%\\java\" %JLINK_VM_OPTIONS% -m ")
|
||||
.append(module).append('/')
|
||||
.append(mainClass.get())
|
||||
.append(" $@\n");
|
||||
.append(" %*\r\n");
|
||||
|
||||
try (BufferedWriter writer = Files.newBufferedWriter(cmd,
|
||||
try (BufferedWriter writer = Files.newBufferedWriter(bat,
|
||||
StandardCharsets.ISO_8859_1,
|
||||
StandardOpenOption.CREATE_NEW)) {
|
||||
writer.write(sb.toString());
|
||||
}
|
||||
if (Files.getFileStore(root.resolve("bin"))
|
||||
.supportsFileAttributeView(PosixFileAttributeView.class)) {
|
||||
setExecutable(cmd);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -42,22 +42,20 @@ public interface ImageBuilder {
|
||||
* Store the external files.
|
||||
*
|
||||
* @param content Pool of module content.
|
||||
* @param bom The options used to build the image file.
|
||||
* @param release the release properties
|
||||
* @throws PluginException
|
||||
*/
|
||||
public default void storeFiles(Pool content, String bom, Properties release) {
|
||||
storeFiles(content, bom);
|
||||
public default void storeFiles(Pool content, Properties release) {
|
||||
storeFiles(content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store the external files.
|
||||
*
|
||||
* @param content Pool of module content.
|
||||
* @param bom The options used to build the image file.
|
||||
* @throws PluginException
|
||||
*/
|
||||
public default void storeFiles(Pool content, String bom) {
|
||||
public default void storeFiles(Pool content) {
|
||||
throw new UnsupportedOperationException("storeFiles");
|
||||
}
|
||||
|
||||
|
@ -88,7 +88,7 @@ public final class ImageFileCreator {
|
||||
ByteOrder byteOrder)
|
||||
throws IOException {
|
||||
return ImageFileCreator.create(archives, byteOrder,
|
||||
new ImagePluginStack(null));
|
||||
new ImagePluginStack());
|
||||
}
|
||||
|
||||
public static ExecutableImage create(Set<Archive> archives,
|
||||
|
@ -68,20 +68,13 @@ public final class ImagePluginConfiguration {
|
||||
private ImagePluginConfiguration() {
|
||||
}
|
||||
|
||||
public static ImagePluginStack parseConfiguration(Jlink.PluginsConfiguration plugins)
|
||||
throws Exception {
|
||||
return parseConfiguration(plugins, null);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a stack of plugins from a a configuration.
|
||||
*
|
||||
*/
|
||||
public static ImagePluginStack parseConfiguration(Jlink.PluginsConfiguration pluginsConfiguration,
|
||||
String bom)
|
||||
public static ImagePluginStack parseConfiguration(Jlink.PluginsConfiguration pluginsConfiguration)
|
||||
throws Exception {
|
||||
if (pluginsConfiguration == null) {
|
||||
return new ImagePluginStack(bom);
|
||||
return new ImagePluginStack();
|
||||
}
|
||||
Map<Plugin.CATEGORY, List<Plugin>> plugins = new LinkedHashMap<>();
|
||||
for (Plugin.CATEGORY cat : CATEGORIES_ORDER) {
|
||||
@ -150,7 +143,7 @@ public final class ImagePluginConfiguration {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void storeFiles(Pool files, String bom) {
|
||||
public void storeFiles(Pool files) {
|
||||
throw new PluginException("No directory setup to store files");
|
||||
}
|
||||
};
|
||||
@ -158,6 +151,6 @@ public final class ImagePluginConfiguration {
|
||||
|
||||
PluginContext ctxt = pluginsConfiguration.getPluginContext();
|
||||
return new ImagePluginStack(builder, transformerPlugins,
|
||||
lastSorter, postProcessingPlugins, ctxt, bom);
|
||||
lastSorter, postProcessingPlugins, ctxt);
|
||||
}
|
||||
}
|
||||
|
@ -167,28 +167,25 @@ public final class ImagePluginStack {
|
||||
|
||||
private final ImageBuilder imageBuilder;
|
||||
private final Properties release;
|
||||
private final String bom;
|
||||
|
||||
public ImagePluginStack(String bom) {
|
||||
public ImagePluginStack() {
|
||||
this(null, Collections.emptyList(), null,
|
||||
Collections.emptyList(), null, bom);
|
||||
Collections.emptyList(), null);
|
||||
}
|
||||
|
||||
public ImagePluginStack(ImageBuilder imageBuilder,
|
||||
List<TransformerPlugin> contentPlugins,
|
||||
Plugin lastSorter,
|
||||
List<PostProcessorPlugin> postprocessingPlugins,
|
||||
String bom) {
|
||||
List<PostProcessorPlugin> postprocessingPlugins) {
|
||||
this(imageBuilder, contentPlugins, lastSorter,
|
||||
postprocessingPlugins, null, bom);
|
||||
postprocessingPlugins, null);
|
||||
}
|
||||
|
||||
public ImagePluginStack(ImageBuilder imageBuilder,
|
||||
List<TransformerPlugin> contentPlugins,
|
||||
Plugin lastSorter,
|
||||
List<PostProcessorPlugin> postprocessingPlugins,
|
||||
PluginContext ctxt,
|
||||
String bom) {
|
||||
PluginContext ctxt) {
|
||||
Objects.requireNonNull(contentPlugins);
|
||||
this.lastSorter = lastSorter;
|
||||
for (TransformerPlugin p : contentPlugins) {
|
||||
@ -204,7 +201,6 @@ public final class ImagePluginStack {
|
||||
}
|
||||
this.imageBuilder = imageBuilder;
|
||||
this.release = ctxt != null? ctxt.getReleaseProperties() : new Properties();
|
||||
this.bom = bom;
|
||||
}
|
||||
|
||||
public void operate(ImageProvider provider) throws Exception {
|
||||
@ -479,7 +475,7 @@ public final class ImagePluginStack {
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
imageBuilder.storeFiles(new LastPool(transformed), bom, release);
|
||||
imageBuilder.storeFiles(new LastPool(transformed), release);
|
||||
}
|
||||
|
||||
public ExecutableImage getExecutableImage() throws IOException {
|
||||
|
@ -70,6 +70,7 @@ import jdk.tools.jlink.plugin.Plugin;
|
||||
* ## Should use jdk.joptsimple some day.
|
||||
*/
|
||||
public class JlinkTask {
|
||||
private static final boolean DEBUG = Boolean.getBoolean("jlink.debug");
|
||||
|
||||
private static <T extends Throwable> void fail(Class<T> type,
|
||||
String format,
|
||||
@ -142,9 +143,6 @@ public class JlinkTask {
|
||||
}
|
||||
task.options.packagedModulesPath = path;
|
||||
}, true, "--keep-packaged-modules"),
|
||||
new Option<JlinkTask>(false, (task, opt, arg) -> {
|
||||
task.options.genbom = true;
|
||||
}, true, "--genbom"),
|
||||
new Option<JlinkTask>(true, (task, opt, arg) -> {
|
||||
task.options.saveoptsfile = arg;
|
||||
}, "--saveopts"),
|
||||
@ -175,7 +173,6 @@ public class JlinkTask {
|
||||
|
||||
static class OptionsValues {
|
||||
boolean help;
|
||||
boolean genbom;
|
||||
String saveoptsfile;
|
||||
boolean version;
|
||||
boolean fullVersion;
|
||||
@ -219,18 +216,24 @@ public class JlinkTask {
|
||||
}
|
||||
|
||||
return EXIT_OK;
|
||||
} catch (UncheckedIOException | PluginException | IOException | ResolutionException e) {
|
||||
} catch (UncheckedIOException | PluginException | IllegalArgumentException |
|
||||
IOException | ResolutionException e) {
|
||||
log.println(taskHelper.getMessage("error.prefix") + " " + e.getMessage());
|
||||
log.println(taskHelper.getMessage("main.usage.summary", PROGNAME));
|
||||
if (DEBUG) {
|
||||
e.printStackTrace(log);
|
||||
}
|
||||
return EXIT_ERROR;
|
||||
} catch (BadArgs e) {
|
||||
taskHelper.reportError(e.key, e.args);
|
||||
if (e.showUsage) {
|
||||
log.println(taskHelper.getMessage("main.usage.summary", PROGNAME));
|
||||
}
|
||||
if (DEBUG) {
|
||||
e.printStackTrace(log);
|
||||
}
|
||||
return EXIT_CMDERR;
|
||||
} catch (Throwable x) {
|
||||
log.println(taskHelper.getMessage("main.msg.bug"));
|
||||
log.println(taskHelper.getMessage("error.prefix") + " " + x.getMessage());
|
||||
x.printStackTrace(log);
|
||||
return EXIT_ABNORMAL;
|
||||
} finally {
|
||||
@ -238,16 +241,6 @@ public class JlinkTask {
|
||||
}
|
||||
}
|
||||
|
||||
private static Map<String, Path> modulesToPath(Configuration cf) {
|
||||
Map<String, Path> modPaths = new HashMap<>();
|
||||
for (ResolvedModule resolvedModule : cf.modules()) {
|
||||
ModuleReference mref = resolvedModule.reference();
|
||||
URI uri = mref.location().get();
|
||||
modPaths.put(mref.descriptor().name(), Paths.get(uri));
|
||||
}
|
||||
return modPaths;
|
||||
}
|
||||
|
||||
/*
|
||||
* Jlink API entry point.
|
||||
*/
|
||||
@ -275,8 +268,7 @@ public class JlinkTask {
|
||||
null);
|
||||
|
||||
// Then create the Plugin Stack
|
||||
ImagePluginStack stack = ImagePluginConfiguration.parseConfiguration(plugins,
|
||||
genBOMContent(config, plugins));
|
||||
ImagePluginStack stack = ImagePluginConfiguration.parseConfiguration(plugins);
|
||||
|
||||
//Ask the stack to proceed;
|
||||
stack.operate(imageProvider);
|
||||
@ -297,7 +289,7 @@ public class JlinkTask {
|
||||
}
|
||||
|
||||
private void postProcessOnly(Path existingImage) throws Exception {
|
||||
PluginsConfiguration config = taskHelper.getPluginsConfig(null, false);
|
||||
PluginsConfiguration config = taskHelper.getPluginsConfig(null);
|
||||
ExecutableImage img = DefaultImageBuilder.getExecutableImage(existingImage);
|
||||
if (img == null) {
|
||||
throw taskHelper.newBadArgs("err.existing.image.invalid");
|
||||
@ -327,8 +319,7 @@ public class JlinkTask {
|
||||
|
||||
// Then create the Plugin Stack
|
||||
ImagePluginStack stack = ImagePluginConfiguration.
|
||||
parseConfiguration(taskHelper.getPluginsConfig(options.output, options.genbom),
|
||||
genBOMContent());
|
||||
parseConfiguration(taskHelper.getPluginsConfig(options.output));
|
||||
|
||||
//Ask the stack to proceed
|
||||
stack.operate(imageProvider);
|
||||
@ -358,6 +349,15 @@ public class JlinkTask {
|
||||
return finder;
|
||||
}
|
||||
|
||||
|
||||
private static Path toPathLocation(ResolvedModule m) {
|
||||
Optional<URI> ouri = m.reference().location();
|
||||
if (!ouri.isPresent())
|
||||
throw new InternalError(m + " does not have a location");
|
||||
URI uri = ouri.get();
|
||||
return Paths.get(uri);
|
||||
}
|
||||
|
||||
private static ImageProvider createImageProvider(ModuleFinder finder,
|
||||
Set<String> addMods,
|
||||
Set<String> limitMods,
|
||||
@ -374,7 +374,8 @@ public class JlinkTask {
|
||||
ModuleFinder.empty(),
|
||||
addMods);
|
||||
|
||||
Map<String, Path> mods = modulesToPath(cf);
|
||||
Map<String, Path> mods = cf.modules().stream()
|
||||
.collect(Collectors.toMap(ResolvedModule::name, JlinkTask::toPathLocation));
|
||||
return new ImageHelper(cf, mods, order, retainModulesPath);
|
||||
}
|
||||
|
||||
@ -399,21 +400,15 @@ public class JlinkTask {
|
||||
map.put(mref.descriptor().name(), mref);
|
||||
});
|
||||
|
||||
// add the other modules
|
||||
otherMods.stream()
|
||||
.map(finder::find)
|
||||
.flatMap(Optional::stream)
|
||||
.forEach(mref -> map.putIfAbsent(mref.descriptor().name(), mref));
|
||||
|
||||
// set of modules that are observable
|
||||
Set<ModuleReference> mrefs = new HashSet<>(map.values());
|
||||
|
||||
// add the other modules
|
||||
for (String mod : otherMods) {
|
||||
Optional<ModuleReference> omref = finder.find(mod);
|
||||
if (omref.isPresent()) {
|
||||
ModuleReference mref = omref.get();
|
||||
map.putIfAbsent(mod, mref);
|
||||
mrefs.add(mref);
|
||||
} else {
|
||||
// no need to fail
|
||||
}
|
||||
}
|
||||
|
||||
return new ModuleFinder() {
|
||||
@Override
|
||||
public Optional<ModuleReference> find(String name) {
|
||||
|
@ -337,8 +337,8 @@ public final class TaskHelper {
|
||||
return null;
|
||||
}
|
||||
|
||||
private PluginsConfiguration getPluginsConfig(Path output,
|
||||
boolean genbom) throws IOException, BadArgs {
|
||||
private PluginsConfiguration getPluginsConfig(Path output
|
||||
) throws IOException, BadArgs {
|
||||
if (output != null) {
|
||||
if (Files.exists(output)) {
|
||||
throw new PluginException(PluginsResourceBundle.
|
||||
@ -367,7 +367,7 @@ public final class TaskHelper {
|
||||
// recreate or postprocessing don't require an output directory.
|
||||
ImageBuilder builder = null;
|
||||
if (output != null) {
|
||||
builder = new DefaultImageBuilder(genbom, output);
|
||||
builder = new DefaultImageBuilder(output);
|
||||
|
||||
}
|
||||
return new Jlink.PluginsConfiguration(pluginsList,
|
||||
@ -676,9 +676,9 @@ public final class TaskHelper {
|
||||
+ bundleHelper.getMessage(key, args));
|
||||
}
|
||||
|
||||
public PluginsConfiguration getPluginsConfig(Path output, boolean genbom)
|
||||
public PluginsConfiguration getPluginsConfig(Path output)
|
||||
throws IOException, BadArgs {
|
||||
return pluginOptions.getPluginsConfig(output, genbom);
|
||||
return pluginOptions.getPluginsConfig(output);
|
||||
}
|
||||
|
||||
public Path getExistingImage() {
|
||||
|
@ -139,7 +139,7 @@ public final class AppRuntimeImageBuilder {
|
||||
|
||||
// build the image
|
||||
Jlink.PluginsConfiguration pluginConfig = new Jlink.PluginsConfiguration(
|
||||
plugins, new DefaultImageBuilder(true, outputDir), null);
|
||||
plugins, new DefaultImageBuilder(outputDir), null);
|
||||
Jlink jlink = new Jlink();
|
||||
jlink.build(jlinkConfig, pluginConfig);
|
||||
}
|
||||
|
@ -125,7 +125,7 @@ public final class DefaultCompressPlugin implements TransformerPlugin, ResourceP
|
||||
zip = new ZipPlugin(resFilter);
|
||||
break;
|
||||
default:
|
||||
throw new PluginException("Invalid level " + level);
|
||||
throw new IllegalArgumentException("Invalid compression level " + level);
|
||||
}
|
||||
} else {
|
||||
ss = new StringSharingPlugin(resFilter);
|
||||
|
@ -208,7 +208,7 @@ public final class ExcludeVMPlugin implements TransformerPlugin {
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
throw new PluginException("Unknown option " + value);
|
||||
throw new IllegalArgumentException("Unknown exclude VM option: " + value);
|
||||
}
|
||||
}
|
||||
predicate = new ResourceFilter(Utils.listParser.apply(exclude), true);
|
||||
|
@ -164,7 +164,7 @@ public final class IncludeLocalesPlugin implements TransformerPlugin, ResourcePr
|
||||
try {
|
||||
return new Locale.LanguageRange(s);
|
||||
} catch (IllegalArgumentException iae) {
|
||||
throw new PluginException(String.format(
|
||||
throw new IllegalArgumentException(String.format(
|
||||
PluginsResourceBundle.getMessage(NAME + ".invalidtag"), s));
|
||||
}
|
||||
})
|
||||
|
@ -273,7 +273,7 @@ public final class OptimizationPlugin extends AsmPlugin {
|
||||
} else if (s.equals(FORNAME_REMOVAL)) {
|
||||
optimizers.add(new ForNameFolding());
|
||||
} else {
|
||||
throw new PluginException("Unknown optimization");
|
||||
throw new IllegalArgumentException("Unknown optimization: " + s);
|
||||
}
|
||||
}
|
||||
String f = config.get(LOG);
|
||||
|
@ -39,6 +39,8 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import jdk.internal.misc.JavaLangModuleAccess;
|
||||
import jdk.internal.misc.SharedSecrets;
|
||||
import jdk.internal.module.Checks;
|
||||
import jdk.internal.module.ModuleInfoExtender;
|
||||
import jdk.internal.module.SystemModules;
|
||||
@ -50,6 +52,7 @@ import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
||||
import jdk.tools.jlink.plugin.PluginException;
|
||||
import jdk.tools.jlink.plugin.Pool;
|
||||
import jdk.tools.jlink.plugin.TransformerPlugin;
|
||||
import jdk.tools.jlink.internal.plugins.SystemModuleDescriptorPlugin.Builder.*;
|
||||
|
||||
/**
|
||||
* Jlink plugin to reconstitute module descriptors for installed modules.
|
||||
@ -63,6 +66,8 @@ import jdk.tools.jlink.plugin.TransformerPlugin;
|
||||
* @see SystemModules
|
||||
*/
|
||||
public final class SystemModuleDescriptorPlugin implements TransformerPlugin {
|
||||
private static final JavaLangModuleAccess JLMA = SharedSecrets.getJavaLangModuleAccess();
|
||||
|
||||
// TODO: packager has the dependency on the plugin name
|
||||
// Keep it as "--installed-modules" until packager removes such
|
||||
// dependency (should not need to specify this plugin since it
|
||||
@ -118,7 +123,8 @@ public final class SystemModuleDescriptorPlugin implements TransformerPlugin {
|
||||
Pool.ModuleData data = module.get("module-info.class");
|
||||
if (data == null) {
|
||||
// automatic module not supported yet
|
||||
throw new PluginException("module-info.class not found for " + module.getName() + " module");
|
||||
throw new PluginException("module-info.class not found for " +
|
||||
module.getName() + " module");
|
||||
}
|
||||
assert module.getName().equals(data.getModule());
|
||||
try {
|
||||
@ -126,15 +132,20 @@ public final class SystemModuleDescriptorPlugin implements TransformerPlugin {
|
||||
ModuleDescriptor md = ModuleDescriptor.read(bain);
|
||||
validateNames(md);
|
||||
|
||||
Builder.ModuleDescriptorBuilder mbuilder = builder.module(md, module.getAllPackages());
|
||||
ModuleDescriptorBuilder mbuilder = builder.module(md, module.getAllPackages());
|
||||
int packages = md.exports().size() + md.conceals().size();
|
||||
if (md.conceals().isEmpty() &&
|
||||
(md.exports().size() + md.conceals().size()) != module.getAllPackages().size()) {
|
||||
packages != module.getAllPackages().size()) {
|
||||
// add ConcealedPackages attribute if not exist
|
||||
bain.reset();
|
||||
ModuleInfoRewriter minfoWriter = new ModuleInfoRewriter(bain, mbuilder.conceals());
|
||||
ModuleInfoRewriter minfoWriter =
|
||||
new ModuleInfoRewriter(bain, mbuilder.conceals());
|
||||
// replace with the overridden version
|
||||
data = new Pool.ModuleData(data.getModule(), data.getPath(), data.getType(),
|
||||
minfoWriter.stream(), minfoWriter.size());
|
||||
data = new Pool.ModuleData(data.getModule(),
|
||||
data.getPath(),
|
||||
data.getType(),
|
||||
minfoWriter.stream(),
|
||||
minfoWriter.size());
|
||||
}
|
||||
out.add(data);
|
||||
} catch (IOException e) {
|
||||
@ -151,8 +162,12 @@ public final class SystemModuleDescriptorPlugin implements TransformerPlugin {
|
||||
|
||||
if (builder.isOverriddenClass(data.getPath())) {
|
||||
byte[] bytes = cwriter.toByteArray();
|
||||
Pool.ModuleData ndata = new Pool.ModuleData(data.getModule(), data.getPath(), data.getType(),
|
||||
new ByteArrayInputStream(bytes), bytes.length);
|
||||
Pool.ModuleData ndata =
|
||||
new Pool.ModuleData(data.getModule(),
|
||||
data.getPath(),
|
||||
data.getType(),
|
||||
new ByteArrayInputStream(bytes),
|
||||
bytes.length);
|
||||
out.add(ndata);
|
||||
} else {
|
||||
out.add(data);
|
||||
@ -230,6 +245,7 @@ public final class SystemModuleDescriptorPlugin implements TransformerPlugin {
|
||||
|
||||
// static variables in SystemModules class
|
||||
private static final String MODULE_NAMES = "MODULE_NAMES";
|
||||
private static final String MODULES_TO_HASH = "MODULES_TO_HASH";
|
||||
private static final String PACKAGE_COUNT = "PACKAGES_IN_BOOT_LAYER";
|
||||
|
||||
private static final int BUILDER_VAR = 0;
|
||||
@ -246,6 +262,9 @@ public final class SystemModuleDescriptorPlugin implements TransformerPlugin {
|
||||
// list of all ModuleDescriptorBuilders, invoked in turn when building.
|
||||
private final List<ModuleDescriptorBuilder> builders = new ArrayList<>();
|
||||
|
||||
// module name to hash
|
||||
private final Map<String, String> modulesToHash = new HashMap<>();
|
||||
|
||||
// map Set<String> to a specialized builder to allow them to be
|
||||
// deduplicated as they are requested
|
||||
private final Map<Set<String>, StringSetBuilder> stringSets = new HashMap<>();
|
||||
@ -268,6 +287,11 @@ public final class SystemModuleDescriptorPlugin implements TransformerPlugin {
|
||||
"[Ljava/lang/String;", null, null)
|
||||
.visitEnd();
|
||||
|
||||
// public static String[] MODULES_TO_HASH = new String[] {....};
|
||||
cw.visitField(ACC_PUBLIC+ACC_FINAL+ACC_STATIC, MODULES_TO_HASH,
|
||||
"[Ljava/lang/String;", null, null)
|
||||
.visitEnd();
|
||||
|
||||
// public static int PACKAGES_IN_BOOT_LAYER;
|
||||
cw.visitField(ACC_PUBLIC+ACC_FINAL+ACC_STATIC, PACKAGE_COUNT,
|
||||
"I", null, numPackages)
|
||||
@ -283,15 +307,35 @@ public final class SystemModuleDescriptorPlugin implements TransformerPlugin {
|
||||
|
||||
int index = 0;
|
||||
for (ModuleDescriptorBuilder builder : builders) {
|
||||
mv.visitInsn(DUP); // arrayref
|
||||
mv.visitInsn(DUP); // arrayref
|
||||
pushInt(index++);
|
||||
mv.visitLdcInsn(builder.md.name()); // value
|
||||
mv.visitLdcInsn(builder.md.name()); // value
|
||||
mv.visitInsn(AASTORE);
|
||||
}
|
||||
|
||||
mv.visitFieldInsn(PUTSTATIC, CLASSNAME, MODULE_NAMES,
|
||||
"[Ljava/lang/String;");
|
||||
|
||||
// create the MODULES_TO_HASH array
|
||||
pushInt(numModules);
|
||||
mv.visitTypeInsn(ANEWARRAY, "java/lang/String");
|
||||
|
||||
index = 0;
|
||||
for (ModuleDescriptorBuilder builder : builders) {
|
||||
String mn = builder.md.name();
|
||||
String recordedHash = modulesToHash.get(mn);
|
||||
if (recordedHash != null) {
|
||||
mv.visitInsn(DUP); // arrayref
|
||||
pushInt(index);
|
||||
mv.visitLdcInsn(recordedHash); // value
|
||||
mv.visitInsn(AASTORE);
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
mv.visitFieldInsn(PUTSTATIC, CLASSNAME, MODULES_TO_HASH,
|
||||
"[Ljava/lang/String;");
|
||||
|
||||
mv.visitInsn(RETURN);
|
||||
mv.visitMaxs(0, 0);
|
||||
mv.visitEnd();
|
||||
@ -315,15 +359,19 @@ public final class SystemModuleDescriptorPlugin implements TransformerPlugin {
|
||||
}
|
||||
}
|
||||
|
||||
// provides
|
||||
// provides (preserve iteration order)
|
||||
for (ModuleDescriptor.Provides p : md.provides().values()) {
|
||||
stringSets.computeIfAbsent(p.providers(), s -> new StringSetBuilder(s))
|
||||
stringSets.computeIfAbsent(p.providers(), s -> new StringSetBuilder(s, true))
|
||||
.increment();
|
||||
}
|
||||
|
||||
// uses
|
||||
stringSets.computeIfAbsent(md.uses(), s -> new StringSetBuilder(s))
|
||||
.increment();
|
||||
|
||||
// hashes
|
||||
JLMA.hashes(md).ifPresent(mh -> modulesToHash.putAll(mh.hashes()));
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
@ -484,13 +532,17 @@ public final class SystemModuleDescriptorPlugin implements TransformerPlugin {
|
||||
conceals(pn);
|
||||
}
|
||||
|
||||
if (md.version().isPresent()) {
|
||||
version(md.version().get());
|
||||
}
|
||||
// version
|
||||
md.version().ifPresent(this::version);
|
||||
|
||||
if (md.mainClass().isPresent()) {
|
||||
mainClass(md.mainClass().get());
|
||||
}
|
||||
// main class
|
||||
md.mainClass().ifPresent(this::mainClass);
|
||||
|
||||
// hashes
|
||||
JLMA.hashes(md).ifPresent(mh -> {
|
||||
algorithm(mh.algorithm());
|
||||
mh.names().forEach(mn -> moduleHash(mn, mh.hashFor(mn)));
|
||||
});
|
||||
|
||||
putModuleDescriptor();
|
||||
}
|
||||
@ -603,7 +655,7 @@ public final class SystemModuleDescriptorPlugin implements TransformerPlugin {
|
||||
/*
|
||||
* Invoke Builder.provides(String service, Set<String> providers)
|
||||
*
|
||||
* Set<String> providers = new HashSet<>();
|
||||
* Set<String> providers = new LinkedHashSet<>();
|
||||
* providers.add(impl);
|
||||
* :
|
||||
* :
|
||||
@ -652,6 +704,22 @@ public final class SystemModuleDescriptorPlugin implements TransformerPlugin {
|
||||
mv.visitInsn(POP);
|
||||
}
|
||||
|
||||
void algorithm(String alg) {
|
||||
mv.visitVarInsn(ALOAD, BUILDER_VAR);
|
||||
mv.visitLdcInsn(alg);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
|
||||
"algorithm", STRING_SIG, false);
|
||||
mv.visitInsn(POP);
|
||||
}
|
||||
|
||||
void moduleHash(String name, String hashString) {
|
||||
mv.visitVarInsn(ALOAD, BUILDER_VAR);
|
||||
mv.visitLdcInsn(name);
|
||||
mv.visitLdcInsn(hashString);
|
||||
mv.visitMethodInsn(INVOKEVIRTUAL, MODULE_DESCRIPTOR_BUILDER,
|
||||
"moduleHash", STRING_STRING_SIG, false);
|
||||
mv.visitInsn(POP);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -663,10 +731,17 @@ public final class SystemModuleDescriptorPlugin implements TransformerPlugin {
|
||||
*/
|
||||
class StringSetBuilder {
|
||||
final Set<String> names;
|
||||
final boolean linked;
|
||||
int refCount;
|
||||
int localVarIndex;
|
||||
StringSetBuilder(Set<String> names) {
|
||||
|
||||
StringSetBuilder(Set<String> names, boolean linked) {
|
||||
this.names = names;
|
||||
this.linked = linked;
|
||||
}
|
||||
|
||||
StringSetBuilder(Set<String> names) {
|
||||
this(names, false);
|
||||
}
|
||||
|
||||
void increment() {
|
||||
@ -704,11 +779,11 @@ public final class SystemModuleDescriptorPlugin implements TransformerPlugin {
|
||||
"singleton", "(Ljava/lang/Object;)Ljava/util/Set;", false);
|
||||
mv.visitVarInsn(ASTORE, index);
|
||||
} else {
|
||||
mv.visitTypeInsn(NEW, "java/util/HashSet");
|
||||
String cn = linked ? "java/util/LinkedHashSet" : "java/util/HashSet";
|
||||
mv.visitTypeInsn(NEW, cn);
|
||||
mv.visitInsn(DUP);
|
||||
pushInt(initialCapacity(names.size()));
|
||||
mv.visitMethodInsn(INVOKESPECIAL, "java/util/HashSet",
|
||||
"<init>", "(I)V", false);
|
||||
mv.visitMethodInsn(INVOKESPECIAL, cn, "<init>", "(I)V", false);
|
||||
|
||||
mv.visitVarInsn(ASTORE, index);
|
||||
for (String t : names) {
|
||||
|
@ -201,6 +201,8 @@ public interface Plugin {
|
||||
* This method is called prior to invoke the plugin.
|
||||
*
|
||||
* @param config The plugin configuration.
|
||||
* @throws IllegalArgumentException if a mandatory argument is missing or
|
||||
* if an argument has invalid value.
|
||||
*/
|
||||
public default void configure(Map<String, String> config) {
|
||||
}
|
||||
@ -211,6 +213,9 @@ public interface Plugin {
|
||||
*
|
||||
* @param config The plugin configuration.
|
||||
* @param ctx The plugin context
|
||||
* @throws IllegalArgumentException if a mandatory argument is missing or
|
||||
* if an argument has invalid value.
|
||||
*
|
||||
*/
|
||||
public default void configure(Map<String, String> config, PluginContext ctx) {
|
||||
configure(config);
|
||||
|
@ -33,9 +33,6 @@ main.command.files=\
|
||||
main.opt.endian=\
|
||||
\ --endian <little|big> Byte order of generated jimage (default:native)
|
||||
|
||||
main.opt.genbom=\
|
||||
\ --genbom Generate a bom file containing jlink info
|
||||
|
||||
main.opt.saveopts=\
|
||||
\ --saveopts <filename> Save jlink options in the given file
|
||||
|
||||
|
@ -26,6 +26,7 @@
|
||||
package jdk.tools.jmod;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
@ -34,14 +35,17 @@ import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.lang.module.FindException;
|
||||
import java.lang.module.Configuration;
|
||||
import java.lang.module.ModuleReader;
|
||||
import java.lang.module.ModuleReference;
|
||||
import java.lang.module.ModuleFinder;
|
||||
import java.lang.module.ModuleDescriptor.Requires;
|
||||
import java.lang.module.ModuleDescriptor;
|
||||
import java.lang.module.ModuleDescriptor.Exports;
|
||||
import java.lang.module.ModuleDescriptor.Provides;
|
||||
import java.lang.module.ModuleDescriptor.Requires;
|
||||
import java.lang.module.ModuleDescriptor.Version;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.module.ResolutionException;
|
||||
import java.lang.module.ResolvedModule;
|
||||
import java.net.URI;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.FileVisitResult;
|
||||
@ -51,13 +55,16 @@ import java.nio.file.Path;
|
||||
import java.nio.file.PathMatcher;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.SimpleFileVisitor;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Formatter;
|
||||
import java.util.Comparator;
|
||||
import java.util.Deque;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
@ -68,6 +75,7 @@ import java.util.Optional;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.jar.JarEntry;
|
||||
@ -89,16 +97,14 @@ import jdk.internal.joptsimple.OptionParser;
|
||||
import jdk.internal.joptsimple.OptionSet;
|
||||
import jdk.internal.joptsimple.OptionSpec;
|
||||
import jdk.internal.joptsimple.ValueConverter;
|
||||
import jdk.internal.misc.JavaLangModuleAccess;
|
||||
import jdk.internal.misc.SharedSecrets;
|
||||
import jdk.internal.module.ConfigurableModuleFinder;
|
||||
import jdk.internal.module.ConfigurableModuleFinder.Phase;
|
||||
import jdk.internal.module.Hasher;
|
||||
import jdk.internal.module.Hasher.DependencyHashes;
|
||||
import jdk.internal.module.ModuleHashes;
|
||||
import jdk.internal.module.ModuleInfoExtender;
|
||||
|
||||
import static java.util.function.Function.identity;
|
||||
import static java.util.stream.Collectors.joining;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
import static java.util.stream.Collectors.toMap;
|
||||
|
||||
/**
|
||||
* Implementation for the jmod tool.
|
||||
@ -127,21 +133,6 @@ public class JmodTask {
|
||||
}
|
||||
}
|
||||
|
||||
static <T extends Throwable> void fail(Class<T> type,
|
||||
String format,
|
||||
Object... args) throws T {
|
||||
String msg = new Formatter().format(format, args).toString();
|
||||
try {
|
||||
T t = type.getConstructor(String.class).newInstance(msg);
|
||||
throw t;
|
||||
} catch (InstantiationException |
|
||||
InvocationTargetException |
|
||||
NoSuchMethodException |
|
||||
IllegalAccessException e) {
|
||||
throw new InternalError("Unable to create an instance of " + type, e);
|
||||
}
|
||||
}
|
||||
|
||||
private static final String PROGNAME = "jmod";
|
||||
private static final String MODULE_INFO = "module-info.class";
|
||||
|
||||
@ -161,7 +152,8 @@ public class JmodTask {
|
||||
enum Mode {
|
||||
CREATE,
|
||||
LIST,
|
||||
DESCRIBE
|
||||
DESCRIBE,
|
||||
HASH
|
||||
};
|
||||
|
||||
static class Options {
|
||||
@ -179,7 +171,8 @@ public class JmodTask {
|
||||
String osName;
|
||||
String osArch;
|
||||
String osVersion;
|
||||
Pattern dependenciesToHash;
|
||||
Pattern modulesToHash;
|
||||
boolean dryrun;
|
||||
List<PathMatcher> excludes;
|
||||
}
|
||||
|
||||
@ -211,6 +204,9 @@ public class JmodTask {
|
||||
case DESCRIBE:
|
||||
ok = describe();
|
||||
break;
|
||||
case HASH:
|
||||
ok = hashModules();
|
||||
break;
|
||||
default:
|
||||
throw new AssertionError("Unknown mode: " + options.mode.name());
|
||||
}
|
||||
@ -248,26 +244,8 @@ public class JmodTask {
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, Path> modulesToPath(Set<ModuleDescriptor> modules) {
|
||||
ModuleFinder finder = options.moduleFinder;
|
||||
|
||||
Map<String,Path> modPaths = new HashMap<>();
|
||||
for (ModuleDescriptor m : modules) {
|
||||
String name = m.name();
|
||||
|
||||
Optional<ModuleReference> omref = finder.find(name);
|
||||
if (!omref.isPresent()) {
|
||||
// this should not happen, module path bug?
|
||||
fail(InternalError.class,
|
||||
"Selected module %s not on module path",
|
||||
name);
|
||||
}
|
||||
|
||||
URI uri = omref.get().location().get();
|
||||
modPaths.put(name, Paths.get(uri));
|
||||
|
||||
}
|
||||
return modPaths;
|
||||
private boolean hashModules() {
|
||||
return new Hasher(options.moduleFinder).run();
|
||||
}
|
||||
|
||||
private boolean describe() throws IOException {
|
||||
@ -297,6 +275,8 @@ public class JmodTask {
|
||||
.collect(joining(" "));
|
||||
}
|
||||
|
||||
private static final JavaLangModuleAccess JLMA = SharedSecrets.getJavaLangModuleAccess();
|
||||
|
||||
private boolean printModuleDescriptor(InputStream in)
|
||||
throws IOException
|
||||
{
|
||||
@ -311,74 +291,45 @@ public class JmodTask {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("\n").append(md.toNameAndVersion());
|
||||
|
||||
List<Requires> requires = md.requires().stream().sorted().collect(toList());
|
||||
if (!requires.isEmpty()) {
|
||||
requires.forEach(r -> {
|
||||
sb.append("\n requires ");
|
||||
if (!r.modifiers().isEmpty())
|
||||
sb.append(toString(r.modifiers())).append(" ");
|
||||
sb.append(r.name());
|
||||
});
|
||||
}
|
||||
md.requires().stream()
|
||||
.sorted(Comparator.comparing(Requires::name))
|
||||
.forEach(r -> {
|
||||
sb.append("\n requires ");
|
||||
if (!r.modifiers().isEmpty())
|
||||
sb.append(toString(r.modifiers())).append(" ");
|
||||
sb.append(r.name());
|
||||
});
|
||||
|
||||
List<String> l = md.uses().stream().sorted().collect(toList());
|
||||
if (!l.isEmpty()) {
|
||||
l.forEach(sv -> sb.append("\n uses ").append(sv));
|
||||
}
|
||||
md.uses().stream().sorted()
|
||||
.forEach(s -> sb.append("\n uses ").append(s));
|
||||
|
||||
List<ModuleDescriptor.Exports> exports = sortExports(md.exports());
|
||||
if (!exports.isEmpty()) {
|
||||
exports.forEach(ex -> sb.append("\n exports ").append(ex));
|
||||
}
|
||||
md.exports().stream()
|
||||
.sorted(Comparator.comparing(Exports::source))
|
||||
.forEach(p -> sb.append("\n exports ").append(p));
|
||||
|
||||
l = md.conceals().stream().sorted().collect(toList());
|
||||
if (!l.isEmpty()) {
|
||||
l.forEach(p -> sb.append("\n conceals ").append(p));
|
||||
}
|
||||
md.conceals().stream().sorted()
|
||||
.forEach(p -> sb.append("\n conceals ").append(p));
|
||||
|
||||
Map<String, ModuleDescriptor.Provides> provides = md.provides();
|
||||
if (!provides.isEmpty()) {
|
||||
provides.values().forEach(p ->
|
||||
sb.append("\n provides ").append(p.service())
|
||||
.append(" with ")
|
||||
.append(toString(p.providers())));
|
||||
}
|
||||
md.provides().values().stream()
|
||||
.sorted(Comparator.comparing(Provides::service))
|
||||
.forEach(p -> sb.append("\n provides ").append(p.service())
|
||||
.append(" with ")
|
||||
.append(toString(p.providers())));
|
||||
|
||||
Optional<String> mc = md.mainClass();
|
||||
if (mc.isPresent())
|
||||
sb.append("\n main-class " + mc.get());
|
||||
md.mainClass().ifPresent(v -> sb.append("\n main-class " + v));
|
||||
|
||||
md.osName().ifPresent(v -> sb.append("\n operating-system-name " + v));
|
||||
|
||||
md.osArch().ifPresent(v -> sb.append("\n operating-system-architecture " + v));
|
||||
|
||||
Optional<String> osname = md.osName();
|
||||
if (osname.isPresent())
|
||||
sb.append("\n operating-system-name " + osname.get());
|
||||
md.osVersion().ifPresent(v -> sb.append("\n operating-system-version " + v));
|
||||
|
||||
Optional<String> osarch = md.osArch();
|
||||
if (osarch.isPresent())
|
||||
sb.append("\n operating-system-architecture " + osarch.get());
|
||||
JLMA.hashes(md).ifPresent(
|
||||
hashes -> hashes.names().stream().sorted().forEach(
|
||||
mod -> sb.append("\n hashes ").append(mod).append(" ")
|
||||
.append(hashes.algorithm()).append(" ")
|
||||
.append(hashes.hashFor(mod))));
|
||||
|
||||
Optional<String> osversion = md.osVersion();
|
||||
if (osversion.isPresent())
|
||||
sb.append("\n operating-system-version " + osversion.get());
|
||||
|
||||
try {
|
||||
Method m = ModuleDescriptor.class.getDeclaredMethod("hashes");
|
||||
m.setAccessible(true);
|
||||
@SuppressWarnings("unchecked")
|
||||
Optional<Hasher.DependencyHashes> optHashes =
|
||||
(Optional<Hasher.DependencyHashes>) m.invoke(md);
|
||||
|
||||
if (optHashes.isPresent()) {
|
||||
Hasher.DependencyHashes hashes = optHashes.get();
|
||||
hashes.names().stream().forEach(mod ->
|
||||
sb.append("\n hashes ").append(mod).append(" ")
|
||||
.append(hashes.algorithm()).append(" ")
|
||||
.append(hashes.hashFor(mod)));
|
||||
}
|
||||
} catch (ReflectiveOperationException x) {
|
||||
throw new InternalError(x);
|
||||
}
|
||||
out.println(sb.toString());
|
||||
return true;
|
||||
}
|
||||
@ -387,21 +338,6 @@ public class JmodTask {
|
||||
return false;
|
||||
}
|
||||
|
||||
static List<ModuleDescriptor.Exports> sortExports(Set<ModuleDescriptor.Exports> exports) {
|
||||
Map<String,ModuleDescriptor.Exports> map =
|
||||
exports.stream()
|
||||
.collect(toMap(ModuleDescriptor.Exports::source,
|
||||
identity()));
|
||||
List<String> sources = exports.stream()
|
||||
.map(ModuleDescriptor.Exports::source)
|
||||
.sorted()
|
||||
.collect(toList());
|
||||
|
||||
List<ModuleDescriptor.Exports> l = new ArrayList<>();
|
||||
sources.forEach(e -> l.add(map.get(e)));
|
||||
return l;
|
||||
}
|
||||
|
||||
private boolean create() throws IOException {
|
||||
JmodFileWriter jmod = new JmodFileWriter();
|
||||
|
||||
@ -410,8 +346,9 @@ public class JmodTask {
|
||||
Path target = options.jmodFile;
|
||||
Path tempTarget = target.resolveSibling(target.getFileName() + ".tmp");
|
||||
try {
|
||||
try (OutputStream out = Files.newOutputStream(tempTarget)) {
|
||||
jmod.write(out);
|
||||
try (OutputStream out = Files.newOutputStream(tempTarget);
|
||||
BufferedOutputStream bos = new BufferedOutputStream(out)) {
|
||||
jmod.write(bos);
|
||||
}
|
||||
Files.move(tempTarget, target);
|
||||
} catch (Exception e) {
|
||||
@ -428,7 +365,6 @@ public class JmodTask {
|
||||
}
|
||||
|
||||
private class JmodFileWriter {
|
||||
final ModuleFinder moduleFinder = options.moduleFinder;
|
||||
final List<Path> cmds = options.cmds;
|
||||
final List<Path> libs = options.libs;
|
||||
final List<Path> configs = options.configs;
|
||||
@ -438,8 +374,8 @@ public class JmodTask {
|
||||
final String osName = options.osName;
|
||||
final String osArch = options.osArch;
|
||||
final String osVersion = options.osVersion;
|
||||
final Pattern dependenciesToHash = options.dependenciesToHash;
|
||||
final List<PathMatcher> excludes = options.excludes;
|
||||
final Hasher hasher = hasher();
|
||||
|
||||
JmodFileWriter() { }
|
||||
|
||||
@ -545,11 +481,13 @@ public class JmodTask {
|
||||
if (moduleVersion != null)
|
||||
extender.version(moduleVersion);
|
||||
|
||||
// --hash-dependencies
|
||||
if (dependenciesToHash != null) {
|
||||
String name = descriptor.name();
|
||||
Set<Requires> dependences = descriptor.requires();
|
||||
extender.hashes(hashDependences(name, dependences));
|
||||
if (hasher != null) {
|
||||
ModuleHashes moduleHashes = hasher.computeHashes(descriptor.name());
|
||||
if (moduleHashes != null) {
|
||||
extender.hashes(moduleHashes);
|
||||
} else {
|
||||
warning("warn.no.module.hashes", descriptor.name());
|
||||
}
|
||||
}
|
||||
|
||||
// write the (possibly extended or modified) module-info.class
|
||||
@ -561,38 +499,56 @@ public class JmodTask {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Examines the module dependences of the given module
|
||||
* and computes the hash of any module that matches the
|
||||
* pattern {@code dependenciesToHash}.
|
||||
/*
|
||||
* Hasher resolves a module graph using the --hash-modules PATTERN
|
||||
* as the roots.
|
||||
*
|
||||
* The jmod file is being created and does not exist in the
|
||||
* given modulepath.
|
||||
*/
|
||||
DependencyHashes hashDependences(String name, Set<Requires> moduleDependences)
|
||||
throws IOException
|
||||
{
|
||||
Set<ModuleDescriptor> descriptors = new HashSet<>();
|
||||
for (Requires md: moduleDependences) {
|
||||
String dn = md.name();
|
||||
if (dependenciesToHash.matcher(dn).find()) {
|
||||
try {
|
||||
Optional<ModuleReference> omref = moduleFinder.find(dn);
|
||||
if (!omref.isPresent()) {
|
||||
throw new RuntimeException("Hashing module " + name
|
||||
+ " dependencies, unable to find module " + dn
|
||||
+ " on module path");
|
||||
}
|
||||
descriptors.add(omref.get().descriptor());
|
||||
} catch (FindException x) {
|
||||
throw new IOException("error reading module path", x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, Path> map = modulesToPath(descriptors);
|
||||
if (map.size() == 0) {
|
||||
private Hasher hasher() {
|
||||
if (options.modulesToHash == null)
|
||||
return null;
|
||||
} else {
|
||||
// use SHA-256 for now, easy to make this configurable if needed
|
||||
return Hasher.generate(map, "SHA-256");
|
||||
|
||||
try {
|
||||
Supplier<InputStream> miSupplier = newModuleInfoSupplier();
|
||||
if (miSupplier == null) {
|
||||
throw new IOException(MODULE_INFO + " not found");
|
||||
}
|
||||
|
||||
ModuleDescriptor descriptor;
|
||||
try (InputStream in = miSupplier.get()) {
|
||||
descriptor = ModuleDescriptor.read(in);
|
||||
}
|
||||
|
||||
URI uri = options.jmodFile.toUri();
|
||||
ModuleReference mref = new ModuleReference(descriptor, uri, new Supplier<>() {
|
||||
@Override
|
||||
public ModuleReader get() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
});
|
||||
|
||||
// compose a module finder with the module path and also
|
||||
// a module finder that can find the jmod file being created
|
||||
ModuleFinder finder = ModuleFinder.compose(options.moduleFinder,
|
||||
new ModuleFinder() {
|
||||
@Override
|
||||
public Optional<ModuleReference> find(String name) {
|
||||
if (descriptor.name().equals(name))
|
||||
return Optional.of(mref);
|
||||
else return Optional.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<ModuleReference> findAll() {
|
||||
return Collections.singleton(mref);
|
||||
}
|
||||
});
|
||||
|
||||
return new Hasher(finder);
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -765,6 +721,273 @@ public class JmodTask {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute and record hashes
|
||||
*/
|
||||
private class Hasher {
|
||||
final ModuleFinder moduleFinder;
|
||||
final Map<String, Path> moduleNameToPath;
|
||||
final Set<String> modules;
|
||||
final Configuration configuration;
|
||||
final boolean dryrun = options.dryrun;
|
||||
Hasher(ModuleFinder finder) {
|
||||
this.moduleFinder = finder;
|
||||
// Determine the modules that matches the pattern {@code modulesToHash}
|
||||
this.modules = moduleFinder.findAll().stream()
|
||||
.map(mref -> mref.descriptor().name())
|
||||
.filter(mn -> options.modulesToHash.matcher(mn).find())
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
// a map from a module name to Path of the packaged module
|
||||
this.moduleNameToPath = moduleFinder.findAll().stream()
|
||||
.map(mref -> mref.descriptor().name())
|
||||
.collect(Collectors.toMap(Function.identity(), mn -> moduleToPath(mn)));
|
||||
|
||||
// get a resolved module graph
|
||||
Configuration config = null;
|
||||
try {
|
||||
config = Configuration.empty()
|
||||
.resolveRequires(ModuleFinder.ofSystem(), moduleFinder, modules);
|
||||
} catch (ResolutionException e) {
|
||||
warning("warn.module.resolution.fail", e.getMessage());
|
||||
}
|
||||
this.configuration = config;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is for jmod hash command.
|
||||
*
|
||||
* Identify the base modules in the module graph, i.e. no outgoing edge
|
||||
* to any of the modules to be hashed.
|
||||
*
|
||||
* For each base module M, compute the hashes of all modules that depend
|
||||
* upon M directly or indirectly. Then update M's module-info.class
|
||||
* to record the hashes.
|
||||
*/
|
||||
boolean run() {
|
||||
if (configuration == null)
|
||||
return false;
|
||||
|
||||
// transposed graph containing the the packaged modules and
|
||||
// its transitive dependences matching --hash-modules
|
||||
Map<String, Set<String>> graph = new HashMap<>();
|
||||
for (String root : modules) {
|
||||
Deque<String> deque = new ArrayDeque<>();
|
||||
deque.add(root);
|
||||
Set<String> visited = new HashSet<>();
|
||||
while (!deque.isEmpty()) {
|
||||
String mn = deque.pop();
|
||||
if (!visited.contains(mn)) {
|
||||
visited.add(mn);
|
||||
|
||||
if (modules.contains(mn))
|
||||
graph.computeIfAbsent(mn, _k -> new HashSet<>());
|
||||
|
||||
ResolvedModule resolvedModule = configuration.findModule(mn).get();
|
||||
for (ResolvedModule dm : resolvedModule.reads()) {
|
||||
String name = dm.name();
|
||||
if (!visited.contains(name)) {
|
||||
deque.push(name);
|
||||
}
|
||||
|
||||
// reverse edge
|
||||
if (modules.contains(name) && modules.contains(mn)) {
|
||||
graph.computeIfAbsent(name, _k -> new HashSet<>()).add(mn);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (dryrun)
|
||||
out.println("Dry run:");
|
||||
|
||||
// each node in a transposed graph is a matching packaged module
|
||||
// in which the hash of the modules that depend upon it is recorded
|
||||
graph.entrySet().stream()
|
||||
.filter(e -> !e.getValue().isEmpty())
|
||||
.forEach(e -> {
|
||||
String mn = e.getKey();
|
||||
Map<String, Path> modulesForHash = e.getValue().stream()
|
||||
.collect(Collectors.toMap(Function.identity(),
|
||||
moduleNameToPath::get));
|
||||
ModuleHashes hashes = ModuleHashes.generate(modulesForHash, "SHA-256");
|
||||
if (dryrun) {
|
||||
out.format("%s%n", mn);
|
||||
hashes.names().stream()
|
||||
.sorted()
|
||||
.forEach(name -> out.format(" hashes %s %s %s%n",
|
||||
name, hashes.algorithm(), hashes.hashFor(name)));
|
||||
} else {
|
||||
try {
|
||||
updateModuleInfo(mn, hashes);
|
||||
} catch (IOException ex) {
|
||||
throw new UncheckedIOException(ex);
|
||||
}
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute hashes of the specified module.
|
||||
*
|
||||
* It records the hashing modules that depend upon the specified
|
||||
* module directly or indirectly.
|
||||
*/
|
||||
ModuleHashes computeHashes(String name) {
|
||||
if (configuration == null)
|
||||
return null;
|
||||
|
||||
// the transposed graph includes all modules in the resolved graph
|
||||
Map<String, Set<String>> graph = transpose();
|
||||
|
||||
// find the modules that transitively depend upon the specified name
|
||||
Deque<String> deque = new ArrayDeque<>();
|
||||
deque.add(name);
|
||||
Set<String> mods = visitNodes(graph, deque);
|
||||
|
||||
// filter modules matching the pattern specified --hash-modules
|
||||
// as well as itself as the jmod file is being generated
|
||||
Map<String, Path> modulesForHash = mods.stream()
|
||||
.filter(mn -> !mn.equals(name) && modules.contains(mn))
|
||||
.collect(Collectors.toMap(Function.identity(), moduleNameToPath::get));
|
||||
|
||||
if (modulesForHash.isEmpty())
|
||||
return null;
|
||||
|
||||
return ModuleHashes.generate(modulesForHash, "SHA-256");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all nodes traversed from the given roots.
|
||||
*/
|
||||
private Set<String> visitNodes(Map<String, Set<String>> graph,
|
||||
Deque<String> roots) {
|
||||
Set<String> visited = new HashSet<>();
|
||||
while (!roots.isEmpty()) {
|
||||
String mn = roots.pop();
|
||||
if (!visited.contains(mn)) {
|
||||
visited.add(mn);
|
||||
// the given roots may not be part of the graph
|
||||
if (graph.containsKey(mn)) {
|
||||
for (String dm : graph.get(mn)) {
|
||||
if (!visited.contains(dm)) {
|
||||
roots.push(dm);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return visited;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a transposed graph from the resolved module graph.
|
||||
*/
|
||||
private Map<String, Set<String>> transpose() {
|
||||
Map<String, Set<String>> transposedGraph = new HashMap<>();
|
||||
Deque<String> deque = new ArrayDeque<>(modules);
|
||||
|
||||
Set<String> visited = new HashSet<>();
|
||||
while (!deque.isEmpty()) {
|
||||
String mn = deque.pop();
|
||||
if (!visited.contains(mn)) {
|
||||
visited.add(mn);
|
||||
|
||||
transposedGraph.computeIfAbsent(mn, _k -> new HashSet<>());
|
||||
|
||||
ResolvedModule resolvedModule = configuration.findModule(mn).get();
|
||||
for (ResolvedModule dm : resolvedModule.reads()) {
|
||||
String name = dm.name();
|
||||
if (!visited.contains(name)) {
|
||||
deque.push(name);
|
||||
}
|
||||
|
||||
// reverse edge
|
||||
transposedGraph.computeIfAbsent(name, _k -> new HashSet<>())
|
||||
.add(mn);
|
||||
}
|
||||
}
|
||||
}
|
||||
return transposedGraph;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the given input stream of module-info.class and write
|
||||
* the extended module-info.class with the given ModuleHashes
|
||||
*
|
||||
* @param in InputStream of module-info.class
|
||||
* @param out OutputStream to write the extended module-info.class
|
||||
* @param hashes ModuleHashes
|
||||
*/
|
||||
private void recordHashes(InputStream in, OutputStream out, ModuleHashes hashes)
|
||||
throws IOException
|
||||
{
|
||||
ModuleInfoExtender extender = ModuleInfoExtender.newExtender(in);
|
||||
extender.hashes(hashes);
|
||||
extender.write(out);
|
||||
}
|
||||
|
||||
private void updateModuleInfo(String name, ModuleHashes moduleHashes)
|
||||
throws IOException
|
||||
{
|
||||
Path target = moduleNameToPath.get(name);
|
||||
Path tempTarget = target.resolveSibling(target.getFileName() + ".tmp");
|
||||
ZipFile zip = new ZipFile(target.toFile());
|
||||
try {
|
||||
try (OutputStream out = Files.newOutputStream(tempTarget);
|
||||
ZipOutputStream zos = new ZipOutputStream(out)) {
|
||||
zip.stream().forEach(e -> {
|
||||
try {
|
||||
InputStream in = zip.getInputStream(e);
|
||||
if (e.getName().equals(MODULE_INFO) ||
|
||||
e.getName().equals(Section.CLASSES.jmodDir() + "/" + MODULE_INFO)) {
|
||||
ZipEntry ze = new ZipEntry(e.getName());
|
||||
ze.setTime(System.currentTimeMillis());
|
||||
zos.putNextEntry(ze);
|
||||
recordHashes(in, zos, moduleHashes);
|
||||
zos.closeEntry();
|
||||
} else {
|
||||
zos.putNextEntry(e);
|
||||
zos.write(in.readAllBytes());
|
||||
zos.closeEntry();
|
||||
}
|
||||
} catch (IOException x) {
|
||||
throw new UncheckedIOException(x);
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (IOException|RuntimeException e) {
|
||||
if (Files.exists(tempTarget)) {
|
||||
try {
|
||||
Files.delete(tempTarget);
|
||||
} catch (IOException ioe) {
|
||||
e.addSuppressed(ioe);
|
||||
}
|
||||
}
|
||||
throw e;
|
||||
} finally {
|
||||
zip.close();
|
||||
}
|
||||
out.println(getMessage("module.hashes.recorded", name));
|
||||
Files.move(tempTarget, target, StandardCopyOption.REPLACE_EXISTING);
|
||||
}
|
||||
|
||||
private Path moduleToPath(String name) {
|
||||
ModuleReference mref = moduleFinder.find(name).orElseThrow(
|
||||
() -> new InternalError("Selected module " + name + " not on module path"));
|
||||
|
||||
URI uri = mref.location().get();
|
||||
Path path = Paths.get(uri);
|
||||
String fn = path.getFileName().toString();
|
||||
if (!fn.endsWith(".jar") && !fn.endsWith(".jmod")) {
|
||||
throw new InternalError(path + " is not a modular JAR or jmod file");
|
||||
}
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
enum Section {
|
||||
NATIVE_LIBS("native"),
|
||||
NATIVE_CMDS("bin"),
|
||||
@ -921,7 +1144,8 @@ public class JmodTask {
|
||||
builder.append("\n").append(" Main operation modes:\n ");
|
||||
builder.append(getMessage("main.opt.mode.create")).append("\n ");
|
||||
builder.append(getMessage("main.opt.mode.list")).append("\n ");
|
||||
builder.append(getMessage("main.opt.mode.describe")).append("\n\n");
|
||||
builder.append(getMessage("main.opt.mode.describe")).append("\n ");
|
||||
builder.append(getMessage("main.opt.mode.hash")).append("\n\n");
|
||||
|
||||
String cmdfile = null;
|
||||
String[] lines = content.split("\n");
|
||||
@ -964,13 +1188,16 @@ public class JmodTask {
|
||||
.withValuesSeparatedBy(File.pathSeparatorChar)
|
||||
.withValuesConvertedBy(DirPathConverter.INSTANCE);
|
||||
|
||||
OptionSpec<Void> dryrun
|
||||
= parser.accepts("dry-run", getMessage("main.opt.dry-run"));
|
||||
|
||||
OptionSpec<PathMatcher> excludes
|
||||
= parser.accepts("exclude", getMessage("main.opt.exclude"))
|
||||
.withRequiredArg()
|
||||
.withValuesConvertedBy(new GlobConverter());
|
||||
|
||||
OptionSpec<Pattern> hashDependencies
|
||||
= parser.accepts("hash-dependencies", getMessage("main.opt.hash-dependencies"))
|
||||
OptionSpec<Pattern> hashModules
|
||||
= parser.accepts("hash-modules", getMessage("main.opt.hash-modules"))
|
||||
.withRequiredArg()
|
||||
.withValuesConvertedBy(new PatternConverter());
|
||||
|
||||
@ -1049,6 +1276,8 @@ public class JmodTask {
|
||||
options.cmds = opts.valuesOf(cmds);
|
||||
if (opts.has(config))
|
||||
options.configs = opts.valuesOf(config);
|
||||
if (opts.has(dryrun))
|
||||
options.dryrun = true;
|
||||
if (opts.has(excludes))
|
||||
options.excludes = opts.valuesOf(excludes);
|
||||
if (opts.has(libs))
|
||||
@ -1069,27 +1298,39 @@ public class JmodTask {
|
||||
options.osArch = opts.valueOf(osArch);
|
||||
if (opts.has(osVersion))
|
||||
options.osVersion = opts.valueOf(osVersion);
|
||||
if (opts.has(hashDependencies)) {
|
||||
options.dependenciesToHash = opts.valueOf(hashDependencies);
|
||||
// if storing hashes of dependencies then the module path is required
|
||||
if (opts.has(hashModules)) {
|
||||
options.modulesToHash = opts.valueOf(hashModules);
|
||||
// if storing hashes then the module path is required
|
||||
if (options.moduleFinder == null)
|
||||
throw new CommandException("err.modulepath.must.be.specified").showUsage(true);
|
||||
throw new CommandException("err.modulepath.must.be.specified")
|
||||
.showUsage(true);
|
||||
}
|
||||
|
||||
if (words.size() <= 1)
|
||||
throw new CommandException("err.jmod.must.be.specified").showUsage(true);
|
||||
Path path = Paths.get(words.get(1));
|
||||
if (options.mode.equals(Mode.CREATE) && Files.exists(path))
|
||||
throw new CommandException("err.file.already.exists", path);
|
||||
else if ((options.mode.equals(Mode.LIST) ||
|
||||
options.mode.equals(Mode.DESCRIBE))
|
||||
&& Files.notExists(path))
|
||||
throw new CommandException("err.jmod.not.found", path);
|
||||
options.jmodFile = path;
|
||||
if (options.mode.equals(Mode.HASH)) {
|
||||
if (options.moduleFinder == null || options.modulesToHash == null)
|
||||
throw new CommandException("err.modulepath.must.be.specified")
|
||||
.showUsage(true);
|
||||
} else {
|
||||
if (words.size() <= 1)
|
||||
throw new CommandException("err.jmod.must.be.specified").showUsage(true);
|
||||
Path path = Paths.get(words.get(1));
|
||||
|
||||
if (words.size() > 2)
|
||||
throw new CommandException("err.unknown.option",
|
||||
words.subList(2, words.size())).showUsage(true);
|
||||
if (options.mode.equals(Mode.CREATE) && Files.exists(path))
|
||||
throw new CommandException("err.file.already.exists", path);
|
||||
else if ((options.mode.equals(Mode.LIST) ||
|
||||
options.mode.equals(Mode.DESCRIBE))
|
||||
&& Files.notExists(path))
|
||||
throw new CommandException("err.jmod.not.found", path);
|
||||
|
||||
if (options.dryrun) {
|
||||
throw new CommandException("err.invalid.dryrun.option");
|
||||
}
|
||||
options.jmodFile = path;
|
||||
|
||||
if (words.size() > 2)
|
||||
throw new CommandException("err.unknown.option",
|
||||
words.subList(2, words.size())).showUsage(true);
|
||||
}
|
||||
|
||||
if (options.mode.equals(Mode.CREATE) && options.classpath == null)
|
||||
throw new CommandException("err.classpath.must.be.specified").showUsage(true);
|
||||
|
@ -1,9 +1,9 @@
|
||||
main.usage.summary=\
|
||||
Usage: {0} (create|list|describe) <OPTIONS> <jmod-file>\n\
|
||||
Usage: {0} (create|list|describe|hash) <OPTIONS> <jmod-file>\n\
|
||||
use --help for a list of possible options
|
||||
|
||||
main.usage=\
|
||||
Usage: {0} (create|list|describe) <OPTIONS> <jmod-file>
|
||||
Usage: {0} (create|list|describe|hash) <OPTIONS> <jmod-file>
|
||||
|
||||
error.prefix=Error:
|
||||
warn.prefix=Warning:
|
||||
@ -14,6 +14,8 @@ main.opt.mode.list=\
|
||||
\list - Prints the names of all the entries
|
||||
main.opt.mode.describe=\
|
||||
\describe - Prints the module details
|
||||
main.opt.mode.hash=\
|
||||
\hash - Records hashes of tied modules.
|
||||
|
||||
main.opt.help=Print this usage message
|
||||
main.opt.version=Version information
|
||||
@ -21,9 +23,9 @@ main.opt.class-path=Application jar files|dir containing classes
|
||||
main.opt.libs=Location of native libraries
|
||||
main.opt.cmds=Location of native commands
|
||||
main.opt.config=Location of user-editable config files
|
||||
main.opt.dry-run=Dry run of hash mode
|
||||
main.opt.exclude=Exclude files, given as a PATTERN
|
||||
main.opt.module-version= Module version
|
||||
main.opt.modulepath=Module path
|
||||
main.opt.main-class=Main class
|
||||
main.opt.main-class.arg=class-name
|
||||
main.opt.os-name=Operating system name
|
||||
@ -32,18 +34,25 @@ main.opt.os-arch=Operating system architecture
|
||||
main.opt.os-arch.arg=os-arch
|
||||
main.opt.os-version=Operating system version
|
||||
main.opt.os-version.arg=os-version
|
||||
main.opt.hash-dependencies=Compute and record hashes of dependencies matched by the pattern
|
||||
main.opt.modulepath=Module path
|
||||
main.opt.hash-modules=Compute and record hashes to tie a packaged module\
|
||||
\ with modules matching the given pattern and depending upon it directly\
|
||||
\ or indirectly. The hashes are recorded in the JMOD file being created, or\
|
||||
\ a JMOD file or modular JAR on the module path specified the jmod hash command.
|
||||
|
||||
main.opt.cmdfile=Read options from the specified file
|
||||
|
||||
err.missing.mode=one of create, list, or describe must be specified
|
||||
err.invalid.mode=mode must be one of create, list, or describe: {0}
|
||||
module.hashes.recorded=Hashes are recorded in module {0}
|
||||
|
||||
err.missing.mode=one of create, list, describe, or hash must be specified
|
||||
err.invalid.mode=mode must be one of create, list, describe, or hash: {0}
|
||||
err.classpath.must.be.specified=--class-path must be specified
|
||||
err.jmod.must.be.specified=jmod-file must be specified
|
||||
err.invalid.version=invalid module version {0}
|
||||
err.output.must.be.specified:--output must be specified
|
||||
err.mods.must.be.specified:--mods must be specified
|
||||
err.modulepath.must.be.specified:--module-path must be specified when hashing dependencies
|
||||
err.invalid.main-class:invalid main-class name: {0}
|
||||
err.output.must.be.specified=--output must be specified
|
||||
err.mods.must.be.specified=--mods must be specified
|
||||
err.modulepath.must.be.specified=--module-path must be specified when hashing modules
|
||||
err.invalid.main-class=invalid main-class name: {0}
|
||||
err.path.not.found=path not found: {0}
|
||||
err.path.not.valid=invalid path: {0}
|
||||
err.path.not.a.dir=path must be a directory: {0}
|
||||
@ -54,5 +63,9 @@ err.bad.pattern=bad pattern {0}
|
||||
err.unknown.option=unknown option(s): {0}
|
||||
err.missing.arg=no value given for {0}
|
||||
err.internal.error=internal error: {0} {1} {2}
|
||||
err.invalid.dryrun.option=--dry-run can only be used with hash mode
|
||||
err.module.descriptor.not.found=Module descriptor not found
|
||||
warn.invalid.arg=Invalid classname or pathname not exist: {0}
|
||||
warn.no.module.hashes=No hashes recorded: no module specified for hashing depends on {0}
|
||||
warn.module.resolution.fail=No hashes recorded: {0}
|
||||
|
||||
|
@ -26,9 +26,12 @@ groups=TEST.groups [closed/TEST.groups]
|
||||
# Allow querying of sun.arch.data.model in @requires clauses
|
||||
requires.properties=sun.arch.data.model
|
||||
|
||||
# Tests using jtreg 4.2 b01 features
|
||||
requiredVersion=4.2 b01
|
||||
# Tests using jtreg 4.2 b02 features
|
||||
requiredVersion=4.2 b02
|
||||
|
||||
# Path to libraries in the topmost test directory. This is needed so @library
|
||||
# does not need ../../ notation to reach them
|
||||
external.lib.roots = ../../
|
||||
|
||||
# Use new form of -Xpatch
|
||||
useNewXpatch=true
|
||||
|
@ -81,9 +81,9 @@ cp ${TESTSRC}${FS}JavaBug.java bug
|
||||
|
||||
chmod -fR 777 bug
|
||||
|
||||
${COMPILEJAVA}${FS}bin${FS}javac -d . bug${FS}*.java
|
||||
${COMPILEJAVA}${FS}bin${FS}javac -addmods java.corba -d . bug${FS}*.java
|
||||
|
||||
${TESTJAVA}${FS}bin${FS}java ${TESTVMOPTS} -cp . bug/JavaBug > test.out 2>&1
|
||||
${TESTJAVA}${FS}bin${FS}java ${TESTVMOPTS} -addmods java.corba -cp . bug/JavaBug > test.out 2>&1
|
||||
|
||||
grep "NullPointerException" test.out
|
||||
|
||||
|
@ -27,7 +27,8 @@
|
||||
* @summary Four helper classes missing in Sun JDK
|
||||
* @library /lib/testlibrary
|
||||
* @build jdk.testlibrary.*
|
||||
* @run main CorbaExceptionsCompileTest
|
||||
* @compile -addmods java.corba CorbaExceptionsCompileTest.java
|
||||
* @run main/othervm -addmods java.corba CorbaExceptionsCompileTest
|
||||
*/
|
||||
|
||||
import java.io.*;
|
||||
|
@ -25,6 +25,8 @@
|
||||
* @test
|
||||
* @bug 7095856
|
||||
* @summary OutputStreamHook doesn't handle null values
|
||||
* @compile -addmods java.corba HookPutFieldsTest.java
|
||||
* @run main/othervm -addmods java.corba HookPutFieldsTest
|
||||
*/
|
||||
|
||||
import java.net.InetAddress;
|
||||
|
@ -25,7 +25,8 @@
|
||||
* @test
|
||||
* @bug 8028215
|
||||
* @summary SetDefaultORBTest setting ORB impl via properties test
|
||||
* @run main/othervm SetDefaultORBTest
|
||||
* @compile -addmods java.corba SetDefaultORBTest.java
|
||||
* @run main/othervm -addmods java.corba SetDefaultORBTest
|
||||
*
|
||||
*/
|
||||
|
||||
|
@ -29,7 +29,6 @@
|
||||
|
||||
import java.net.*;
|
||||
import java.io.*;
|
||||
import javax.xml.soap.*;
|
||||
import java.util.*;
|
||||
import com.sun.net.httpserver.*;
|
||||
import java.util.concurrent.*;
|
||||
|
@ -37,7 +37,7 @@ import com.oracle.testlibrary.jsr292.CodeCacheOverflowProcessor;
|
||||
* @library /lib/testlibrary /lib/testlibrary/jsr292
|
||||
* @compile/module=java.base java/lang/invoke/MethodHandleHelper.java
|
||||
* @run main/bootclasspath VarargsArrayTest
|
||||
* @run main/bootclasspath -DVarargsArrayTest.MAX_ARITY=255 -DVarargsArrayTest.START_ARITY=250
|
||||
* @run main/bootclasspath/othervm -DVarargsArrayTest.MAX_ARITY=255 -DVarargsArrayTest.START_ARITY=250
|
||||
* VarargsArrayTest
|
||||
*/
|
||||
|
||||
|
@ -340,7 +340,7 @@ public class ModuleFinderTest {
|
||||
*/
|
||||
public void testOfWithUnrecognizedEntry() throws Exception {
|
||||
Path dir = Files.createTempDirectory(USER_DIR, "mods");
|
||||
Path mod = Files.createTempFile(dir, "m", "mod");
|
||||
Path mod = Files.createTempFile(dir, "m", ".junk");
|
||||
|
||||
ModuleFinder finder = ModuleFinder.of(mod);
|
||||
try {
|
||||
@ -360,6 +360,48 @@ public class ModuleFinderTest {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test ModuleFinder.of with a file path to a directory containing a file
|
||||
* that will not be recognized as a module.
|
||||
*/
|
||||
public void testOfWithUnrecognizedEntryInDirectory() throws Exception {
|
||||
Path dir = Files.createTempDirectory(USER_DIR, "mods");
|
||||
Files.createTempFile(dir, "m", ".junk");
|
||||
|
||||
ModuleFinder finder = ModuleFinder.of(dir);
|
||||
try {
|
||||
finder.find("java.rhubarb");
|
||||
assertTrue(false);
|
||||
} catch (FindException e) {
|
||||
// expected
|
||||
}
|
||||
|
||||
finder = ModuleFinder.of(dir);
|
||||
try {
|
||||
finder.findAll();
|
||||
assertTrue(false);
|
||||
} catch (FindException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test ModuleFinder.of with a file path to a directory containing a file
|
||||
* starting with ".", the file should be ignored.
|
||||
*/
|
||||
public void testOfWithHiddenEntryInDirectory() throws Exception {
|
||||
Path dir = Files.createTempDirectory(USER_DIR, "mods");
|
||||
Files.createTempFile(dir, ".marker", "");
|
||||
|
||||
ModuleFinder finder = ModuleFinder.of(dir);
|
||||
assertFalse(finder.find("java.rhubarb").isPresent());
|
||||
|
||||
finder = ModuleFinder.of(dir);
|
||||
assertTrue(finder.findAll().isEmpty());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test ModuleFinder.of with a directory that contains two
|
||||
* versions of the same module
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 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
|
||||
@ -21,12 +21,15 @@
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package app;
|
||||
|
||||
import jdk.lib.Util;
|
||||
|
||||
public class Main {
|
||||
/**
|
||||
* @test
|
||||
* @bug 8888888
|
||||
* @summary Functional test for WeakPairMap
|
||||
* @build java.base/java.lang.reflect.WeakPairMapTest
|
||||
* @run main Driver
|
||||
*/
|
||||
public class Driver {
|
||||
public static void main(String[] args) {
|
||||
Object obj = Util.makeObject();
|
||||
java.lang.reflect.WeakPairMapTest.main(args);
|
||||
}
|
||||
}
|
@ -0,0 +1,175 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 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.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package java.lang.reflect;
|
||||
|
||||
import java.lang.ref.Reference;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Functional test for WeakPairMap
|
||||
*
|
||||
* @author Peter Levart
|
||||
*/
|
||||
public class WeakPairMapTest {
|
||||
public static void main(String[] args) {
|
||||
WeakPairMap<Object, Object, String> pm = new WeakPairMap<>();
|
||||
Object key1 = new Object();
|
||||
Object key2 = new Object();
|
||||
|
||||
// check for emptiness
|
||||
assertEquals(pm.containsKeyPair(key1, key2), false);
|
||||
assertEquals(pm.get(key1, key2), null);
|
||||
|
||||
// check for NPE(s)
|
||||
for (Object k1 : new Object[]{null, key1}) {
|
||||
for (Object k2 : new Object[]{null, key2}) {
|
||||
for (String v : new String[]{null, "abc"}) {
|
||||
|
||||
if (k1 != null && k2 != null && v != null) {
|
||||
// skip non-null args
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
pm.put(k1, k2, v);
|
||||
throw new AssertionError("Unexpected code path, k1=" +
|
||||
k1 + ", k2=" + k2 + ", v=" + v);
|
||||
} catch (NullPointerException e) {
|
||||
// expected
|
||||
}
|
||||
|
||||
try {
|
||||
pm.putIfAbsent(k1, k2, v);
|
||||
throw new AssertionError("Unexpected code path, k1=" +
|
||||
k1 + ", k2=" + k2 + ", v=" + v);
|
||||
} catch (NullPointerException e) {
|
||||
// expected
|
||||
}
|
||||
|
||||
if (k1 != null && k2 != null) {
|
||||
// skip non-null args
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
pm.computeIfAbsent(k1, k2, (_k1, _k2) -> v);
|
||||
throw new AssertionError("Unexpected code path, k1=" +
|
||||
k1 + ", k2=" + k2 + ", v=" + v);
|
||||
} catch (NullPointerException e) {
|
||||
// expected
|
||||
}
|
||||
|
||||
try {
|
||||
pm.containsKeyPair(k1, k2);
|
||||
throw new AssertionError("Unexpected code path, k1=" +
|
||||
k1 + ", k2=" + k2);
|
||||
} catch (NullPointerException e) {
|
||||
// expected
|
||||
}
|
||||
|
||||
try {
|
||||
pm.get(k1, k2);
|
||||
throw new AssertionError("Unexpected code path, k1=" +
|
||||
k1 + ", k2=" + k2);
|
||||
} catch (NullPointerException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// how much to wait when it is expected for entry to be retained
|
||||
final long retentionTimeout = 500L;
|
||||
// how much to wait when it is expected for entry to be removed
|
||||
final long cleanupTimeout = 30_000L;
|
||||
|
||||
// check insertion
|
||||
assertEquals(pm.putIfAbsent(key1, key2, "abc"), null);
|
||||
assertEquals(pm.get(key1, key2), "abc");
|
||||
|
||||
// check retention while both keys are still reachable
|
||||
assertEquals(gcAndWaitRemoved(pm, "abc", retentionTimeout), false);
|
||||
assertEquals(pm.get(key1, key2), "abc");
|
||||
|
||||
// check cleanup when both keys are unreachable
|
||||
key1 = null;
|
||||
key2 = null;
|
||||
assertEquals(gcAndWaitRemoved(pm, "abc", cleanupTimeout), true);
|
||||
|
||||
// new insertion
|
||||
key1 = new Object();
|
||||
key2 = new Object();
|
||||
assertEquals(pm.putIfAbsent(key1, key2, "abc"), null);
|
||||
assertEquals(pm.get(key1, key2), "abc");
|
||||
|
||||
// check retention while both keys are still reachable
|
||||
assertEquals(gcAndWaitRemoved(pm, "abc", retentionTimeout), false);
|
||||
assertEquals(pm.get(key1, key2), "abc");
|
||||
|
||||
// check cleanup when 1st key is unreachable
|
||||
key1 = null;
|
||||
assertEquals(gcAndWaitRemoved(pm, "abc", cleanupTimeout), true);
|
||||
Reference.reachabilityFence(key2);
|
||||
|
||||
// new insertion
|
||||
key1 = new Object();
|
||||
key2 = new Object();
|
||||
assertEquals(pm.putIfAbsent(key1, key2, "abc"), null);
|
||||
assertEquals(pm.get(key1, key2), "abc");
|
||||
|
||||
// check retention while both keys are still reachable
|
||||
assertEquals(gcAndWaitRemoved(pm, "abc", retentionTimeout), false);
|
||||
assertEquals(pm.get(key1, key2), "abc");
|
||||
|
||||
// check cleanup when 2nd key is unreachable
|
||||
key2 = null;
|
||||
assertEquals(gcAndWaitRemoved(pm, "abc", cleanupTimeout), true);
|
||||
Reference.reachabilityFence(key1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger GC and wait for at most {@code millis} ms for given value to
|
||||
* be removed from given WeakPairMap.
|
||||
*
|
||||
* @return true if element has been removed or false if not
|
||||
*/
|
||||
static <V> boolean gcAndWaitRemoved(WeakPairMap<?, ?, V> pm, V value,
|
||||
long millis) {
|
||||
System.gc();
|
||||
for (int i = 0; i < (millis + 99) / 100 && pm.values().contains(value); i++) {
|
||||
try {
|
||||
Thread.sleep(100L);
|
||||
} catch (InterruptedException e) {
|
||||
throw new AssertionError("Interrupted");
|
||||
}
|
||||
}
|
||||
return !pm.values().contains(value);
|
||||
}
|
||||
|
||||
static void assertEquals(Object actual, Object expected) {
|
||||
if (!Objects.equals(actual, expected)) {
|
||||
throw new AssertionError("Expected: " + expected + ", actual: " + actual);
|
||||
}
|
||||
}
|
||||
}
|
@ -68,7 +68,7 @@ cd ${PATCHDIR}/java.desktop
|
||||
${TESTJAVA}/bin/jar xf ${TESTSRC}/awtres.jar
|
||||
|
||||
echo
|
||||
${TESTJAVA}/bin/java ${TESTVMOPTS} -Xpatch:${PATCHDIR} \
|
||||
${TESTJAVA}/bin/java ${TESTVMOPTS} -Xpatch:java.desktop=${PATCHDIR}/java.desktop \
|
||||
-cp ${TESTCLASSES} Bug6299235Test
|
||||
|
||||
if [ $? -ne 0 ]
|
||||
|
@ -27,6 +27,8 @@
|
||||
* @summary Make sure Cipher IO streams doesn't call extra doFinal if close()
|
||||
* is called multiple times. Additionally, verify the input and output streams
|
||||
* match with encryption and decryption with non-stream crypto.
|
||||
* @compile -addmods java.xml.bind CipherStreamClose.java
|
||||
* @run main/othervm -addmods java.xml.bind CipherStreamClose
|
||||
*/
|
||||
|
||||
import java.io.*;
|
||||
|
@ -27,8 +27,10 @@
|
||||
* @summary test RMI-IIOP call with ConcurrentHashMap as an argument
|
||||
* @library /lib/testlibrary
|
||||
* @build jdk.testlibrary.*
|
||||
* @build Test HelloInterface HelloServer HelloClient HelloImpl _HelloImpl_Tie _HelloInterface_Stub ConcurrentHashMapTest
|
||||
* @run main/othervm -Djava.naming.provider.url=iiop://localhost:1050 -Djava.naming.factory.initial=com.sun.jndi.cosnaming.CNCtxFactory ConcurrentHashMapTest
|
||||
* @compile -addmods java.corba Test.java HelloInterface.java HelloServer.java HelloClient.java
|
||||
* HelloImpl.java _HelloImpl_Tie.java _HelloInterface_Stub.java ConcurrentHashMapTest.java
|
||||
* @run main/othervm -addmods java.corba -Djava.naming.provider.url=iiop://localhost:1050
|
||||
* -Djava.naming.factory.initial=com.sun.jndi.cosnaming.CNCtxFactory ConcurrentHashMapTest
|
||||
* @key intermittent
|
||||
*/
|
||||
|
||||
@ -101,6 +103,8 @@ public class ConcurrentHashMapTest {
|
||||
// -Djava.naming.provider.url=iiop://localhost:1050 HelloServer
|
||||
List<String> commands = new ArrayList<>();
|
||||
commands.add(ConcurrentHashMapTest.JAVA);
|
||||
commands.add("-addmods");
|
||||
commands.add("java.corba");
|
||||
commands.add("-Djava.naming.factory.initial=com.sun.jndi.cosnaming.CNCtxFactory");
|
||||
commands.add("-Djava.naming.provider.url=iiop://localhost:1050");
|
||||
commands.add("-cp");
|
||||
|
@ -26,7 +26,8 @@
|
||||
* @bug 8049021
|
||||
* @summary Test different constructors for CommandAPDU and check CLA,INS,NC,NE,
|
||||
* P1,and P2
|
||||
* @run testng CommandAPDUTest
|
||||
* @compile -addmods java.smartcardio CommandAPDUTest.java
|
||||
* @run testng/othervm -addmods java.smartcardio CommandAPDUTest
|
||||
*/
|
||||
import java.nio.ByteBuffer;
|
||||
import javax.smartcardio.CommandAPDU;
|
||||
|
@ -26,7 +26,9 @@
|
||||
* @bug 6445367
|
||||
* @summary Verify that ATR.getHistoricalBytes() works
|
||||
* @author Andreas Sterbenz
|
||||
**/
|
||||
* @compile -addmods java.smartcardio HistoricalBytes.java
|
||||
* @run main/othervm -addmods java.smartcardio HistoricalBytes
|
||||
*/
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
|
@ -25,7 +25,8 @@
|
||||
* @test
|
||||
* @bug 8049021
|
||||
* @summary Construct ResponseAPDU from byte array and check NR< SW, SW1 and SW2
|
||||
* @run testng ResponseAPDUTest
|
||||
* @compile -addmods java.smartcardio ResponseAPDUTest.java
|
||||
* @run testng/othervm -addmods java.smartcardio ResponseAPDUTest
|
||||
*/
|
||||
import javax.smartcardio.ResponseAPDU;
|
||||
import static org.testng.Assert.*;
|
||||
|
@ -26,6 +26,8 @@
|
||||
* @bug 6445367
|
||||
* @summary make sure serialization works
|
||||
* @author Andreas Sterbenz
|
||||
* @compile -addmods java.smartcardio Serialize.java
|
||||
* @run main/othervm -addmods java.smartcardio Serialize
|
||||
*/
|
||||
|
||||
import java.io.*;
|
||||
|
@ -25,7 +25,8 @@
|
||||
* @test
|
||||
* @bug 8049021
|
||||
* @summary Test if we can write new provider for smart card
|
||||
* @run main/othervm/policy=policy TerminalFactorySpiTest
|
||||
* @compile -addmods java.smartcardio TerminalFactorySpiTest.java
|
||||
* @run main/othervm/policy=policy -addmods java.smartcardio TerminalFactorySpiTest
|
||||
*/
|
||||
import java.security.Provider;
|
||||
import java.security.Security;
|
||||
|
@ -26,6 +26,8 @@
|
||||
* @bug 6293767
|
||||
* @summary Test for the CardPermission class
|
||||
* @author Andreas Sterbenz
|
||||
* @compile -addmods java.smartcardio TestCardPermission.java
|
||||
* @run main/othervm -addmods java.smartcardio TestCardPermission
|
||||
*/
|
||||
|
||||
import javax.smartcardio.*;
|
||||
|
@ -27,6 +27,8 @@
|
||||
* @summary Test for the CommandAPDU class
|
||||
* @author Andreas Sterbenz
|
||||
* @key randomness
|
||||
* @compile -addmods java.smartcardio TestCommandAPDU.java
|
||||
* @run main/othervm -addmods java.smartcardio TestCommandAPDU
|
||||
*/
|
||||
|
||||
import java.util.*;
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 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
|
||||
@ -21,12 +21,16 @@
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package org.not_matched;
|
||||
|
||||
public class Name {
|
||||
private Name() { }
|
||||
|
||||
public static String name() {
|
||||
return "new_module";
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @test
|
||||
* @compile -addmods java.transaction
|
||||
* test/transaction/InvalidTransactionExceptionTests.java
|
||||
* test/transaction/TransactionRequiredExceptionTests.java
|
||||
* test/transaction/TransactionRolledbackExceptionTests.java
|
||||
* test/transaction/XAExceptionTests.java
|
||||
* util/SerializedTransactionExceptions.java
|
||||
* @run testng/othervm -addmods java.transaction test.transaction.InvalidTransactionExceptionTests
|
||||
* @run testng/othervm -addmods java.transaction test.transaction.TransactionRequiredExceptionTests
|
||||
* @run testng/othervm -addmods java.transaction test.transaction.TransactionRolledbackExceptionTests
|
||||
* @run testng/othervm -addmods java.transaction util.SerializedTransactionExceptions
|
||||
*/
|
@ -1,4 +0,0 @@
|
||||
# JDBC unit tests uses TestNG
|
||||
TestNG.dirs= .
|
||||
othervm.dirs= .
|
||||
|
@ -26,6 +26,7 @@
|
||||
* @bug 8032884
|
||||
* @summary Globalbindings optionalProperty="primitive" does not work when minOccurs=0
|
||||
* @run shell compile-schema.sh
|
||||
* @compile -addmods java.xml.bind XjcOptionalPropertyTest.java
|
||||
* @run main/othervm XjcOptionalPropertyTest
|
||||
*/
|
||||
|
||||
|
@ -34,12 +34,12 @@ $COMPILEJAVA/bin/javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} \
|
||||
-d compile/java.xml -Xmodule:java.xml $TESTSRC/Document.java $TESTSRC/Node.java || exit 1
|
||||
|
||||
$COMPILEJAVA/bin/javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} \
|
||||
-d exec/java.xml -Xpatch:compile -Xmodule:java.xml $TESTSRC/DocumentImpl.java || exit 2
|
||||
-d exec/java.xml -Xpatch:java.xml=compile/java.xml -Xmodule:java.xml $TESTSRC/DocumentImpl.java || exit 2
|
||||
|
||||
$COMPILEJAVA/bin/javac ${TESTJAVACOPTS} ${TESTTOOLVMOPTS} \
|
||||
$TESTSRC/AbstractMethodErrorTest.java -d exec || exit 3
|
||||
|
||||
$TESTJAVA/bin/java ${TESTVMOPTS} -Xpatch:exec -cp exec AbstractMethodErrorTest || exit 4
|
||||
$TESTJAVA/bin/java ${TESTVMOPTS} -Xpatch:java.xml=exec -cp exec AbstractMethodErrorTest || exit 4
|
||||
|
||||
exit 0
|
||||
|
||||
|
@ -38,6 +38,8 @@ import java.util.Iterator;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @compile -addmods java.xml.ws XmlTest.java
|
||||
* @run main/othervm -addmods java.xml.ws XmlTest
|
||||
* @summary tests JAF DataHandler can be instantiated; test serialize and
|
||||
* deserialize SOAP message containing xml attachment
|
||||
*/
|
||||
|
@ -49,29 +49,29 @@ import java.nio.file.StandardOpenOption;
|
||||
* run main/othervm SAAJFactoryTest saaj.factory.Valid -
|
||||
* scenario14 javax.xml.soap.MessageFactory=saaj.factory.Valid saaj.factory.Valid2 -
|
||||
*
|
||||
* @build saaj.factory.*
|
||||
* @compile -addmods java.xml.ws saaj/factory/Invalid.java saaj/factory/Valid.java
|
||||
* saaj/factory/Valid2.java saaj/factory/Valid3.java SAAJFactoryTest.java
|
||||
*
|
||||
* @run main/othervm SAAJFactoryTest com.sun.xml.internal.messaging.saaj.soap.ver1_1.SOAPMessageFactory1_1Impl -
|
||||
* scenario2 - -
|
||||
* @run main/othervm -Djavax.xml.soap.MessageFactory=saaj.factory.Valid SAAJFactoryTest saaj.factory.Valid -
|
||||
* scenario5 - -
|
||||
* @run main/othervm -Djavax.xml.soap.MessageFactory=saaj.factory.NonExisting SAAJFactoryTest
|
||||
* - javax.xml.soap.SOAPException
|
||||
* scenario6 - -
|
||||
* @run main/othervm -Djavax.xml.soap.MessageFactory=saaj.factory.Invalid SAAJFactoryTest - javax.xml.soap.SOAPException
|
||||
* scenario7 - -
|
||||
* @run main/othervm SAAJFactoryTest saaj.factory.Valid -
|
||||
* scenario8 - saaj.factory.Valid
|
||||
* @run main/othervm SAAJFactoryTest saaj.factory.Valid -
|
||||
* scenario9 - saaj.factory.Valid
|
||||
* @run main/othervm SAAJFactoryTest - javax.xml.soap.SOAPException
|
||||
* scenario10 - saaj.factory.NonExisting
|
||||
* @run main/othervm SAAJFactoryTest - javax.xml.soap.SOAPException
|
||||
* scenario11 - saaj.factory.Invalid
|
||||
* @run main/othervm SAAJFactoryTest com.sun.xml.internal.messaging.saaj.soap.ver1_1.SOAPMessageFactory1_1Impl -
|
||||
* scenario12 - -
|
||||
* @run main/othervm SAAJFactoryTest saaj.factory.Valid -
|
||||
* scenario15 - saaj.factory.Valid
|
||||
* @run main/othervm -addmods java.xml.ws
|
||||
* SAAJFactoryTest com.sun.xml.internal.messaging.saaj.soap.ver1_1.SOAPMessageFactory1_1Impl - scenario2 - -
|
||||
* @run main/othervm -addmods java.xml.ws -Djavax.xml.soap.MessageFactory=saaj.factory.Valid
|
||||
* SAAJFactoryTest saaj.factory.Valid - scenario5 - -
|
||||
* @run main/othervm -addmods java.xml.ws -Djavax.xml.soap.MessageFactory=saaj.factory.NonExisting
|
||||
* SAAJFactoryTest - javax.xml.soap.SOAPException scenario6 - -
|
||||
* @run main/othervm -addmods java.xml.ws -Djavax.xml.soap.MessageFactory=saaj.factory.Invalid
|
||||
* SAAJFactoryTest - javax.xml.soap.SOAPException scenario7 - -
|
||||
* @run main/othervm -addmods java.xml.ws
|
||||
* SAAJFactoryTest saaj.factory.Valid - scenario8 - saaj.factory.Valid
|
||||
* @run main/othervm -addmods java.xml.ws
|
||||
* SAAJFactoryTest saaj.factory.Valid - scenario9 - saaj.factory.Valid
|
||||
* @run main/othervm -addmods java.xml.ws
|
||||
* SAAJFactoryTest - javax.xml.soap.SOAPException scenario10 - saaj.factory.NonExisting
|
||||
* @run main/othervm -addmods java.xml.ws
|
||||
* SAAJFactoryTest - javax.xml.soap.SOAPException scenario11 - saaj.factory.Invalid scenario11 - saaj.factory.Invalid
|
||||
* @run main/othervm -addmods java.xml.ws
|
||||
* SAAJFactoryTest com.sun.xml.internal.messaging.saaj.soap.ver1_1.SOAPMessageFactory1_1Impl - scenario12 - -
|
||||
* @run main/othervm -addmods java.xml.ws
|
||||
* SAAJFactoryTest saaj.factory.Valid - scenario15 - saaj.factory.Valid
|
||||
*/
|
||||
public class SAAJFactoryTest {
|
||||
|
||||
|
@ -27,8 +27,8 @@
|
||||
* @summary JAF initialisation in SAAJ clashing with the one in javax.mail
|
||||
* @author mkos
|
||||
* @library javax.mail.jar
|
||||
* @build MailTest
|
||||
* @run main MailTest
|
||||
* @compile -addmods java.xml.ws MailTest.java
|
||||
* @run main/othervm -addmods java.xml.ws MailTest
|
||||
*/
|
||||
|
||||
import javax.activation.CommandMap;
|
||||
|
@ -25,7 +25,8 @@
|
||||
* @test
|
||||
* @bug 8016271 8026405
|
||||
* @summary wsimport -clientjar does not create portable jar on windows due to hardcoded '\'
|
||||
* @run main/othervm TestWsImport
|
||||
* @compile -addmods java.xml.ws TestWsImport.java
|
||||
* @run main/othervm -addmods java.xml.ws TestWsImport
|
||||
*/
|
||||
|
||||
import javax.xml.namespace.QName;
|
||||
|
@ -25,7 +25,8 @@
|
||||
* @test
|
||||
* @bug 8146086
|
||||
* @summary Publishing two webservices on same port fails with "java.net.BindException: Address already in use"
|
||||
* @run main/othervm WSTest
|
||||
* @compile -addmods java.xml.ws WSTest.java
|
||||
* @run main/othervm -addmods java.xml.ws WSTest
|
||||
*/
|
||||
import javax.jws.WebMethod;
|
||||
import javax.jws.WebService;
|
||||
|
@ -27,7 +27,8 @@
|
||||
* @summary the content of xs:any content:mixed should remain as is,
|
||||
* no white space changes and no changes to namespace prefixes
|
||||
* @run shell compile-wsdl.sh
|
||||
* @run main/othervm Test
|
||||
* @compile -addmods java.xml.ws Test.java
|
||||
* @run main/othervm -addmods java.xml.ws Test
|
||||
*/
|
||||
|
||||
import com.sun.net.httpserver.HttpServer;
|
||||
|
@ -26,7 +26,6 @@ import java.util.*;
|
||||
import java.security.*;
|
||||
import javax.crypto.*;
|
||||
import javax.crypto.spec.*;
|
||||
import javax.xml.bind.DatatypeConverter;
|
||||
|
||||
public class SecretKeysBasic extends PKCS11Test {
|
||||
|
||||
@ -131,8 +130,11 @@ public class SecretKeysBasic extends PKCS11Test {
|
||||
System.out.println(info + "> " + key);
|
||||
System.out.println("\tALGO=" + key.getAlgorithm());
|
||||
if (key.getFormat() != null) {
|
||||
System.out.println("\t[" + key.getFormat() + "] VALUE=" +
|
||||
DatatypeConverter.printHexBinary(key.getEncoded()));
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (byte b : key.getEncoded()) {
|
||||
sb.append(String.format("%02x", b & 0xff));
|
||||
}
|
||||
System.out.println("\t[" + key.getFormat() + "] VALUE=" + sb);
|
||||
} else {
|
||||
System.out.println("\tVALUE=n/a");
|
||||
}
|
||||
|
@ -25,7 +25,9 @@
|
||||
* @test
|
||||
* @bug 8047771
|
||||
* @summary check permissions and principals from various modules
|
||||
* @run main/othervm/java.security.policy==modules.policy Modules
|
||||
* @compile -addmods java.xml.ws,java.smartcardio Modules.java
|
||||
* @run main/othervm/java.security.policy==modules.policy
|
||||
* -addmods java.xml.ws,java.smartcardio Modules
|
||||
*/
|
||||
|
||||
import java.security.AccessController;
|
||||
|
@ -340,43 +340,43 @@ public class Basic {
|
||||
"--file=" + modularJar.toString())
|
||||
.assertSuccess()
|
||||
.resultChecker(r -> {
|
||||
// Expect similar output: "Name:bar, Requires: foo,...
|
||||
// Conceals: jdk.test.foo, jdk.test.foo.internal"
|
||||
Pattern p = Pattern.compile("\\s+Name:\\s+bar\\s+Requires:\\s+foo");
|
||||
// Expect similar output: "bar, requires mandated foo, ...
|
||||
// conceals jdk.test.foo, conceals jdk.test.foo.internal"
|
||||
Pattern p = Pattern.compile("\\s+bar\\s+requires\\s++foo");
|
||||
assertTrue(p.matcher(r.output).find(),
|
||||
"Expecting to find \"Name: bar, Requires: foo,...\"",
|
||||
"Expecting to find \"bar, requires foo,...\"",
|
||||
"in output, but did not: [" + r.output + "]");
|
||||
p = Pattern.compile(
|
||||
"Conceals:\\s+jdk.test.foo\\s+jdk.test.foo.internal");
|
||||
"conceals\\s+jdk.test.foo\\s+conceals\\s+jdk.test.foo.internal");
|
||||
assertTrue(p.matcher(r.output).find(),
|
||||
"Expecting to find \"Conceals: jdk.test.foo,...\"",
|
||||
"Expecting to find \"conceals jdk.test.foo,...\"",
|
||||
"in output, but did not: [" + r.output + "]");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dependencesFooBar() throws IOException {
|
||||
public void hashBarInFooModule() throws IOException {
|
||||
Path mp = Paths.get("dependencesFooBar");
|
||||
createTestDir(mp);
|
||||
|
||||
Path modClasses = MODULE_CLASSES.resolve(FOO.moduleName);
|
||||
Path modularJar = mp.resolve(FOO.moduleName + ".jar");
|
||||
jar("--create",
|
||||
"--file=" + modularJar.toString(),
|
||||
"--main-class=" + FOO.mainClass,
|
||||
"--module-version=" + FOO.version,
|
||||
"--no-manifest",
|
||||
"-C", modClasses.toString(), ".")
|
||||
.assertSuccess();
|
||||
|
||||
modClasses = MODULE_CLASSES.resolve(BAR.moduleName);
|
||||
modularJar = mp.resolve(BAR.moduleName + ".jar");
|
||||
Path modClasses = MODULE_CLASSES.resolve(BAR.moduleName);
|
||||
Path modularJar = mp.resolve(BAR.moduleName + ".jar");
|
||||
jar("--create",
|
||||
"--file=" + modularJar.toString(),
|
||||
"--main-class=" + BAR.mainClass,
|
||||
"--module-version=" + BAR.version,
|
||||
"--no-manifest",
|
||||
"-C", modClasses.toString(), ".")
|
||||
.assertSuccess();
|
||||
|
||||
modClasses = MODULE_CLASSES.resolve(FOO.moduleName);
|
||||
modularJar = mp.resolve(FOO.moduleName + ".jar");
|
||||
jar("--create",
|
||||
"--file=" + modularJar.toString(),
|
||||
"--main-class=" + FOO.mainClass,
|
||||
"--module-version=" + FOO.version,
|
||||
"--modulepath=" + mp.toString(),
|
||||
"--hash-dependencies=" + "foo", // dependency on foo
|
||||
"--hash-modules=" + "bar",
|
||||
"--no-manifest",
|
||||
"-C", modClasses.toString(), ".")
|
||||
.assertSuccess();
|
||||
@ -392,49 +392,49 @@ public class Basic {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void badDependencyFooBar() throws IOException {
|
||||
public void invalidHashInFooModule() throws IOException {
|
||||
Path mp = Paths.get("badDependencyFooBar");
|
||||
createTestDir(mp);
|
||||
|
||||
Path fooClasses = MODULE_CLASSES.resolve(FOO.moduleName);
|
||||
Path fooJar = mp.resolve(FOO.moduleName + ".jar");
|
||||
jar("--create",
|
||||
"--file=" + fooJar.toString(),
|
||||
"--main-class=" + FOO.mainClass,
|
||||
"--module-version=" + FOO.version,
|
||||
"--no-manifest",
|
||||
"-C", fooClasses.toString(), ".").assertSuccess();
|
||||
|
||||
Path barClasses = MODULE_CLASSES.resolve(BAR.moduleName);
|
||||
Path barJar = mp.resolve(BAR.moduleName + ".jar");
|
||||
jar("--create",
|
||||
"--file=" + barJar.toString(),
|
||||
"--main-class=" + BAR.mainClass,
|
||||
"--module-version=" + BAR.version,
|
||||
"--modulepath=" + mp.toString(),
|
||||
"--hash-dependencies=" + "foo", // dependency on foo
|
||||
"--no-manifest",
|
||||
"-C", barClasses.toString(), ".").assertSuccess();
|
||||
|
||||
// Rebuild foo.jar with a change that will cause its hash to be different
|
||||
FileUtils.deleteFileWithRetry(fooJar);
|
||||
Path fooClasses = MODULE_CLASSES.resolve(FOO.moduleName);
|
||||
Path fooJar = mp.resolve(FOO.moduleName + ".jar");
|
||||
jar("--create",
|
||||
"--file=" + fooJar.toString(),
|
||||
"--main-class=" + FOO.mainClass,
|
||||
"--module-version=" + FOO.version + ".1", // a newer version
|
||||
"--module-version=" + FOO.version,
|
||||
"--modulepath=" + mp.toString(),
|
||||
"--hash-modules=" + "bar",
|
||||
"--no-manifest",
|
||||
"-C", fooClasses.toString(), ".").assertSuccess();
|
||||
|
||||
// Rebuild bar.jar with a change that will cause its hash to be different
|
||||
FileUtils.deleteFileWithRetry(barJar);
|
||||
jar("--create",
|
||||
"--file=" + barJar.toString(),
|
||||
"--main-class=" + BAR.mainClass,
|
||||
"--module-version=" + BAR.version + ".1", // a newer version
|
||||
"--no-manifest",
|
||||
"-C", barClasses.toString(), ".").assertSuccess();
|
||||
|
||||
java(mp, BAR.moduleName + "/" + BAR.mainClass,
|
||||
"-XaddExports:java.base/jdk.internal.module=bar")
|
||||
.assertFailure()
|
||||
.resultChecker(r -> {
|
||||
// Expect similar output: "java.lang.module.ResolutionException: Hash
|
||||
// of foo (WdktSIQSkd4+CEacpOZoeDrCosMATNrIuNub9b5yBeo=) differs to
|
||||
// of bar (WdktSIQSkd4+CEacpOZoeDrCosMATNrIuNub9b5yBeo=) differs to
|
||||
// expected hash (iepvdv8xTeVrFgMtUhcFnmetSub6qQHCHc92lSaSEg0=)"
|
||||
Pattern p = Pattern.compile(".*Hash of foo.*differs to expected hash.*");
|
||||
Pattern p = Pattern.compile(".*Hash of bar.*differs to expected hash.*");
|
||||
assertTrue(p.matcher(r.output).find(),
|
||||
"Expecting error message containing \"Hash of foo ... differs to"
|
||||
"Expecting error message containing \"Hash of bar ... differs to"
|
||||
+ " expected hash...\" but got: [", r.output + "]");
|
||||
});
|
||||
}
|
||||
@ -454,7 +454,7 @@ public class Basic {
|
||||
|
||||
jar("--create",
|
||||
"--file=" + modularJar.toString(),
|
||||
"--hash-dependencies=" + ".*", // no module-info.class
|
||||
"--hash-modules=" + ".*", // no module-info.class
|
||||
"-C", modClasses.toString(), "jdk")
|
||||
.assertFailure(); // TODO: expected failure message
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ import java.lang.reflect.Method;
|
||||
import java.util.Optional;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
import jdk.internal.module.Hasher;
|
||||
import jdk.internal.module.ModuleHashes;
|
||||
import jdk.test.bar.internal.Message;
|
||||
|
||||
public class Bar {
|
||||
@ -43,10 +43,11 @@ public class Bar {
|
||||
|
||||
Method m = ModuleDescriptor.class.getDeclaredMethod("hashes");
|
||||
m.setAccessible(true);
|
||||
Optional<Hasher.DependencyHashes> optHashes =
|
||||
(Optional<Hasher.DependencyHashes>) m.invoke(md);
|
||||
ModuleDescriptor foo = jdk.test.foo.Foo.class.getModule().getDescriptor();
|
||||
Optional<ModuleHashes> oHashes =
|
||||
(Optional<ModuleHashes>) m.invoke(foo);
|
||||
|
||||
System.out.println("hashes:" + optHashes.get().hashFor("foo"));
|
||||
System.out.println("hashes:" + oHashes.get().hashFor("bar"));
|
||||
|
||||
StringJoiner sj = new StringJoiner(",");
|
||||
md.conceals().forEach(sj::add);
|
||||
|
@ -214,14 +214,12 @@ public class ImageFileCreatorTest {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void storeFiles(Pool content, String bom) {
|
||||
|
||||
public void storeFiles(Pool content) {
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
ImagePluginStack stack = new ImagePluginStack(noopBuilder, Collections.emptyList(),
|
||||
null, Collections.emptyList(), "");
|
||||
null, Collections.emptyList());
|
||||
|
||||
ImageFileCreator.create(archives, ByteOrder.nativeOrder(), stack);
|
||||
}
|
||||
|
@ -241,7 +241,7 @@ public class IntegrationTest {
|
||||
lst.add(new MyPostProcessor());
|
||||
}
|
||||
// Image builder
|
||||
DefaultImageBuilder builder = new DefaultImageBuilder(true, output);
|
||||
DefaultImageBuilder builder = new DefaultImageBuilder(output);
|
||||
PluginsConfiguration plugins
|
||||
= new Jlink.PluginsConfiguration(lst, builder, null);
|
||||
|
||||
@ -254,10 +254,6 @@ public class IntegrationTest {
|
||||
if (!jimage.exists()) {
|
||||
throw new AssertionError("jimage not generated");
|
||||
}
|
||||
File bom = new File(output.toString(), "bom");
|
||||
if (!bom.exists()) {
|
||||
throw new AssertionError("bom not generated");
|
||||
}
|
||||
File release = new File(output.toString(), "release");
|
||||
if (!release.exists()) {
|
||||
throw new AssertionError("release not generated");
|
||||
@ -311,7 +307,7 @@ public class IntegrationTest {
|
||||
}
|
||||
|
||||
// Image builder
|
||||
DefaultImageBuilder builder = new DefaultImageBuilder(false, output);
|
||||
DefaultImageBuilder builder = new DefaultImageBuilder(output);
|
||||
PluginsConfiguration plugins
|
||||
= new Jlink.PluginsConfiguration(lst, builder, null);
|
||||
|
||||
@ -359,7 +355,7 @@ public class IntegrationTest {
|
||||
}
|
||||
|
||||
// Image builder
|
||||
DefaultImageBuilder builder = new DefaultImageBuilder(false, output);
|
||||
DefaultImageBuilder builder = new DefaultImageBuilder(output);
|
||||
PluginsConfiguration plugins
|
||||
= new Jlink.PluginsConfiguration(lst, builder, null);
|
||||
boolean failed = false;
|
||||
|
@ -66,8 +66,6 @@ public class JLink2Test {
|
||||
|
||||
// This test case must be first one, the JlinkTask is clean
|
||||
// and reveals possible bug related to plugin options in defaults
|
||||
// e. g.: --genbom
|
||||
testBomFile(helper);
|
||||
testSameNames(helper);
|
||||
testModulePath(helper);
|
||||
testOptions();
|
||||
@ -136,35 +134,6 @@ public class JLink2Test {
|
||||
validator.validate();
|
||||
}
|
||||
|
||||
private static void testBomFile(Helper helper) throws Exception {
|
||||
String[] userOptions = {
|
||||
"--compress",
|
||||
"2",
|
||||
"--addmods",
|
||||
"bomzip",
|
||||
"--strip-debug",
|
||||
"--genbom",
|
||||
"--exclude-resources",
|
||||
"*.jcov,*/META-INF/*"};
|
||||
String moduleName = "bomzip";
|
||||
helper.generateDefaultJModule(moduleName, "composite2");
|
||||
Path imgDir = helper.generateDefaultImage(userOptions, moduleName).assertSuccess();
|
||||
helper.checkImage(imgDir, moduleName, userOptions, null, null);
|
||||
File bom = new File(imgDir.toFile(), "bom");
|
||||
if (!bom.exists()) {
|
||||
throw new RuntimeException(bom.getAbsolutePath() + " not generated");
|
||||
}
|
||||
String bomcontent = new String(Files.readAllBytes(bom.toPath()));
|
||||
if (!bomcontent.contains("--strip-debug")
|
||||
|| !bomcontent.contains("--compress")
|
||||
|| !bomcontent.contains("--genbom")
|
||||
|| !bomcontent.contains("--exclude-resources *.jcov,"
|
||||
+ "*/META-INF/*")
|
||||
|| !bomcontent.contains("--addmods bomzip")) {
|
||||
throw new Exception("Not expected content in " + bom);
|
||||
}
|
||||
}
|
||||
|
||||
private static void testOptions() throws Exception {
|
||||
List<Plugin> builtInPlugins = new ArrayList<>();
|
||||
builtInPlugins.addAll(PluginRepository.getPlugins(Layer.boot()));
|
||||
|
@ -204,7 +204,7 @@ public class JLinkTest {
|
||||
String[] userOptions = {"--compress", "invalid"};
|
||||
String moduleName = "invalidCompressLevel";
|
||||
helper.generateDefaultJModule(moduleName, "composite2");
|
||||
helper.generateDefaultImage(userOptions, moduleName).assertFailure("Error: Invalid level invalid");
|
||||
helper.generateDefaultImage(userOptions, moduleName).assertFailure("Error: Invalid compression level invalid");
|
||||
}
|
||||
|
||||
// @file
|
||||
|
@ -1,160 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2015, 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.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary Test the recording and checking of dependency hashes
|
||||
* @author Andrei Eremeev
|
||||
* @library /lib/testlibrary
|
||||
* @modules java.base/jdk.internal.module
|
||||
* jdk.jlink/jdk.tools.jlink
|
||||
* jdk.jlink/jdk.tools.jmod
|
||||
* jdk.compiler
|
||||
* @ignore
|
||||
* @build jdk.testlibrary.ProcessTools jdk.testlibrary.OutputAnalyzer CompilerUtils
|
||||
* @run main HashesTest
|
||||
*/
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.file.FileVisitResult;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.SimpleFileVisitor;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import jdk.testlibrary.OutputAnalyzer;
|
||||
import jdk.testlibrary.ProcessTools;
|
||||
|
||||
public class HashesTest {
|
||||
|
||||
private final Path jdkHome = Paths.get(System.getProperty("test.jdk"));
|
||||
private final Path stdJmods = jdkHome.resolve("jmods");
|
||||
private final Path testSrc = Paths.get(System.getProperty("test.src"));
|
||||
private final Path modSrc = testSrc.resolve("src");
|
||||
private final Path newModSrc = testSrc.resolve("newsrc");
|
||||
private final Path classes = Paths.get("classes");
|
||||
private final Path jmods = Paths.get("jmods");
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
new HashesTest().run();
|
||||
}
|
||||
|
||||
private void run() throws Exception {
|
||||
if (!Files.exists(stdJmods)) {
|
||||
return;
|
||||
}
|
||||
Files.createDirectories(jmods);
|
||||
Path m1Classes = classes.resolve("m1");
|
||||
Path m2Classes = classes.resolve("m2");
|
||||
Path m3Classes = classes.resolve("not_matched");
|
||||
// build the second module
|
||||
compileClasses(modSrc, m2Classes);
|
||||
runJmod(m2Classes.toString(), m2Classes.getFileName().toString());
|
||||
|
||||
// build the third module
|
||||
compileClasses(modSrc, m3Classes);
|
||||
runJmod(m3Classes.toString(), m3Classes.getFileName().toString());
|
||||
|
||||
compileClasses(modSrc, m1Classes, "-mp", jmods.toString());
|
||||
runJmod(m1Classes.toString(), m1Classes.getFileName().toString(),
|
||||
"--modulepath", jmods.toString(), "--hash-dependencies", "m2");
|
||||
runJava(0, "-mp", jmods.toString(), "-m", "m1/org.m1.Main");
|
||||
|
||||
deleteDirectory(m3Classes);
|
||||
Files.delete(jmods.resolve("not_matched.jmod"));
|
||||
|
||||
// build the new third module
|
||||
compileClasses(newModSrc, m3Classes);
|
||||
runJmod(m3Classes.toString(), m3Classes.getFileName().toString());
|
||||
runJava(0, "-mp", jmods.toString(), "-m", "m1/org.m1.Main");
|
||||
|
||||
deleteDirectory(m2Classes);
|
||||
Files.delete(jmods.resolve("m2.jmod"));
|
||||
|
||||
compileClasses(newModSrc, m2Classes);
|
||||
runJmod(m2Classes.toString(), m2Classes.getFileName().toString());
|
||||
|
||||
runJava(1, "-mp", jmods.toString(), "-m", "m1/org.m1.Main");
|
||||
|
||||
if (jdk.tools.jlink.internal.Main.run(new String[]{
|
||||
"--modulepath", stdJmods.toString() + File.pathSeparator + jmods.toString(),
|
||||
"--addmods", "m1", "--output", "myimage"}, new PrintWriter(System.out)) == 0) {
|
||||
throw new AssertionError("Expected failure. rc = 0");
|
||||
}
|
||||
}
|
||||
|
||||
private void deleteDirectory(Path dir) throws IOException {
|
||||
Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
||||
Files.delete(file);
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
|
||||
Files.delete(dir);
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void runJava(int expectedExitCode, String... args) throws Exception {
|
||||
OutputAnalyzer analyzer = ProcessTools.executeTestJava(args);
|
||||
if (analyzer.getExitValue() != expectedExitCode) {
|
||||
throw new AssertionError("Expected exit code: " + expectedExitCode +
|
||||
", got: " + analyzer.getExitValue());
|
||||
}
|
||||
}
|
||||
|
||||
private void compileClasses(Path src, Path output, String... options) throws IOException {
|
||||
List<String> args = new ArrayList<>();
|
||||
Collections.addAll(args, options);
|
||||
Collections.addAll(args, "-d", output.toString());
|
||||
args.add(src.toString());
|
||||
System.out.println("javac options: " + args.stream().collect(Collectors.joining(" ")));
|
||||
if (!CompilerUtils.compile(src.resolve(output.getFileName()), output, options)) {
|
||||
throw new AssertionError("Compilation failure. See log.");
|
||||
}
|
||||
}
|
||||
|
||||
private void runJmod(String cp, String modName, String... options) {
|
||||
List<String> args = new ArrayList<>();
|
||||
args.add("create");
|
||||
Collections.addAll(args, options);
|
||||
Collections.addAll(args, "--class-path", cp,
|
||||
jmods + File.separator + modName + ".jmod");
|
||||
int rc = jdk.tools.jmod.Main.run(args.toArray(new String[args.size()]), System.out);
|
||||
System.out.println("jmod options: " + args.stream().collect(Collectors.joining(" ")));
|
||||
if (rc != 0) {
|
||||
throw new AssertionError("Jmod failed: rc = " + rc);
|
||||
}
|
||||
}
|
||||
}
|
@ -101,9 +101,8 @@ public class FileCopierPluginTest {
|
||||
|
||||
}
|
||||
Path root = new File(".").toPath();
|
||||
DefaultImageBuilder imgbuilder = new DefaultImageBuilder(false,
|
||||
root);
|
||||
imgbuilder.storeFiles(pool, "");
|
||||
DefaultImageBuilder imgbuilder = new DefaultImageBuilder(root);
|
||||
imgbuilder.storeFiles(pool);
|
||||
|
||||
if (lic.exists()) {
|
||||
File license = new File(root.toFile(), "LICENSE");
|
||||
|
@ -76,7 +76,7 @@ public class JmodNegativeTest {
|
||||
jmod()
|
||||
.assertFailure()
|
||||
.resultChecker(r ->
|
||||
assertContains(r.output, "Error: one of create, list, or describe must be specified")
|
||||
assertContains(r.output, "Error: one of create, list, describe, or hash must be specified")
|
||||
);
|
||||
}
|
||||
|
||||
@ -85,7 +85,7 @@ public class JmodNegativeTest {
|
||||
jmod("badAction")
|
||||
.assertFailure()
|
||||
.resultChecker(r ->
|
||||
assertContains(r.output, "Error: mode must be one of create, list, or describe")
|
||||
assertContains(r.output, "Error: mode must be one of create, list, describe, or hash")
|
||||
);
|
||||
|
||||
jmod("--badOption")
|
||||
@ -170,14 +170,14 @@ public class JmodNegativeTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHashDependenciesModulePathNotSpecified() {
|
||||
public void testHashModulesModulePathNotSpecified() {
|
||||
jmod("create",
|
||||
"--hash-dependencies", "anyPattern.*",
|
||||
"--hash-modules", "anyPattern.*",
|
||||
"output.jmod")
|
||||
.assertFailure()
|
||||
.resultChecker(r ->
|
||||
assertContains(r.output, "Error: --module-path must be "
|
||||
+"specified when hashing dependencies")
|
||||
+"specified when hashing modules")
|
||||
);
|
||||
}
|
||||
|
||||
@ -317,7 +317,7 @@ public class JmodNegativeTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDependencyNotFound() throws IOException {
|
||||
public void testNoModuleHash() throws IOException {
|
||||
Path jmod = MODS_DIR.resolve("output.jmod");
|
||||
FileUtils.deleteFileIfExistsWithRetry(jmod);
|
||||
Path emptyDir = Paths.get("empty");
|
||||
@ -328,13 +328,12 @@ public class JmodNegativeTest {
|
||||
|
||||
jmod("create",
|
||||
"--class-path", cp,
|
||||
"--hash-dependencies", ".*",
|
||||
"--hash-modules", ".*",
|
||||
"--modulepath", emptyDir.toString(),
|
||||
jmod.toString())
|
||||
.assertFailure()
|
||||
.resultChecker(r ->
|
||||
assertContains(r.output, "Hashing module foo dependencies, "
|
||||
+ "unable to find module java.base on module path")
|
||||
assertContains(r.output, "No hashes recorded: " +
|
||||
"no module specified for hashing depends on foo")
|
||||
);
|
||||
}
|
||||
|
||||
@ -350,13 +349,10 @@ public class JmodNegativeTest {
|
||||
|
||||
jmod("create",
|
||||
"--class-path", cp,
|
||||
"--hash-dependencies", ".*",
|
||||
"--hash-modules", ".*",
|
||||
"--modulepath", MODS_DIR.toString(),
|
||||
jmod.toString())
|
||||
.assertFailure()
|
||||
.resultChecker(r ->
|
||||
assertContains(r.output, "Error: error reading module path")
|
||||
);
|
||||
.assertFailure();
|
||||
} finally {
|
||||
FileUtils.deleteFileWithRetry(empty);
|
||||
}
|
||||
@ -371,7 +367,7 @@ public class JmodNegativeTest {
|
||||
Files.createFile(file);
|
||||
|
||||
jmod("create",
|
||||
"--hash-dependencies", ".*",
|
||||
"--hash-modules", ".*",
|
||||
"--modulepath", file.toString(),
|
||||
jmod.toString())
|
||||
.assertFailure()
|
||||
@ -388,7 +384,7 @@ public class JmodNegativeTest {
|
||||
|
||||
List<Supplier<JmodResult>> tasks = Arrays.asList(
|
||||
() -> jmod("create",
|
||||
"--hash-dependencies", "anyPattern",
|
||||
"--hash-modules", "anyPattern",
|
||||
"--modulepath", "doesNotExist",
|
||||
"output.jmod"),
|
||||
() -> jmod("create",
|
||||
@ -436,7 +432,7 @@ public class JmodNegativeTest {
|
||||
|
||||
List<Supplier<JmodResult>> tasks = Arrays.asList(
|
||||
() -> jmod("create",
|
||||
"--hash-dependencies", "anyPattern",
|
||||
"--hash-modules", "anyPattern",
|
||||
"--modulepath","empty" + pathSeparator + "doesNotExist",
|
||||
"output.jmod"),
|
||||
() -> jmod("create",
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user