# HG changeset patch # User alanb # Date 1462272356 -3600 # Node ID 06f3783b338fe711ccaebdb44a9e3bb47faa3122 # Parent 82b8d12a553f5617737c238cec060281d52e351c# Parent 7c84df693837df959cdfc0470ce7f90e21d26f7b Merge diff -r 82b8d12a553f -r 06f3783b338f jdk/make/Tools.gmk --- a/jdk/make/Tools.gmk Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/make/Tools.gmk Tue May 03 11:45:56 2016 +0100 @@ -96,7 +96,13 @@ 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 \ diff -r 82b8d12a553f -r 06f3783b338f jdk/make/gendata/GendataBreakIterator.gmk --- a/jdk/make/gendata/GendataBreakIterator.gmk Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/make/gendata/GendataBreakIterator.gmk Tue May 03 11:45:56 2016 +0100 @@ -62,10 +62,13 @@ 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 \ diff -r 82b8d12a553f -r 06f3783b338f jdk/make/launcher/Launcher-java.desktop.gmk --- a/jdk/make/launcher/Launcher-java.desktop.gmk Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/make/launcher/Launcher-java.desktop.gmk Tue May 03 11:45:56 2016 +0100 @@ -31,7 +31,7 @@ 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 diff -r 82b8d12a553f -r 06f3783b338f jdk/make/launcher/Launcher-java.scripting.gmk --- a/jdk/make/launcher/Launcher-java.scripting.gmk Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/make/launcher/Launcher-java.scripting.gmk Tue May 03 11:45:56 2016 +0100 @@ -27,4 +27,5 @@ $(eval $(call SetupBuildLauncher, jrunscript, \ MAIN_CLASS := com.sun.tools.script.shell.Main, \ + JAVA_ARGS := -addmods ALL-DEFAULT, \ )) diff -r 82b8d12a553f -r 06f3783b338f jdk/make/launcher/Launcher-jdk.compiler.gmk --- a/jdk/make/launcher/Launcher-jdk.compiler.gmk Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/make/launcher/Launcher-jdk.compiler.gmk Tue May 03 11:45:56 2016 +0100 @@ -27,7 +27,8 @@ $(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, \ )) diff -r 82b8d12a553f -r 06f3783b338f jdk/make/launcher/Launcher-jdk.javadoc.gmk --- a/jdk/make/launcher/Launcher-jdk.javadoc.gmk Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/make/launcher/Launcher-jdk.javadoc.gmk Tue May 03 11:45:56 2016 +0100 @@ -27,6 +27,7 @@ $(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, \ )) diff -r 82b8d12a553f -r 06f3783b338f jdk/make/launcher/Launcher-jdk.jlink.gmk --- a/jdk/make/launcher/Launcher-jdk.jlink.gmk Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/make/launcher/Launcher-jdk.jlink.gmk Tue May 03 11:45:56 2016 +0100 @@ -32,6 +32,7 @@ $(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, \ diff -r 82b8d12a553f -r 06f3783b338f jdk/make/launcher/Launcher-jdk.scripting.nashorn.shell.gmk --- a/jdk/make/launcher/Launcher-jdk.scripting.nashorn.shell.gmk Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/make/launcher/Launcher-jdk.scripting.nashorn.shell.gmk Tue May 03 11:45:56 2016 +0100 @@ -27,6 +27,6 @@ $(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, \ )) diff -r 82b8d12a553f -r 06f3783b338f jdk/src/java.base/share/classes/java/lang/ClassLoader.java --- a/jdk/src/java.base/share/classes/java/lang/ClassLoader.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/src/java.base/share/classes/java/lang/ClassLoader.java Tue May 03 11:45:56 2016 +0100 @@ -2615,7 +2615,7 @@ 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 diff -r 82b8d12a553f -r 06f3783b338f jdk/src/java.base/share/classes/java/lang/System.java --- a/jdk/src/java.base/share/classes/java/lang/System.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/src/java.base/share/classes/java/lang/System.java Tue May 03 11:45:56 2016 +0100 @@ -69,7 +69,6 @@ import jdk.internal.logger.LocalizedLoggerWrapper; import jdk.internal.module.ModuleBootstrap; -import jdk.internal.module.Modules; import jdk.internal.module.ServicesCatalog; /** @@ -1924,10 +1923,6 @@ // 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); } diff -r 82b8d12a553f -r 06f3783b338f jdk/src/java.base/share/classes/java/lang/module/Configuration.java --- a/jdk/src/java.base/share/classes/java/lang/module/Configuration.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/src/java.base/share/classes/java/lang/module/Configuration.java Tue May 03 11:45:56 2016 +0100 @@ -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 @@ this.nameToModule = Collections.emptyMap(); } - private Configuration(Configuration parent, Resolver resolver) { - Map> graph = resolver.finish(this); + private Configuration(Configuration parent, + Resolver resolver, + boolean check) + { + Map> g = resolver.finish(this, check); Map 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 @@ 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 @@ 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 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); } diff -r 82b8d12a553f -r 06f3783b338f jdk/src/java.base/share/classes/java/lang/module/ModuleDescriptor.java --- a/jdk/src/java.base/share/classes/java/lang/module/ModuleDescriptor.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/src/java.base/share/classes/java/lang/module/ModuleDescriptor.java Tue May 03 11:45:56 2016 +0100 @@ -27,13 +27,17 @@ 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 java.util.Objects.*; import jdk.internal.module.Checks; -import jdk.internal.module.Hasher.DependencyHashes; +import jdk.internal.module.ModuleHashes; /** @@ -372,8 +376,9 @@ private Provides(String service, Set 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 @@ private final String osVersion; private final Set conceals; private final Set packages; - private final DependencyHashes hashes; + private final ModuleHashes hashes; private ModuleDescriptor(String name, boolean automatic, @@ -802,7 +807,7 @@ String osArch, String osVersion, Set conceals, - DependencyHashes hashes) + ModuleHashes hashes) { this.name = name; @@ -878,7 +883,8 @@ String osArch, String osVersion, Set conceals, - Set packages) { + Set packages, + ModuleHashes hashes) { this.name = name; this.automatic = automatic; this.synthetic = synthetic; @@ -894,7 +900,7 @@ this.osName = osName; this.osArch = osArch; this.osVersion = osVersion; - this.hashes = null; + this.hashes = hashes; } /** @@ -1063,9 +1069,9 @@ } /** - * Returns the object with the hashes of the dependences. + * Returns the object with the hashes of other modules */ - Optional hashes() { + Optional hashes() { return Optional.ofNullable(hashes); } @@ -1103,7 +1109,7 @@ String osArch; String osVersion; String mainClass; - DependencyHashes hashes; + ModuleHashes hashes; /** * Initializes a new builder with the given module name. @@ -1580,7 +1586,7 @@ return this; } - /* package */ Builder hashes(DependencyHashes hashes) { + /* package */ Builder hashes(ModuleHashes hashes) { this.hashes = hashes; return this; } @@ -1719,7 +1725,9 @@ 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 @@ 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 ms, String mn) { return new Requires(ms, mn, false); @@ -1974,7 +1983,8 @@ String osArch, String osVersion, Set conceals, - Set packages) { + Set packages, + ModuleHashes hashes) { return new ModuleDescriptor(name, automatic, synthetic, @@ -1988,7 +1998,29 @@ osArch, osVersion, conceals, - packages); + packages, + hashes); + } + + @Override + public Configuration resolveRequiresAndUses(ModuleFinder finder, + Collection roots, + boolean check, + PrintStream traceOutput) + { + return Configuration.resolveRequiresAndUses(finder, roots, check, traceOutput); + } + + @Override + public ModuleReference newPatchedModule(ModuleDescriptor descriptor, + URI location, + Supplier s) { + return new ModuleReference(descriptor, location, s, true, null); + } + + @Override + public Optional hashes(ModuleDescriptor descriptor) { + return descriptor.hashes(); } }); } diff -r 82b8d12a553f -r 06f3783b338f jdk/src/java.base/share/classes/java/lang/module/ModuleFinder.java diff -r 82b8d12a553f -r 06f3783b338f jdk/src/java.base/share/classes/java/lang/module/ModuleInfo.java --- a/jdk/src/java.base/share/classes/java/lang/module/ModuleInfo.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/src/java.base/share/classes/java/lang/module/ModuleInfo.java Tue May 03 11:45:56 2016 +0100 @@ -37,11 +37,12 @@ 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 @@ // computeIfAbsent Set 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 @@ map.put(dn, hash); } - builder.hashes(new DependencyHashes(algorithm, map)); + builder.hashes(new ModuleHashes(algorithm, map)); } diff -r 82b8d12a553f -r 06f3783b338f jdk/src/java.base/share/classes/java/lang/module/ModulePath.java --- a/jdk/src/java.base/share/classes/java/lang/module/ModulePath.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/src/java.base/share/classes/java/lang/module/ModulePath.java Tue May 03 11:45:56 2016 +0100 @@ -40,7 +40,7 @@ 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.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 @@ } } - 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 @@ // 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 @@ /** * 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 @@ 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 @@ // scan the entries in the JAR file to locate the .class and service // configuration file - Stream stream = jf.stream() - .map(e -> e.getName()) - .filter(e -> (e.endsWith(".class") || e.startsWith(SERVICES_PREFIX))) - .distinct(); - Map> map - = stream.collect(Collectors.partitioningBy(s -> s.endsWith(".class"), - Collectors.toSet())); + Map> 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 classFiles = map.get(Boolean.TRUE); Set configFiles = map.get(Boolean.FALSE); @@ -433,19 +440,18 @@ 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 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 providerClasses = new HashSet<>(); + Set providerClasses = new LinkedHashSet<>(); try (InputStream in = jf.getInputStream(entry)) { BufferedReader reader = new BufferedReader(new InputStreamReader(in, "UTF-8")); @@ -475,19 +481,25 @@ private Set 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 @@ 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 @@ /** * 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 @@ } } - 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"); diff -r 82b8d12a553f -r 06f3783b338f jdk/src/java.base/share/classes/java/lang/module/ModuleReader.java --- a/jdk/src/java.base/share/classes/java/lang/module/ModuleReader.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/src/java.base/share/classes/java/lang/module/ModuleReader.java Tue May 03 11:45:56 2016 +0100 @@ -142,10 +142,11 @@ * @see ClassLoader#defineClass(String, ByteBuffer, java.security.ProtectionDomain) */ default Optional read(String name) throws IOException { - Optional in = open(name); - if (in.isPresent()) { - byte[] bytes = in.get().readAllBytes(); - return Optional.of(ByteBuffer.wrap(bytes)); + Optional oin = open(name); + if (oin.isPresent()) { + try (InputStream in = oin.get()) { + return Optional.of(ByteBuffer.wrap(in.readAllBytes())); + } } else { return Optional.empty(); } diff -r 82b8d12a553f -r 06f3783b338f jdk/src/java.base/share/classes/java/lang/module/ModuleReference.java --- a/jdk/src/java.base/share/classes/java/lang/module/ModuleReference.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/src/java.base/share/classes/java/lang/module/ModuleReference.java Tue May 03 11:45:56 2016 +0100 @@ -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.Optional; import java.util.function.Supplier; -import jdk.internal.module.Hasher.HashSupplier; +import jdk.internal.module.ModuleHashes.HashSupplier; /** @@ -54,12 +54,33 @@ private final URI location; private final Supplier 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 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 @@ URI location, Supplier 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 @@ URI location, Supplier readerSupplier) { - this(descriptor, location, readerSupplier, null); + this(descriptor, location, readerSupplier, false, null); } - /** * Returns the module descriptor. * @@ -151,6 +169,20 @@ /** + * 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 @@ return result; } - private int hash; - /** * Computes a hash code for this module reference. * @@ -181,12 +211,17 @@ 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 @@ 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; } /** diff -r 82b8d12a553f -r 06f3783b338f jdk/src/java.base/share/classes/java/lang/module/ModuleReferences.java --- a/jdk/src/java.base/share/classes/java/lang/module/ModuleReferences.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/src/java.base/share/classes/java/lang/module/ModuleReferences.java Tue May 03 11:45:56 2016 +0100 @@ -48,8 +48,8 @@ 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 @@ static ModuleReference newJarModule(ModuleDescriptor md, Path file) { URI uri = file.toUri(); Supplier 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 @@ static ModuleReference newJModModule(ModuleDescriptor md, Path file) { URI uri = file.toUri(); Supplier 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 @@ 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 @@ 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 @@ 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(); diff -r 82b8d12a553f -r 06f3783b338f jdk/src/java.base/share/classes/java/lang/module/Resolver.java --- a/jdk/src/java.base/share/classes/java/lang/module/Resolver.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/src/java.base/share/classes/java/lang/module/Resolver.java Tue May 03 11:45:56 2016 +0100 @@ -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.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 @@ 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 nameToReference = new HashMap<>(); @@ -62,10 +63,12 @@ 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 @@ */ Resolver resolveRequires(Collection roots) { - long start = trace_start("Resolve"); - // create the visit stack to get us started Deque q = new ArrayDeque<>(); for (String root : roots) { @@ -95,10 +96,9 @@ } } - 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 @@ resolve(q); - if (TRACE) { - long duration = System.currentTimeMillis() - start; - Set names = nameToReference.keySet(); - trace("Resolver completed in %s ms", duration); - names.stream().sorted().forEach(name -> trace(" %s", name)); - } - return this; } @@ -153,11 +146,10 @@ 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 @@ */ 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 @@ 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 @@ } while (!candidateConsumers.isEmpty()); - - if (TRACE) { - long duration = System.currentTimeMillis() - start; - Set names = nameToReference.keySet(); - trace("Bind completed in %s ms", duration); - names.stream().sorted().forEach(name -> trace(" %s", name)); - } - return this; } @@ -264,23 +246,33 @@ * 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> finish(Configuration cf) { + Map> finish(Configuration cf, + boolean check) + { + if (isTracing()) { + trace("Result:"); + Set names = nameToReference.keySet(); + names.stream().sorted().forEach(name -> trace(" %s", name)); + } - detectCycles(); - - checkPlatformConstraints(); - - checkHashes(); + if (check) { + detectCycles(); + checkPlatformConstraints(); + checkHashes(); + } Map> graph = makeGraph(cf); - checkExportSuppliers(graph); + if (check) { + checkExportSuppliers(graph); + } return graph; } - /** * Checks the given module graph for cycles. * @@ -420,52 +412,44 @@ } - /** * 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 ohashes = descriptor.hashes(); + // get map of module hashes + Optional 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) { + ModuleHashes hashes = ohashes.get(); - 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 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); + } - String actualHash = other.computeHash(hashes.algorithm()); + // 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)", - dn, actualHash, recordedHash); + fail("Hash of %s (%s) differs to expected hash (%s)" + + " recorded in %s", dn, actualHash, recordedHash, + descriptor.name()); } + } + } - } - - } } - } @@ -666,7 +650,7 @@ // 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 @@ } } - // 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 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); + // 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); + } } - for (String provider : provides.providers()) { - if (!packages.contains(packageName(provider))) { - fail("Provider %s not in module %s", - provider, descriptor1.name()); + // provides S + for (Map.Entry 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 @@ 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(); } } diff -r 82b8d12a553f -r 06f3783b338f jdk/src/java.base/share/classes/java/lang/module/SystemModuleFinder.java --- a/jdk/src/java.base/share/classes/java/lang/module/SystemModuleFinder.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/src/java.base/share/classes/java/lang/module/SystemModuleFinder.java Tue May 03 11:45:56 2016 +0100 @@ -44,6 +44,7 @@ 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 @@ 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 @@ } }; - 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 @@ 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 diff -r 82b8d12a553f -r 06f3783b338f jdk/src/java.base/share/classes/java/lang/reflect/Layer.java --- a/jdk/src/java.base/share/classes/java/lang/reflect/Layer.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/src/java.base/share/classes/java/lang/reflect/Layer.java Tue May 03 11:45:56 2016 +0100 @@ -27,6 +27,7 @@ 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 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 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> map = new HashMap<>(); + for (Module m : nameToModule.values()) { + ModuleDescriptor descriptor = m.getDescriptor(); + for (Provides provides : descriptor.provides().values()) { + String service = provides.service(); + Set 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 findServices(String service) { + Set 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; } diff -r 82b8d12a553f -r 06f3783b338f jdk/src/java.base/share/classes/java/lang/reflect/Module.java --- a/jdk/src/java.base/share/classes/java/lang/reflect/Module.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/src/java.base/share/classes/java/lang/reflect/Module.java Tue May 03 11:45:56 2016 +0100 @@ -43,11 +43,7 @@ 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 @@ this.name = null; this.loader = loader; this.descriptor = null; - - // unnamed modules are loose - this.loose = true; } @@ -245,17 +238,27 @@ } - // -- readability -- + // -- + + // the special Module to mean reads or exported to "all unnamed modules" + private static final Module ALL_UNNAMED_MODULE = new Module(null); - // true if this module reads all unnamed modules (a.k.a. loose module) - private volatile boolean loose; + // special Module to mean exported to "everyone" + private static final Module EVERYONE_MODULE = new Module(null); + + // exported to all modules + private static final Set 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 reads; - // created lazily, additional modules that this module reflectively reads - private volatile WeakSet transientReads; + // additional module (2nd key) that some module (1st key) reflectively reads + private static final WeakPairMap transientReads + = new WeakPairMap<>(); /** @@ -284,22 +287,19 @@ // check if this module reads other if (other.isNamed()) { - Set 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 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 @@ } /** - * 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 @@ * 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 reads = this.reads; if (reads != null && reads.contains(other)) return; // update VM first, just in case it fails - if (syncVM) - addReads0(this, other); + if (syncVM) { + if (other == ALL_UNNAMED_MODULE) { + addReads0(this, null); + } else { + addReads0(this, other); + } + } // add reflective read - WeakSet tr = this.transientReads; - if (tr == null) { - synchronized (this) { - tr = this.transientReads; - if (tr == null) { - tr = new WeakSet<>(); - this.transientReads = tr; - } - } - } - tr.add(other); + transientReads.putIfAbsent(this, other, Boolean.TRUE); } @@ -404,15 +391,10 @@ // (will be final when the modules are defined in reverse topology order) private volatile Map> exports; - // created lazily, additional exports added at run-time - private volatile Map> transientExports; - - // the special Module to mean exported to all modules - private static final Module EVERYONE_MODULE = new Module(null); - private static final Set 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> + transientExports = new WeakPairMap<>(); /** @@ -489,23 +471,9 @@ if (exports != null) { Set 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 @@ * package package to the given module. */ private boolean isExportedReflectively(String pn, Module other) { - Map> te = this.transientExports; - if (te != null) { - WeakSet targets = te.get(pn); + // exported to all modules + Map exports = transientExports.get(this, EVERYONE_MODULE); + if (exports != null && exports.containsKey(pn)) + return true; - if (targets != null) { - - // exported to all modules - if (targets.contains(EVERYONE_MODULE)) - return true; + if (other != EVERYONE_MODULE) { - if (other != EVERYONE_MODULE) { + // exported to other + exports = transientExports.get(this, other); + if (exports != null && exports.containsKey(pn)) + return true; - // 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; - } + // 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; } } + return false; } @@ -638,34 +604,19 @@ } } - // create transientExports if needed - Map> 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 s = te.get(pn); - if (s == null) { - s = new WeakSet<>(); - WeakSet 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> transientUses; + // additional service type (2nd key) that some module (1st key) uses + private static final WeakPairMap, Boolean> transientUses + = new WeakPairMap<>(); /** * If the caller's module is this module then update this module to add a @@ -702,17 +653,7 @@ } if (!canUse(st)) { - WeakSet> 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 @@ return true; // uses added via addUses - WeakSet> uses = this.transientUses; - if (uses != null && uses.contains(st)) - return true; - - return false; + return transientUses.containsKeyPair(this, st); } @@ -885,7 +822,7 @@ // -- 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 @@ // automatic modules reads all unnamed modules if (descriptor.isAutomatic()) { - m.implAddReads(null, true); + m.implAddReads(ALL_UNNAMED_MODULE, true); } // exports @@ -1097,7 +1034,7 @@ * 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 @@ } - // -- supporting classes -- - - - /** - * A "not-a-Set" set of weakly referenced objects that supports concurrent - * access. - */ - private static class WeakSet { - private final ReadWriteLock lock = new ReentrantReadWriteLock(); - private final Lock readLock = lock.readLock(); - private final Lock writeLock = lock.writeLock(); - - private final WeakHashMap 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 @@ 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 void addPackage(Module m, String pn) { m.implAddPackage(pn, true); } + @Override + public ServicesCatalog getServicesCatalog(Layer layer) { + return layer.getServicesCatalog(); + } }); } } diff -r 82b8d12a553f -r 06f3783b338f jdk/src/java.base/share/classes/java/lang/reflect/WeakPairMap.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.base/share/classes/java/lang/reflect/WeakPairMap.java Tue May 03 11:45:56 2016 +0100 @@ -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 the type of 1st key in key pair + * @param the type of 2nd key in key pair + * @param the type of value + * @author Peter Levart + */ +final class WeakPairMap { + + private final ConcurrentHashMap, V> map = new ConcurrentHashMap<>(); + private final ReferenceQueue 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. + *

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. + *

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 + 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 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 { + + static Pair weak(K1 k1, K2 k2, + ReferenceQueue queue) { + return new Weak<>(k1, k2, queue); + } + + static Pair 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: + *
{@code
+         *
+         *     +-referent-> [K1]                +-referent-> [K2]
+         *     |                                |
+         *   +----------------+               +----------------+
+         *   | Pair.Weak <:   |-----peer----->| (anonymous) <: |
+         *   | WeakRefPeer,   |               | WeakRefPeer    |
+         *   | Pair           |<--weakPair()--|                |
+         *   +----------------+               +----------------+
+         *     |            ^
+         *     |            |
+         *     +-weakPair()-+
+         *
+         * }
+ *

+ * 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 extends WeakRefPeer implements Pair { + + // saved hash so it can be retrieved after the reference is cleared + private final int hash; + // link to peer + private final WeakRefPeer peer; + + Weak(K1 k1, K2 k2, ReferenceQueue queue) { + super(k1, queue); + hash = Pair.hashCode(k1, k2); + peer = new WeakRefPeer<>(k2, queue) { + // link back to 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 implements Pair { + 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 extends WeakReference { + + WeakRefPeer(K k, ReferenceQueue queue) { + super(Objects.requireNonNull(k), queue); + } + + /** + * @return the {@link Pair.Weak} side of the pair of peers. + */ + abstract Pair.Weak weakPair(); + } +} diff -r 82b8d12a553f -r 06f3783b338f jdk/src/java.base/share/classes/java/util/ServiceLoader.java --- a/jdk/src/java.base/share/classes/java/util/ServiceLoader.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/src/java.base/share/classes/java/util/ServiceLoader.java Tue May 03 11:45:56 2016 +0100 @@ -29,8 +29,6 @@ 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 @@ * and deployed as a named module must have an appropriate uses clause * in its module descriptor 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 * provides clause in its module descriptor to declare that the module * provides an implementation of the service. The uses and * provides allow consumers of a service to be linked to @@ -550,35 +548,29 @@ /** * 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 { final String serviceName; Layer currentLayer; - Iterator descriptorIterator; - Iterator providersIterator; - - Module nextModule; - String nextProvider; + Iterator iterator; + ServiceProvider nextProvider; LayerLookupIterator() { serviceName = service.getName(); currentLayer = layer; // need to get us started - descriptorIterator = descriptors(layer, serviceName); + iterator = providers(currentLayer, serviceName); } - Iterator descriptors(Layer layer, String service) { - return layer.modules().stream() - .map(Module::getDescriptor) - .filter(d -> d.provides().get(service) != null) - .iterator(); + Iterator providers(Layer layer, String service) { + ServicesCatalog catalog = SharedSecrets + .getJavaLangReflectModuleAccess() + .getServicesCatalog(layer); + + return catalog.findServices(serviceName).iterator(); } @Override @@ -591,30 +583,18 @@ 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 @@ 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)) diff -r 82b8d12a553f -r 06f3783b338f jdk/src/java.base/share/classes/jdk/internal/loader/BootLoader.java --- a/jdk/src/java.base/share/classes/jdk/internal/loader/BootLoader.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/src/java.base/share/classes/jdk/internal/loader/BootLoader.java Tue May 03 11:45:56 2016 +0100 @@ -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 @@ } // 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 = diff -r 82b8d12a553f -r 06f3783b338f jdk/src/java.base/share/classes/jdk/internal/loader/BuiltinClassLoader.java --- a/jdk/src/java.base/share/classes/jdk/internal/loader/BuiltinClassLoader.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/src/java.base/share/classes/jdk/internal/loader/BuiltinClassLoader.java Tue May 03 11:45:56 2016 +0100 @@ -104,7 +104,7 @@ * 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; diff -r 82b8d12a553f -r 06f3783b338f jdk/src/java.base/share/classes/jdk/internal/misc/JavaLangModuleAccess.java --- a/jdk/src/java.base/share/classes/jdk/internal/misc/JavaLangModuleAccess.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/src/java.base/share/classes/jdk/internal/misc/JavaLangModuleAccess.java Tue May 03 11:45:56 2016 +0100 @@ -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 @@ String osArch, String osVersion, Set conceals, - Set packages); + Set 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 roots, + boolean check, + PrintStream traceOutput); + + /** + * Creates a ModuleReference to a "patched" module. + */ + ModuleReference newPatchedModule(ModuleDescriptor descriptor, + URI location, + Supplier readerSupplier); + + /** + * Returns the object with the hashes of other modules + */ + Optional hashes(ModuleDescriptor descriptor); + } diff -r 82b8d12a553f -r 06f3783b338f jdk/src/java.base/share/classes/jdk/internal/misc/JavaLangReflectModuleAccess.java --- a/jdk/src/java.base/share/classes/jdk/internal/misc/JavaLangReflectModuleAccess.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/src/java.base/share/classes/jdk/internal/misc/JavaLangReflectModuleAccess.java Tue May 03 11:45:56 2016 +0100 @@ -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 */ @@ -57,6 +60,11 @@ 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 @@ * Add a package to the given module. */ void addPackage(Module m, String pkg); -} + + /** + * Returns the ServicesCatalog for the given Layer. + */ + ServicesCatalog getServicesCatalog(Layer layer); + +} \ No newline at end of file diff -r 82b8d12a553f -r 06f3783b338f jdk/src/java.base/share/classes/jdk/internal/module/Builder.java --- a/jdk/src/java.base/share/classes/jdk/internal/module/Builder.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/src/java.base/share/classes/jdk/internal/module/Builder.java Tue May 03 11:45:56 2016 +0100 @@ -74,6 +74,8 @@ String osName; String osArch; String osVersion; + String algorithm; + Map hashes; Builder(String name, int reqs, int exports, int provides, int conceals, int packages) { @@ -252,6 +254,25 @@ } /** + * 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 @@ 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 @@ osArch, osVersion, conceals, - computePackages(exports, conceals)); + computePackages(exports, conceals), + moduleHashes); } } diff -r 82b8d12a553f -r 06f3783b338f jdk/src/java.base/share/classes/jdk/internal/module/ClassFileAttributes.java --- a/jdk/src/java.base/share/classes/jdk/internal/module/ClassFileAttributes.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/src/java.base/share/classes/jdk/internal/module/ClassFileAttributes.java Tue May 03 11:45:56 2016 +0100 @@ -34,6 +34,7 @@ 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.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 @@ for (int i=0; i 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 @@ * 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]; * * } */ @@ -579,9 +579,9 @@ * 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 @@ map.put(dn, hash); } - DependencyHashes hashes = new DependencyHashes(algorithm, map); + ModuleHashes hashes = new ModuleHashes(algorithm, map); return new HashesAttribute(hashes); } diff -r 82b8d12a553f -r 06f3783b338f jdk/src/java.base/share/classes/jdk/internal/module/Hasher.java --- a/jdk/src/java.base/share/classes/jdk/internal/module/Hasher.java Wed Jul 05 21:39:33 2017 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,142 +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. 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 jdk.internal.module; - -import java.io.IOException; -import java.io.UncheckedIOException; -import java.nio.ByteBuffer; -import java.nio.channels.FileChannel; -import java.nio.file.Path; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.Base64; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - -/** - * Supporting class for computing, encoding and decoding hashes (message - * digests). - */ - -public class Hasher { - private Hasher() { } - - /** - * A supplier of an encoded message digest. - */ - public static interface HashSupplier { - String generate(String algorithm); - } - - /** - * Encapsulates the result of hashing the contents of a number of module - * artifacts. - */ - public static class DependencyHashes { - private final String algorithm; - private final Map nameToHash; - - public DependencyHashes(String algorithm, Map 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 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); - } - } - - - /** - * Computes the hash for the given file with the given message digest - * algorithm. Returns the results a base64-encoded String. - * - * @throws UncheckedIOException if an I/O error occurs - * @throws RuntimeException if the algorithm is not available - */ - public static String generate(Path file, String algorithm) { - try { - MessageDigest md = MessageDigest.getInstance(algorithm); - - // Ideally we would just mmap the file but this consumes too much - // 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) { - bb.flip(); - md.update(bb); - assert bb.remaining() == 0; - bb.clear(); - } - } - - byte[] bytes = md.digest(); - return Base64.getEncoder().encodeToString(bytes); - } catch (NoSuchAlgorithmException e) { - throw new RuntimeException(e); - } catch (IOException ioe) { - throw new UncheckedIOException(ioe); - } - } - - /** - * Computes the hash for every entry in the given map, returning a - * {@code DependencyHashes} 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 - */ - public static DependencyHashes generate(Map map, String algorithm) { - Map nameToHash = new HashMap<>(); - for (Map.Entry entry: map.entrySet()) { - String name = entry.getKey(); - Path path = entry.getValue(); - nameToHash.put(name, generate(path, algorithm)); - } - return new DependencyHashes(algorithm, nameToHash); - } -} diff -r 82b8d12a553f -r 06f3783b338f jdk/src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java --- a/jdk/src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java Tue May 03 11:45:56 2016 +0100 @@ -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.ModuleDescriptor; +import java.lang.module.ModuleFinder; import java.lang.module.ModuleReference; -import java.lang.module.ModuleFinder; 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.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 @@ * 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 @@ 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 @@ long t0 = System.nanoTime(); - // system module path - ModuleFinder systemModulePath = ModuleFinder.ofSystem(); + // system modules + ModuleFinder systemModules = ModuleFinder.ofSystem(); + + PerfCounters.systemModulesTime.addElapsedTimeFrom(t0); - // 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 + + 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 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 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 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 @@ addAllApplicationModules = true; break; default : - addModules.add(mod); + roots.add(mod); } } } - // The root modules to resolve - Set 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 mods = new HashSet<>(); @@ -171,62 +180,101 @@ 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 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 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 @@ && 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 @@ } } - PerfCounters.loadModulesTime.addElapsedTimeFrom(t3); + PerfCounters.loadModulesTime.addElapsedTimeFrom(t5); + // -XaddReads and -XaddExports addExtraReads(bootLayer); @@ -295,25 +346,21 @@ // module name -> reference Map 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 mrefs = new HashSet<>(map.values()); - // add the other modules - for (String mod : otherMods) { - Optional 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 find(String name) { @@ -369,15 +416,15 @@ 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 @@ * 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> decode(String prefix) { int index = 0; @@ -467,42 +510,15 @@ 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); - - if (pos == -1) { - - // new format: $KEY=$MODULE(,$MODULE)* - - Set values = map.get(key); - if (values != null) - fail(key + " specified more than once"); + // value is (,)* + if (map.containsKey(key)) + 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 values = new HashSet<>(); + map.put(key, values); + for (String s : rhs.split(",")) { + if (s.length() > 0) values.add(s); } index++; @@ -521,6 +537,13 @@ } 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 diff -r 82b8d12a553f -r 06f3783b338f jdk/src/java.base/share/classes/jdk/internal/module/ModuleHashes.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.base/share/classes/jdk/internal/module/ModuleHashes.java Tue May 03 11:45:56 2016 +0100 @@ -0,0 +1,147 @@ +/* + * 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 + * 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 jdk.internal.module; + +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; +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; + +/** + * The result of hashing the contents of a number of module artifacts. + */ + +public final class ModuleHashes { + + /** + * A supplier of an encoded message digest. + */ + public static interface HashSupplier { + String generate(String algorithm); + } + + + private final String algorithm; + private final Map nameToHash; + + /** + * 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 ModuleHashes(String algorithm, Map 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 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 hashes() { + return nameToHash; + } + + /** + * Computes the hash for the given file with the given message digest + * algorithm. Returns the results a base64-encoded String. + * + * @throws UncheckedIOException if an I/O error occurs + * @throws RuntimeException if the algorithm is not available + */ + public static String computeHashAsString(Path file, String algorithm) { + try { + MessageDigest md = MessageDigest.getInstance(algorithm); + + // Ideally we would just mmap the file but this consumes too much + // memory when jlink is running concurrently on very large jmods + try (FileChannel fc = FileChannel.open(file)) { + ByteBuffer bb = ByteBuffer.allocate(32*1024); + while (fc.read(bb) > 0) { + bb.flip(); + md.update(bb); + assert bb.remaining() == 0; + bb.clear(); + } + } + + byte[] bytes = md.digest(); + return Base64.getEncoder().encodeToString(bytes); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } catch (IOException ioe) { + throw new UncheckedIOException(ioe); + } + } + + /** + * Computes the hash for every entry in the given map, returning a + * {@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 ModuleHashes encapsulate the hashes + */ + public static ModuleHashes generate(Map map, String algorithm) { + Map nameToHash = new HashMap<>(); + for (Map.Entry entry: map.entrySet()) { + String name = entry.getKey(); + Path path = entry.getValue(); + nameToHash.put(name, computeHashAsString(path, algorithm)); + } + return new ModuleHashes(algorithm, nameToHash); + } +} diff -r 82b8d12a553f -r 06f3783b338f jdk/src/java.base/share/classes/jdk/internal/module/ModuleInfoExtender.java --- a/jdk/src/java.base/share/classes/jdk/internal/module/ModuleInfoExtender.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/src/java.base/share/classes/jdk/internal/module/ModuleInfoExtender.java Tue May 03 11:45:56 2016 +0100 @@ -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.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 @@ 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 @@ /** * 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; } diff -r 82b8d12a553f -r 06f3783b338f jdk/src/java.base/share/classes/jdk/internal/module/ModuleInfoWriter.java --- a/jdk/src/java.base/share/classes/jdk/internal/module/ModuleInfoWriter.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/src/java.base/share/classes/jdk/internal/module/ModuleInfoWriter.java Tue May 03 11:45:56 2016 +0100 @@ -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 @@ * 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 oversion = descriptor.version(); - if (oversion.isPresent()) - cw.visitAttribute(new VersionAttribute(oversion.get())); - - Optional 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, diff -r 82b8d12a553f -r 06f3783b338f jdk/src/java.base/share/classes/jdk/internal/module/ModulePatcher.java --- a/jdk/src/java.base/share/classes/jdk/internal/module/ModulePatcher.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/src/java.base/share/classes/jdk/internal/module/ModulePatcher.java Tue May 03 11:45:56 2016 +0100 @@ -91,56 +91,29 @@ Map> map = new HashMap<>(); while (value != null) { - int pos = value.indexOf('='); + + // =(:)* - if (pos == -1 && index > 0) + int pos = value.indexOf('='); + if (pos == -1) throwIAE("Unable to parse: " + value); - if (pos == 0) throwIAE("Missing module name: " + value); - if (pos > 0) { - - // new format: =(:)* - - String mn = value.substring(0, pos); - List 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)); - } - } - - } else { + String mn = value.substring(0, pos); + List list = map.get(mn); + if (list != null) + throwIAE("Module " + mn + " specified more than once"); + list = new ArrayList<>(); + map.put(mn, list); - // old format: (:)* - - 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) { } - } + String paths = value.substring(pos+1); + for (String path : paths.split(File.pathSeparator)) { + if (!path.isEmpty()) { + list.add(Paths.get(path)); } - } - index++; value = System.getProperty(PATCH_PROPERTY_PREFIX + index); } @@ -175,7 +148,8 @@ 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 @@ 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)); } diff -r 82b8d12a553f -r 06f3783b338f jdk/src/java.base/share/classes/jdk/internal/module/Modules.java --- a/jdk/src/java.base/share/classes/jdk/internal/module/Modules.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/src/java.base/share/classes/jdk/internal/module/Modules.java Tue May 03 11:45:56 2016 +0100 @@ -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 @@ * 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 @@ * 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, @@ -96,6 +96,13 @@ } /** + * 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. */ diff -r 82b8d12a553f -r 06f3783b338f jdk/src/java.base/share/classes/jdk/internal/module/ServicesCatalog.java --- a/jdk/src/java.base/share/classes/jdk/internal/module/ServicesCatalog.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/src/java.base/share/classes/jdk/internal/module/ServicesCatalog.java Tue May 03 11:45:56 2016 +0100 @@ -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.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 services catalog. 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> loaderServices = new HashMap<>(); - - /** - * Creates a new module catalog. - */ - public ServicesCatalog() { } - - /** - * Registers the module in this module catalog. - */ - public void register(Module m) { - ModuleDescriptor descriptor = m.getDescriptor(); + @Override + public int hashCode() { + return Objects.hash(module, providerName); + } - writeLock.lock(); - try { - // extend the services map - for (Provides ps : descriptor.provides().values()) { - String service = ps.service(); - Set providerNames = ps.providers(); - - // create a new set to replace the existing - Set result = new HashSet<>(); - Set 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 + */ + Set findServices(String service); + + /** + * Creates a ServicesCatalog that supports concurrent registration and + * and lookup. */ - public Set findServices(String service) { - readLock.lock(); - try { - return loaderServices.getOrDefault(service, Collections.emptySet()); - } finally { - readLock.unlock(); - } + static ServicesCatalog create() { + return new ServicesCatalog() { + + private Map> map = new ConcurrentHashMap<>(); + + @Override + public void register(Module m) { + ModuleDescriptor descriptor = m.getDescriptor(); + + for (Provides provides : descriptor.provides().values()) { + String service = provides.service(); + Set providerNames = provides.providers(); + + // create a new set to replace the existing + Set result = new HashSet<>(); + Set 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 findServices(String service) { + return map.getOrDefault(service, Collections.emptySet()); + } + + }; } -} +} \ No newline at end of file diff -r 82b8d12a553f -r 06f3783b338f jdk/src/java.base/share/classes/jdk/internal/module/SystemModules.java --- a/jdk/src/java.base/share/classes/jdk/internal/module/SystemModules.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/src/java.base/share/classes/jdk/internal/module/SystemModules.java Tue May 03 11:45:56 2016 +0100 @@ -40,21 +40,26 @@ */ 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 static ModuleDescriptor[] modules() { return new ModuleDescriptor[0]; } -} \ No newline at end of file + +} diff -r 82b8d12a553f -r 06f3783b338f jdk/src/java.base/share/classes/module-info.java --- a/jdk/src/java.base/share/classes/module-info.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/src/java.base/share/classes/module-info.java Tue May 03 11:45:56 2016 +0100 @@ -166,6 +166,8 @@ java.sql, java.xml, jdk.charsets, + jdk.jartool, + jdk.jlink, jdk.net, jdk.scripting.nashorn, jdk.unsupported, diff -r 82b8d12a553f -r 06f3783b338f jdk/src/java.base/share/classes/sun/launcher/resources/launcher.properties --- a/jdk/src/java.base/share/classes/sun/launcher/resources/launcher.properties Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/src/java.base/share/classes/sun/launcher/resources/launcher.properties Tue May 03 11:45:56 2016 +0100 @@ -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 -m | /\n\ +\ or {0} [options] -mp -m [/] [args...]\n\ \ (to execute the main class in a module)\n\ where options include:\n @@ -51,8 +51,9 @@ \ 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 | /\n\ -\ the initial or main module to resolve\n\ +\ -m [/]\n\ +\ the initial module to resolve, and the name of the main class\n\ +\ to execute if not specified by the module\n\ \ -addmods [,...]\n\ \ root modules to resolve in addition to the initial module\n\ \ -limitmods [,...]\n\ diff -r 82b8d12a553f -r 06f3783b338f jdk/src/jdk.jartool/share/classes/sun/tools/jar/GNUStyleOptions.java --- a/jdk/src/jdk.jartool/share/classes/sun/tools/jar/GNUStyleOptions.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/src/jdk.jartool/share/classes/sun/tools/jar/GNUStyleOptions.java Tue May 03 11:45:56 2016 +0100 @@ -136,10 +136,10 @@ 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); } diff -r 82b8d12a553f -r 06f3783b338f jdk/src/jdk.jartool/share/classes/sun/tools/jar/Main.java --- a/jdk/src/jdk.jartool/share/classes/sun/tools/jar/Main.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/src/jdk.jartool/share/classes/sun/tools/jar/Main.java Tue May 03 11:45:56 2016 +0100 @@ -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.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 @@ /* 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 @@ 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 @@ } } 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 @@ 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 @@ return false; } - @SuppressWarnings("unchecked") + static String toString(Set 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()); - - Set requires = md.requires(); - if (!requires.isEmpty()) { - sb.append("\nRequires:"); - requires.forEach(r -> - sb.append("\n ").append(r.name()) - .append(toString(r.modifiers(), " [ ", " ]"))); - } + sb.append("\n").append(md.toNameAndVersion()); - Set s = md.uses(); - if (!s.isEmpty()) { - sb.append("\nUses: "); - s.forEach(sv -> sb.append("\n ").append(sv)); - } + 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 exports = md.exports(); - if (!exports.isEmpty()) { - sb.append("\nExports:"); - exports.forEach(sv -> sb.append("\n ").append(sv)); - } + md.uses().stream().sorted() + .forEach(p -> sb.append("\n uses ").append(p)); + + md.exports().stream() + .sorted(Comparator.comparing(Exports::source)) + .forEach(p -> sb.append("\n exports ").append(p)); + + md.conceals().stream().sorted() + .forEach(p -> sb.append("\n conceals ").append(p)); - Map 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.provides().values().stream() + .sorted(Comparator.comparing(Provides::service)) + .forEach(p -> sb.append("\n provides ").append(p.service()) + .append(" with ") + .append(toString(p.providers()))); - Optional mc = md.mainClass(); - if (mc.isPresent()) - sb.append("\nMain class:\n " + mc.get()); + md.mainClass().ifPresent(v -> sb.append("\n main-class " + v)); + + md.osName().ifPresent(v -> sb.append("\n operating-system-name " + v)); - s = md.conceals(); - if (!s.isEmpty()) { - sb.append("\nConceals:"); - s.forEach(p -> sb.append("\n ").append(p)); - } + md.osArch().ifPresent(v -> sb.append("\n operating-system-architecture " + v)); - try { - Method m = ModuleDescriptor.class.getDeclaredMethod("hashes"); - m.setAccessible(true); - Optional optHashes = - (Optional) m.invoke(md); + md.osVersion().ifPresent(v -> sb.append("\n operating-system-version " + v)); - 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); - } + 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)))); + output(sb.toString()); } @@ -1751,7 +1748,6 @@ md = ModuleDescriptor.read(in); } String name = md.name(); - Set dependences = md.requires(); Set exported = md.exports() .stream() .map(ModuleDescriptor.Exports::source) @@ -1778,9 +1774,17 @@ 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 @@ } /** - * 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 moduleDependences) - throws IOException - { - Map map = new HashMap<>(); - Matcher matcher = dependenciesToHash.matcher(""); - for (ModuleDescriptor.Requires md: moduleDependences) { - String dn = md.name(); - if (matcher.reset(dn).find()) { - Optional 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 moduleNameToPath; + final Set 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 find(String name) { + if (descriptor.name().equals(name)) + return Optional.of(mref); + else + return Optional.empty(); + } + + @Override + public Set 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> graph = transpose(); + + // find the modules that transitively depend upon the specified name + Deque deque = new ArrayDeque<>(); + deque.add(name); + Set mods = visitNodes(graph, deque); + + // filter modules matching the pattern specified --hash-modules + // as well as itself as the jmod file is being generated + Map 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 visitNodes(Map> graph, + Deque roots) { + Set 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> transpose() { + Map> transposedGraph = new HashMap<>(); + Deque deque = new ArrayDeque<>(modules); + + Set 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; } } - - private static Path modRefToPath(ModuleReference mref) { - URI location = mref.location().get(); - return Paths.get(location); - } } diff -r 82b8d12a553f -r 06f3783b338f jdk/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar.properties --- a/jdk/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar.properties Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar.properties Tue May 03 11:45:56 2016 +0100 @@ -57,7 +57,7 @@ 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.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 @@ \ 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. \ No newline at end of file diff -r 82b8d12a553f -r 06f3783b338f jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/builder/DefaultImageBuilder.java --- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/builder/DefaultImageBuilder.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/builder/DefaultImageBuilder.java Tue May 03 11:45:56 2016 +0100 @@ -95,27 +95,23 @@ private final Path root; private final Path mdir; - private final boolean genBom; private final Set 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 modules, String bom, Properties release) throws IOException { + private void storeFiles(Set modules, Properties release) throws IOException { if (release != null) { addModules(release, modules); File r = new File(root.toFile(), "release"); @@ -123,11 +119,6 @@ release.store(fo, null); } } - // Generate bom - if (genBom) { - File bomFile = new File(root.toFile(), "bom"); - createUtf8File(bomFile, bom); - } } private void addModules(Properties release, Set modules) throws IOException { @@ -144,7 +135,7 @@ } @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 @@ 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 @@ } @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 @@ 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); - } } } } diff -r 82b8d12a553f -r 06f3783b338f jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/builder/ImageBuilder.java --- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/builder/ImageBuilder.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/builder/ImageBuilder.java Tue May 03 11:45:56 2016 +0100 @@ -42,22 +42,20 @@ * 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"); } diff -r 82b8d12a553f -r 06f3783b338f jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImageFileCreator.java --- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImageFileCreator.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImageFileCreator.java Tue May 03 11:45:56 2016 +0100 @@ -88,7 +88,7 @@ ByteOrder byteOrder) throws IOException { return ImageFileCreator.create(archives, byteOrder, - new ImagePluginStack(null)); + new ImagePluginStack()); } public static ExecutableImage create(Set archives, diff -r 82b8d12a553f -r 06f3783b338f jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImagePluginConfiguration.java --- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImagePluginConfiguration.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImagePluginConfiguration.java Tue May 03 11:45:56 2016 +0100 @@ -68,20 +68,13 @@ 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> plugins = new LinkedHashMap<>(); for (Plugin.CATEGORY cat : CATEGORIES_ORDER) { @@ -150,7 +143,7 @@ } @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 @@ PluginContext ctxt = pluginsConfiguration.getPluginContext(); return new ImagePluginStack(builder, transformerPlugins, - lastSorter, postProcessingPlugins, ctxt, bom); + lastSorter, postProcessingPlugins, ctxt); } } diff -r 82b8d12a553f -r 06f3783b338f jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImagePluginStack.java --- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImagePluginStack.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImagePluginStack.java Tue May 03 11:45:56 2016 +0100 @@ -167,28 +167,25 @@ private final ImageBuilder imageBuilder; private final Properties release; - private final String bom; + + public ImagePluginStack() { + this(null, Collections.emptyList(), null, + Collections.emptyList(), null); + } - public ImagePluginStack(String bom) { - this(null, Collections.emptyList(), null, - Collections.emptyList(), null, bom); + public ImagePluginStack(ImageBuilder imageBuilder, + List contentPlugins, + Plugin lastSorter, + List postprocessingPlugins) { + this(imageBuilder, contentPlugins, lastSorter, + postprocessingPlugins, null); } public ImagePluginStack(ImageBuilder imageBuilder, List contentPlugins, Plugin lastSorter, List postprocessingPlugins, - String bom) { - this(imageBuilder, contentPlugins, lastSorter, - postprocessingPlugins, null, bom); - } - - public ImagePluginStack(ImageBuilder imageBuilder, - List contentPlugins, - Plugin lastSorter, - List postprocessingPlugins, - PluginContext ctxt, - String bom) { + PluginContext ctxt) { Objects.requireNonNull(contentPlugins); this.lastSorter = lastSorter; for (TransformerPlugin p : contentPlugins) { @@ -204,7 +201,6 @@ } 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 @@ } catch (Exception ignored) { } - imageBuilder.storeFiles(new LastPool(transformed), bom, release); + imageBuilder.storeFiles(new LastPool(transformed), release); } public ExecutableImage getExecutableImage() throws IOException { diff -r 82b8d12a553f -r 06f3783b338f jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java --- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java Tue May 03 11:45:56 2016 +0100 @@ -70,6 +70,7 @@ * ## Should use jdk.joptsimple some day. */ public class JlinkTask { + private static final boolean DEBUG = Boolean.getBoolean("jlink.debug"); private static void fail(Class type, String format, @@ -142,9 +143,6 @@ } task.options.packagedModulesPath = path; }, true, "--keep-packaged-modules"), - new Option(false, (task, opt, arg) -> { - task.options.genbom = true; - }, true, "--genbom"), new Option(true, (task, opt, arg) -> { task.options.saveoptsfile = arg; }, "--saveopts"), @@ -175,7 +173,6 @@ static class OptionsValues { boolean help; - boolean genbom; String saveoptsfile; boolean version; boolean fullVersion; @@ -219,18 +216,24 @@ } 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 @@ } } - private static Map modulesToPath(Configuration cf) { - Map 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 @@ 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 @@ } 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 @@ // 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 @@ return finder; } + + private static Path toPathLocation(ResolvedModule m) { + Optional 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 addMods, Set limitMods, @@ -374,7 +374,8 @@ ModuleFinder.empty(), addMods); - Map mods = modulesToPath(cf); + Map mods = cf.modules().stream() + .collect(Collectors.toMap(ResolvedModule::name, JlinkTask::toPathLocation)); return new ImageHelper(cf, mods, order, retainModulesPath); } @@ -399,21 +400,15 @@ 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 mrefs = new HashSet<>(map.values()); - // add the other modules - for (String mod : otherMods) { - Optional 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 find(String name) { diff -r 82b8d12a553f -r 06f3783b338f jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/TaskHelper.java --- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/TaskHelper.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/TaskHelper.java Tue May 03 11:45:56 2016 +0100 @@ -337,8 +337,8 @@ 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 @@ // 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 @@ + 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() { diff -r 82b8d12a553f -r 06f3783b338f jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/packager/AppRuntimeImageBuilder.java --- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/packager/AppRuntimeImageBuilder.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/packager/AppRuntimeImageBuilder.java Tue May 03 11:45:56 2016 +0100 @@ -139,7 +139,7 @@ // 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); } diff -r 82b8d12a553f -r 06f3783b338f jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/DefaultCompressPlugin.java --- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/DefaultCompressPlugin.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/DefaultCompressPlugin.java Tue May 03 11:45:56 2016 +0100 @@ -125,7 +125,7 @@ zip = new ZipPlugin(resFilter); break; default: - throw new PluginException("Invalid level " + level); + throw new IllegalArgumentException("Invalid compression level " + level); } } else { ss = new StringSharingPlugin(resFilter); diff -r 82b8d12a553f -r 06f3783b338f jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/ExcludeVMPlugin.java --- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/ExcludeVMPlugin.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/ExcludeVMPlugin.java Tue May 03 11:45:56 2016 +0100 @@ -208,7 +208,7 @@ break; } default: { - throw new PluginException("Unknown option " + value); + throw new IllegalArgumentException("Unknown exclude VM option: " + value); } } predicate = new ResourceFilter(Utils.listParser.apply(exclude), true); diff -r 82b8d12a553f -r 06f3783b338f jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/IncludeLocalesPlugin.java --- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/IncludeLocalesPlugin.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/IncludeLocalesPlugin.java Tue May 03 11:45:56 2016 +0100 @@ -164,7 +164,7 @@ try { return new Locale.LanguageRange(s); } catch (IllegalArgumentException iae) { - throw new PluginException(String.format( + throw new IllegalArgumentException(String.format( PluginsResourceBundle.getMessage(NAME + ".invalidtag"), s)); } }) diff -r 82b8d12a553f -r 06f3783b338f jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/OptimizationPlugin.java --- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/OptimizationPlugin.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/OptimizationPlugin.java Tue May 03 11:45:56 2016 +0100 @@ -273,7 +273,7 @@ } 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); diff -r 82b8d12a553f -r 06f3783b338f jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/SystemModuleDescriptorPlugin.java --- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/SystemModuleDescriptorPlugin.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/SystemModuleDescriptorPlugin.java Tue May 03 11:45:56 2016 +0100 @@ -39,6 +39,8 @@ 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 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 @@ * @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 @@ 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 @@ 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 @@ 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 @@ // 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 @@ // list of all ModuleDescriptorBuilders, invoked in turn when building. private final List builders = new ArrayList<>(); + // module name to hash + private final Map modulesToHash = new HashMap<>(); + // map Set to a specialized builder to allow them to be // deduplicated as they are requested private final Map, StringSetBuilder> stringSets = new HashMap<>(); @@ -268,6 +287,11 @@ "[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 @@ 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 @@ } } - // 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 @@ conceals(pn); } - if (md.version().isPresent()) { - version(md.version().get()); - } + // version + md.version().ifPresent(this::version); + + // main class + md.mainClass().ifPresent(this::mainClass); - if (md.mainClass().isPresent()) { - mainClass(md.mainClass().get()); - } + // hashes + JLMA.hashes(md).ifPresent(mh -> { + algorithm(mh.algorithm()); + mh.names().forEach(mn -> moduleHash(mn, mh.hashFor(mn))); + }); putModuleDescriptor(); } @@ -603,7 +655,7 @@ /* * Invoke Builder.provides(String service, Set providers) * - * Set providers = new HashSet<>(); + * Set providers = new LinkedHashSet<>(); * providers.add(impl); * : * : @@ -652,6 +704,22 @@ 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 @@ */ class StringSetBuilder { final Set names; + final boolean linked; int refCount; int localVarIndex; + + StringSetBuilder(Set names, boolean linked) { + this.names = names; + this.linked = linked; + } + StringSetBuilder(Set names) { - this.names = names; + this(names, false); } void increment() { @@ -704,11 +779,11 @@ "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", - "", "(I)V", false); + mv.visitMethodInsn(INVOKESPECIAL, cn, "", "(I)V", false); mv.visitVarInsn(ASTORE, index); for (String t : names) { diff -r 82b8d12a553f -r 06f3783b338f jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/plugin/Plugin.java --- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/plugin/Plugin.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/plugin/Plugin.java Tue May 03 11:45:56 2016 +0100 @@ -201,6 +201,8 @@ * 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 config) { } @@ -211,6 +213,9 @@ * * @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 config, PluginContext ctx) { configure(config); diff -r 82b8d12a553f -r 06f3783b338f jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/jlink.properties --- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/jlink.properties Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/jlink.properties Tue May 03 11:45:56 2016 +0100 @@ -33,9 +33,6 @@ main.opt.endian=\ \ --endian Byte order of generated jimage (default:native) -main.opt.genbom=\ -\ --genbom Generate a bom file containing jlink info - main.opt.saveopts=\ \ --saveopts Save jlink options in the given file diff -r 82b8d12a553f -r 06f3783b338f jdk/src/jdk.jlink/share/classes/jdk/tools/jmod/JmodTask.java --- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jmod/JmodTask.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jmod/JmodTask.java Tue May 03 11:45:56 2016 +0100 @@ -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.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; +import java.lang.module.ModuleDescriptor.Exports; +import java.lang.module.ModuleDescriptor.Provides; import java.lang.module.ModuleDescriptor.Requires; -import java.lang.module.ModuleDescriptor; 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.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.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.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 @@ } } - static void fail(Class 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 @@ enum Mode { CREATE, LIST, - DESCRIBE + DESCRIBE, + HASH }; static class Options { @@ -179,7 +171,8 @@ String osName; String osArch; String osVersion; - Pattern dependenciesToHash; + Pattern modulesToHash; + boolean dryrun; List excludes; } @@ -211,6 +204,9 @@ case DESCRIBE: ok = describe(); break; + case HASH: + ok = hashModules(); + break; default: throw new AssertionError("Unknown mode: " + options.mode.name()); } @@ -248,26 +244,8 @@ } } - private Map modulesToPath(Set modules) { - ModuleFinder finder = options.moduleFinder; - - Map modPaths = new HashMap<>(); - for (ModuleDescriptor m : modules) { - String name = m.name(); - - Optional 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 @@ .collect(joining(" ")); } + private static final JavaLangModuleAccess JLMA = SharedSecrets.getJavaLangModuleAccess(); + private boolean printModuleDescriptor(InputStream in) throws IOException { @@ -311,74 +291,45 @@ StringBuilder sb = new StringBuilder(); sb.append("\n").append(md.toNameAndVersion()); - List 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()); - }); - } - - List l = md.uses().stream().sorted().collect(toList()); - if (!l.isEmpty()) { - l.forEach(sv -> sb.append("\n uses ").append(sv)); - } + 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 exports = sortExports(md.exports()); - if (!exports.isEmpty()) { - exports.forEach(ex -> sb.append("\n exports ").append(ex)); - } + md.uses().stream().sorted() + .forEach(s -> sb.append("\n uses ").append(s)); - l = md.conceals().stream().sorted().collect(toList()); - if (!l.isEmpty()) { - l.forEach(p -> sb.append("\n conceals ").append(p)); - } + md.exports().stream() + .sorted(Comparator.comparing(Exports::source)) + .forEach(p -> sb.append("\n exports ").append(p)); - Map provides = md.provides(); - if (!provides.isEmpty()) { - provides.values().forEach(p -> - sb.append("\n provides ").append(p.service()) - .append(" with ") - .append(toString(p.providers()))); - } + md.conceals().stream().sorted() + .forEach(p -> sb.append("\n conceals ").append(p)); - Optional mc = md.mainClass(); - if (mc.isPresent()) - sb.append("\n main-class " + 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()))); - Optional osname = md.osName(); - if (osname.isPresent()) - sb.append("\n operating-system-name " + osname.get()); + md.mainClass().ifPresent(v -> sb.append("\n main-class " + v)); - Optional osarch = md.osArch(); - if (osarch.isPresent()) - sb.append("\n operating-system-architecture " + osarch.get()); - - Optional osversion = md.osVersion(); - if (osversion.isPresent()) - sb.append("\n operating-system-version " + osversion.get()); + md.osName().ifPresent(v -> sb.append("\n operating-system-name " + v)); - try { - Method m = ModuleDescriptor.class.getDeclaredMethod("hashes"); - m.setAccessible(true); - @SuppressWarnings("unchecked") - Optional optHashes = - (Optional) m.invoke(md); + md.osArch().ifPresent(v -> sb.append("\n operating-system-architecture " + v)); + + md.osVersion().ifPresent(v -> sb.append("\n operating-system-version " + v)); - 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); - } + 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)))); + out.println(sb.toString()); return true; } @@ -387,21 +338,6 @@ return false; } - static List sortExports(Set exports) { - Map map = - exports.stream() - .collect(toMap(ModuleDescriptor.Exports::source, - identity())); - List sources = exports.stream() - .map(ModuleDescriptor.Exports::source) - .sorted() - .collect(toList()); - - List 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 @@ 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 @@ } private class JmodFileWriter { - final ModuleFinder moduleFinder = options.moduleFinder; final List cmds = options.cmds; final List libs = options.libs; final List configs = options.configs; @@ -438,8 +374,8 @@ final String osName = options.osName; final String osArch = options.osArch; final String osVersion = options.osVersion; - final Pattern dependenciesToHash = options.dependenciesToHash; final List excludes = options.excludes; + final Hasher hasher = hasher(); JmodFileWriter() { } @@ -545,11 +481,13 @@ if (moduleVersion != null) extender.version(moduleVersion); - // --hash-dependencies - if (dependenciesToHash != null) { - String name = descriptor.name(); - Set 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 @@ } } - /** - * 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 moduleDependences) - throws IOException - { - Set descriptors = new HashSet<>(); - for (Requires md: moduleDependences) { - String dn = md.name(); - if (dependenciesToHash.matcher(dn).find()) { - try { - Optional omref = moduleFinder.find(dn); - if (!omref.isPresent()) { - throw new RuntimeException("Hashing module " + name - + " dependencies, unable to find module " + dn - + " on module path"); + private Hasher hasher() { + if (options.modulesToHash == null) + return null; + + try { + Supplier 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 find(String name) { + if (descriptor.name().equals(name)) + return Optional.of(mref); + else return Optional.empty(); } - descriptors.add(omref.get().descriptor()); - } catch (FindException x) { - throw new IOException("error reading module path", x); - } - } - } - Map map = modulesToPath(descriptors); - if (map.size() == 0) { - return null; - } else { - // use SHA-256 for now, easy to make this configurable if needed - return Hasher.generate(map, "SHA-256"); + @Override + public Set findAll() { + return Collections.singleton(mref); + } + }); + + return new Hasher(finder); + } catch (IOException e) { + throw new UncheckedIOException(e); } } @@ -765,6 +721,273 @@ } } + /** + * Compute and record hashes + */ + private class Hasher { + final ModuleFinder moduleFinder; + final Map moduleNameToPath; + final Set 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> graph = new HashMap<>(); + for (String root : modules) { + Deque deque = new ArrayDeque<>(); + deque.add(root); + Set 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 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> graph = transpose(); + + // find the modules that transitively depend upon the specified name + Deque deque = new ArrayDeque<>(); + deque.add(name); + Set mods = visitNodes(graph, deque); + + // filter modules matching the pattern specified --hash-modules + // as well as itself as the jmod file is being generated + Map 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 visitNodes(Map> graph, + Deque roots) { + Set 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> transpose() { + Map> transposedGraph = new HashMap<>(); + Deque deque = new ArrayDeque<>(modules); + + Set 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 @@ 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 @@ .withValuesSeparatedBy(File.pathSeparatorChar) .withValuesConvertedBy(DirPathConverter.INSTANCE); + OptionSpec dryrun + = parser.accepts("dry-run", getMessage("main.opt.dry-run")); + OptionSpec excludes = parser.accepts("exclude", getMessage("main.opt.exclude")) .withRequiredArg() .withValuesConvertedBy(new GlobConverter()); - OptionSpec hashDependencies - = parser.accepts("hash-dependencies", getMessage("main.opt.hash-dependencies")) + OptionSpec hashModules + = parser.accepts("hash-modules", getMessage("main.opt.hash-modules")) .withRequiredArg() .withValuesConvertedBy(new PatternConverter()); @@ -1049,6 +1276,8 @@ 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 @@ 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); diff -r 82b8d12a553f -r 06f3783b338f jdk/src/jdk.jlink/share/classes/jdk/tools/jmod/resources/jmod.properties --- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jmod/resources/jmod.properties Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jmod/resources/jmod.properties Tue May 03 11:45:56 2016 +0100 @@ -1,9 +1,9 @@ main.usage.summary=\ -Usage: {0} (create|list|describe) \n\ +Usage: {0} (create|list|describe|hash) \n\ use --help for a list of possible options main.usage=\ -Usage: {0} (create|list|describe) +Usage: {0} (create|list|describe|hash) error.prefix=Error: warn.prefix=Warning: @@ -14,6 +14,8 @@ \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.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.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.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} + diff -r 82b8d12a553f -r 06f3783b338f jdk/test/TEST.ROOT --- a/jdk/test/TEST.ROOT Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/test/TEST.ROOT Tue May 03 11:45:56 2016 +0100 @@ -26,9 +26,12 @@ # 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 diff -r 82b8d12a553f -r 06f3783b338f jdk/test/com/sun/corba/5036554/TestCorbaBug.sh --- a/jdk/test/com/sun/corba/5036554/TestCorbaBug.sh Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/test/com/sun/corba/5036554/TestCorbaBug.sh Tue May 03 11:45:56 2016 +0100 @@ -81,9 +81,9 @@ 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 diff -r 82b8d12a553f -r 06f3783b338f jdk/test/com/sun/corba/7130985/CorbaExceptionsCompileTest.java --- a/jdk/test/com/sun/corba/7130985/CorbaExceptionsCompileTest.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/test/com/sun/corba/7130985/CorbaExceptionsCompileTest.java Tue May 03 11:45:56 2016 +0100 @@ -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.*; diff -r 82b8d12a553f -r 06f3783b338f jdk/test/com/sun/corba/se/impl/io/HookPutFieldsTest.java --- a/jdk/test/com/sun/corba/se/impl/io/HookPutFieldsTest.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/test/com/sun/corba/se/impl/io/HookPutFieldsTest.java Tue May 03 11:45:56 2016 +0100 @@ -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; diff -r 82b8d12a553f -r 06f3783b338f jdk/test/com/sun/corba/se/impl/orb/SetDefaultORBTest.java --- a/jdk/test/com/sun/corba/se/impl/orb/SetDefaultORBTest.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/test/com/sun/corba/se/impl/orb/SetDefaultORBTest.java Tue May 03 11:45:56 2016 +0100 @@ -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 * */ diff -r 82b8d12a553f -r 06f3783b338f jdk/test/com/sun/net/httpserver/bugs/B6373555.java --- a/jdk/test/com/sun/net/httpserver/bugs/B6373555.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/test/com/sun/net/httpserver/bugs/B6373555.java Tue May 03 11:45:56 2016 +0100 @@ -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.*; diff -r 82b8d12a553f -r 06f3783b338f jdk/test/java/lang/invoke/VarargsArrayTest.java --- a/jdk/test/java/lang/invoke/VarargsArrayTest.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/test/java/lang/invoke/VarargsArrayTest.java Tue May 03 11:45:56 2016 +0100 @@ -37,7 +37,7 @@ * @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 */ diff -r 82b8d12a553f -r 06f3783b338f jdk/test/java/lang/module/ModuleFinderTest.java --- a/jdk/test/java/lang/module/ModuleFinderTest.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/test/java/lang/module/ModuleFinderTest.java Tue May 03 11:45:56 2016 +0100 @@ -340,7 +340,7 @@ */ 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 { @@ -361,6 +361,48 @@ /** + * 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 */ diff -r 82b8d12a553f -r 06f3783b338f jdk/test/java/lang/reflect/WeakPairMap/Driver.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/lang/reflect/WeakPairMap/Driver.java Tue May 03 11:45:56 2016 +0100 @@ -0,0 +1,35 @@ +/* + * 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. + */ + +/** + * @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) { + java.lang.reflect.WeakPairMapTest.main(args); + } +} diff -r 82b8d12a553f -r 06f3783b338f jdk/test/java/lang/reflect/WeakPairMap/java.base/java/lang/reflect/WeakPairMapTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/java/lang/reflect/WeakPairMap/java.base/java/lang/reflect/WeakPairMapTest.java Tue May 03 11:45:56 2016 +0100 @@ -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 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 boolean gcAndWaitRemoved(WeakPairMap 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); + } + } +} diff -r 82b8d12a553f -r 06f3783b338f jdk/test/java/util/ResourceBundle/Bug6299235Test.sh --- a/jdk/test/java/util/ResourceBundle/Bug6299235Test.sh Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/test/java/util/ResourceBundle/Bug6299235Test.sh Tue May 03 11:45:56 2016 +0100 @@ -68,7 +68,7 @@ ${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 ] diff -r 82b8d12a553f -r 06f3783b338f jdk/test/javax/crypto/Cipher/CipherStreamClose.java --- a/jdk/test/javax/crypto/Cipher/CipherStreamClose.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/test/javax/crypto/Cipher/CipherStreamClose.java Tue May 03 11:45:56 2016 +0100 @@ -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.*; diff -r 82b8d12a553f -r 06f3783b338f jdk/test/javax/rmi/PortableRemoteObject/ConcurrentHashMapTest.java --- a/jdk/test/javax/rmi/PortableRemoteObject/ConcurrentHashMapTest.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/test/javax/rmi/PortableRemoteObject/ConcurrentHashMapTest.java Tue May 03 11:45:56 2016 +0100 @@ -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 @@ // -Djava.naming.provider.url=iiop://localhost:1050 HelloServer List 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"); diff -r 82b8d12a553f -r 06f3783b338f jdk/test/javax/smartcardio/CommandAPDUTest.java --- a/jdk/test/javax/smartcardio/CommandAPDUTest.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/test/javax/smartcardio/CommandAPDUTest.java Tue May 03 11:45:56 2016 +0100 @@ -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; diff -r 82b8d12a553f -r 06f3783b338f jdk/test/javax/smartcardio/HistoricalBytes.java --- a/jdk/test/javax/smartcardio/HistoricalBytes.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/test/javax/smartcardio/HistoricalBytes.java Tue May 03 11:45:56 2016 +0100 @@ -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; diff -r 82b8d12a553f -r 06f3783b338f jdk/test/javax/smartcardio/ResponseAPDUTest.java --- a/jdk/test/javax/smartcardio/ResponseAPDUTest.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/test/javax/smartcardio/ResponseAPDUTest.java Tue May 03 11:45:56 2016 +0100 @@ -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.*; diff -r 82b8d12a553f -r 06f3783b338f jdk/test/javax/smartcardio/Serialize.java --- a/jdk/test/javax/smartcardio/Serialize.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/test/javax/smartcardio/Serialize.java Tue May 03 11:45:56 2016 +0100 @@ -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.*; diff -r 82b8d12a553f -r 06f3783b338f jdk/test/javax/smartcardio/TerminalFactorySpiTest.java --- a/jdk/test/javax/smartcardio/TerminalFactorySpiTest.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/test/javax/smartcardio/TerminalFactorySpiTest.java Tue May 03 11:45:56 2016 +0100 @@ -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; diff -r 82b8d12a553f -r 06f3783b338f jdk/test/javax/smartcardio/TestCardPermission.java --- a/jdk/test/javax/smartcardio/TestCardPermission.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/test/javax/smartcardio/TestCardPermission.java Tue May 03 11:45:56 2016 +0100 @@ -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.*; diff -r 82b8d12a553f -r 06f3783b338f jdk/test/javax/smartcardio/TestCommandAPDU.java --- a/jdk/test/javax/smartcardio/TestCommandAPDU.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/test/javax/smartcardio/TestCommandAPDU.java Tue May 03 11:45:56 2016 +0100 @@ -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.*; diff -r 82b8d12a553f -r 06f3783b338f jdk/test/javax/transaction/testng/Driver.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/javax/transaction/testng/Driver.java Tue May 03 11:45:56 2016 +0100 @@ -0,0 +1,36 @@ +/* + * 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. + */ + +/** + * @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 + */ diff -r 82b8d12a553f -r 06f3783b338f jdk/test/javax/transaction/testng/TEST.properties --- a/jdk/test/javax/transaction/testng/TEST.properties Wed Jul 05 21:39:33 2017 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,4 +0,0 @@ -# JDBC unit tests uses TestNG -TestNG.dirs= . -othervm.dirs= . - diff -r 82b8d12a553f -r 06f3783b338f jdk/test/javax/xml/bind/xjc/8032884/XjcOptionalPropertyTest.java --- a/jdk/test/javax/xml/bind/xjc/8032884/XjcOptionalPropertyTest.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/test/javax/xml/bind/xjc/8032884/XjcOptionalPropertyTest.java Tue May 03 11:45:56 2016 +0100 @@ -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 */ diff -r 82b8d12a553f -r 06f3783b338f jdk/test/javax/xml/jaxp/common/8035437/run.sh --- a/jdk/test/javax/xml/jaxp/common/8035437/run.sh Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/test/javax/xml/jaxp/common/8035437/run.sh Tue May 03 11:45:56 2016 +0100 @@ -34,12 +34,12 @@ -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 diff -r 82b8d12a553f -r 06f3783b338f jdk/test/javax/xml/soap/XmlTest.java --- a/jdk/test/javax/xml/soap/XmlTest.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/test/javax/xml/soap/XmlTest.java Tue May 03 11:45:56 2016 +0100 @@ -38,6 +38,8 @@ /* * @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 */ diff -r 82b8d12a553f -r 06f3783b338f jdk/test/javax/xml/soap/spi/SAAJFactoryTest.java --- a/jdk/test/javax/xml/soap/spi/SAAJFactoryTest.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/test/javax/xml/soap/spi/SAAJFactoryTest.java Tue May 03 11:45:56 2016 +0100 @@ -49,29 +49,29 @@ * 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 { diff -r 82b8d12a553f -r 06f3783b338f jdk/test/javax/xml/ws/8043129/MailTest.java --- a/jdk/test/javax/xml/ws/8043129/MailTest.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/test/javax/xml/ws/8043129/MailTest.java Tue May 03 11:45:56 2016 +0100 @@ -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; diff -r 82b8d12a553f -r 06f3783b338f jdk/test/javax/xml/ws/clientjar/TestWsImport.java --- a/jdk/test/javax/xml/ws/clientjar/TestWsImport.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/test/javax/xml/ws/clientjar/TestWsImport.java Tue May 03 11:45:56 2016 +0100 @@ -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; diff -r 82b8d12a553f -r 06f3783b338f jdk/test/javax/xml/ws/publish/WSTest.java --- a/jdk/test/javax/xml/ws/publish/WSTest.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/test/javax/xml/ws/publish/WSTest.java Tue May 03 11:45:56 2016 +0100 @@ -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; diff -r 82b8d12a553f -r 06f3783b338f jdk/test/javax/xml/ws/xsanymixed/Test.java --- a/jdk/test/javax/xml/ws/xsanymixed/Test.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/test/javax/xml/ws/xsanymixed/Test.java Tue May 03 11:45:56 2016 +0100 @@ -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; diff -r 82b8d12a553f -r 06f3783b338f jdk/test/sun/security/pkcs11/KeyStore/SecretKeysBasic.java --- a/jdk/test/sun/security/pkcs11/KeyStore/SecretKeysBasic.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/test/sun/security/pkcs11/KeyStore/SecretKeysBasic.java Tue May 03 11:45:56 2016 +0100 @@ -26,7 +26,6 @@ import java.security.*; import javax.crypto.*; import javax.crypto.spec.*; -import javax.xml.bind.DatatypeConverter; public class SecretKeysBasic extends PKCS11Test { @@ -131,8 +130,11 @@ 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"); } diff -r 82b8d12a553f -r 06f3783b338f jdk/test/sun/security/provider/PolicyFile/Modules.java --- a/jdk/test/sun/security/provider/PolicyFile/Modules.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/test/sun/security/provider/PolicyFile/Modules.java Tue May 03 11:45:56 2016 +0100 @@ -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; diff -r 82b8d12a553f -r 06f3783b338f jdk/test/tools/jar/modularJar/Basic.java --- a/jdk/test/tools/jar/modularJar/Basic.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/test/tools/jar/modularJar/Basic.java Tue May 03 11:45:56 2016 +0100 @@ -340,43 +340,43 @@ "--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"); + 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, - "--no-manifest", - "-C", modClasses.toString(), ".") - .assertSuccess(); - - modClasses = MODULE_CLASSES.resolve(BAR.moduleName); - modularJar = mp.resolve(BAR.moduleName + ".jar"); - jar("--create", - "--file=" + modularJar.toString(), - "--main-class=" + BAR.mainClass, - "--module-version=" + BAR.version, "--modulepath=" + mp.toString(), - "--hash-dependencies=" + "foo", // dependency on foo + "--hash-modules=" + "bar", "--no-manifest", "-C", modClasses.toString(), ".") .assertSuccess(); @@ -392,49 +392,49 @@ } @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 @@ 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 } diff -r 82b8d12a553f -r 06f3783b338f jdk/test/tools/jar/modularJar/src/bar/jdk/test/bar/Bar.java --- a/jdk/test/tools/jar/modularJar/src/bar/jdk/test/bar/Bar.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/test/tools/jar/modularJar/src/bar/jdk/test/bar/Bar.java Tue May 03 11:45:56 2016 +0100 @@ -30,7 +30,7 @@ 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 @@ Method m = ModuleDescriptor.class.getDeclaredMethod("hashes"); m.setAccessible(true); - Optional optHashes = - (Optional) m.invoke(md); + ModuleDescriptor foo = jdk.test.foo.Foo.class.getModule().getDescriptor(); + Optional oHashes = + (Optional) 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); diff -r 82b8d12a553f -r 06f3783b338f jdk/test/tools/jlink/ImageFileCreatorTest.java --- a/jdk/test/tools/jlink/ImageFileCreatorTest.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/test/tools/jlink/ImageFileCreatorTest.java Tue May 03 11:45:56 2016 +0100 @@ -214,14 +214,12 @@ } @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); } diff -r 82b8d12a553f -r 06f3783b338f jdk/test/tools/jlink/IntegrationTest.java --- a/jdk/test/tools/jlink/IntegrationTest.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/test/tools/jlink/IntegrationTest.java Tue May 03 11:45:56 2016 +0100 @@ -241,7 +241,7 @@ 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 @@ 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 @@ } // 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 @@ } // Image builder - DefaultImageBuilder builder = new DefaultImageBuilder(false, output); + DefaultImageBuilder builder = new DefaultImageBuilder(output); PluginsConfiguration plugins = new Jlink.PluginsConfiguration(lst, builder, null); boolean failed = false; diff -r 82b8d12a553f -r 06f3783b338f jdk/test/tools/jlink/JLink2Test.java --- a/jdk/test/tools/jlink/JLink2Test.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/test/tools/jlink/JLink2Test.java Tue May 03 11:45:56 2016 +0100 @@ -66,8 +66,6 @@ // 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 @@ 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 builtInPlugins = new ArrayList<>(); builtInPlugins.addAll(PluginRepository.getPlugins(Layer.boot())); diff -r 82b8d12a553f -r 06f3783b338f jdk/test/tools/jlink/JLinkTest.java --- a/jdk/test/tools/jlink/JLinkTest.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/test/tools/jlink/JLinkTest.java Tue May 03 11:45:56 2016 +0100 @@ -204,7 +204,7 @@ 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 diff -r 82b8d12a553f -r 06f3783b338f jdk/test/tools/jlink/hashes/HashesTest.java --- a/jdk/test/tools/jlink/hashes/HashesTest.java Wed Jul 05 21:39:33 2017 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -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() { - @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 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 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); - } - } -} diff -r 82b8d12a553f -r 06f3783b338f jdk/test/tools/jlink/hashes/newsrc/m2/module-info.java --- a/jdk/test/tools/jlink/hashes/newsrc/m2/module-info.java Wed Jul 05 21:39:33 2017 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,26 +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. - */ - -module m2 { - exports org.m2; -} diff -r 82b8d12a553f -r 06f3783b338f jdk/test/tools/jlink/hashes/newsrc/m2/org/m2/Util.java --- a/jdk/test/tools/jlink/hashes/newsrc/m2/org/m2/Util.java Wed Jul 05 21:39:33 2017 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,32 +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. - */ - -package org.m2; - -public class Util { - private Util() { } - - public static String timeOfDay() { - return "Time for a beer"; - } -} diff -r 82b8d12a553f -r 06f3783b338f jdk/test/tools/jlink/hashes/newsrc/not_matched/module-info.java --- a/jdk/test/tools/jlink/hashes/newsrc/not_matched/module-info.java Wed Jul 05 21:39:33 2017 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,26 +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. - */ - -module not_matched { - exports org.not_matched; -} diff -r 82b8d12a553f -r 06f3783b338f jdk/test/tools/jlink/hashes/newsrc/not_matched/org/not_matched/Name.java --- a/jdk/test/tools/jlink/hashes/newsrc/not_matched/org/not_matched/Name.java Wed Jul 05 21:39:33 2017 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,32 +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. - */ - -package org.not_matched; - -public class Name { - private Name() { } - - public static String name() { - return "new_module"; - } -} diff -r 82b8d12a553f -r 06f3783b338f jdk/test/tools/jlink/hashes/src/m1/module-info.java --- a/jdk/test/tools/jlink/hashes/src/m1/module-info.java Wed Jul 05 21:39:33 2017 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,27 +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. - */ - -module m1 { - requires m2; - requires not_matched; -} diff -r 82b8d12a553f -r 06f3783b338f jdk/test/tools/jlink/hashes/src/m1/org/m1/Main.java --- a/jdk/test/tools/jlink/hashes/src/m1/org/m1/Main.java Wed Jul 05 21:39:33 2017 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,34 +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. - */ - -package org.m1; - -import org.m2.Util; -import org.not_matched.Name; - -public class Main { - public static void main(String[] args) { - System.out.println(Util.timeOfDay()); - System.out.println(Name.name()); - } -} diff -r 82b8d12a553f -r 06f3783b338f jdk/test/tools/jlink/hashes/src/m2/module-info.java --- a/jdk/test/tools/jlink/hashes/src/m2/module-info.java Wed Jul 05 21:39:33 2017 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,26 +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. - */ - -module m2 { - exports org.m2; -} diff -r 82b8d12a553f -r 06f3783b338f jdk/test/tools/jlink/hashes/src/m2/org/m2/Util.java --- a/jdk/test/tools/jlink/hashes/src/m2/org/m2/Util.java Wed Jul 05 21:39:33 2017 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,32 +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. - */ - -package org.m2; - -public class Util { - private Util() { } - - public static String timeOfDay() { - return "Time for lunch"; - } -} diff -r 82b8d12a553f -r 06f3783b338f jdk/test/tools/jlink/hashes/src/not_matched/module-info.java --- a/jdk/test/tools/jlink/hashes/src/not_matched/module-info.java Wed Jul 05 21:39:33 2017 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,26 +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. - */ - -module not_matched { - exports org.not_matched; -} diff -r 82b8d12a553f -r 06f3783b338f jdk/test/tools/jlink/hashes/src/not_matched/org/not_matched/Name.java --- a/jdk/test/tools/jlink/hashes/src/not_matched/org/not_matched/Name.java Wed Jul 05 21:39:33 2017 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,32 +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. - */ - -package org.not_matched; - -public class Name { - private Name() { } - - public static String name() { - return "old_module"; - } -} diff -r 82b8d12a553f -r 06f3783b338f jdk/test/tools/jlink/plugins/FileCopierPluginTest.java --- a/jdk/test/tools/jlink/plugins/FileCopierPluginTest.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/test/tools/jlink/plugins/FileCopierPluginTest.java Tue May 03 11:45:56 2016 +0100 @@ -101,9 +101,8 @@ } 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"); diff -r 82b8d12a553f -r 06f3783b338f jdk/test/tools/jmod/JmodNegativeTest.java --- a/jdk/test/tools/jmod/JmodNegativeTest.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/test/tools/jmod/JmodNegativeTest.java Tue May 03 11:45:56 2016 +0100 @@ -76,7 +76,7 @@ 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 @@ 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 @@ } @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 @@ } @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 @@ 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 @@ 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 @@ Files.createFile(file); jmod("create", - "--hash-dependencies", ".*", + "--hash-modules", ".*", "--modulepath", file.toString(), jmod.toString()) .assertFailure() @@ -388,7 +384,7 @@ List> tasks = Arrays.asList( () -> jmod("create", - "--hash-dependencies", "anyPattern", + "--hash-modules", "anyPattern", "--modulepath", "doesNotExist", "output.jmod"), () -> jmod("create", @@ -436,7 +432,7 @@ List> tasks = Arrays.asList( () -> jmod("create", - "--hash-dependencies", "anyPattern", + "--hash-modules", "anyPattern", "--modulepath","empty" + pathSeparator + "doesNotExist", "output.jmod"), () -> jmod("create", diff -r 82b8d12a553f -r 06f3783b338f jdk/test/tools/jmod/hashes/HashesTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/tools/jmod/hashes/HashesTest.java Tue May 03 11:45:56 2016 +0100 @@ -0,0 +1,213 @@ +/** + * 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 module hashes + * @author Andrei Eremeev + * @library /lib/testlibrary + * @modules java.base/jdk.internal.module + * jdk.jlink/jdk.tools.jlink.internal + * jdk.jlink/jdk.tools.jmod + * jdk.compiler + * @build CompilerUtils + * @run testng HashesTest + */ + +import java.io.IOException; +import java.io.InputStream; +import java.lang.module.ModuleDescriptor; +import java.lang.module.ModuleFinder; +import java.lang.module.ModuleReader; +import java.lang.module.ModuleReference; +import java.lang.reflect.Method; +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.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +import jdk.internal.module.ConfigurableModuleFinder; +import jdk.internal.module.ModuleHashes; +import org.testng.annotations.BeforeTest; +import org.testng.annotations.Test; + +import static org.testng.Assert.*; + +public class HashesTest { + + private final Path testSrc = Paths.get(System.getProperty("test.src")); + private final Path modSrc = testSrc.resolve("src"); + private final Path mods = Paths.get("mods"); + private final Path jmods = Paths.get("jmods"); + private final String[] modules = new String[] { "m1", "m2", "m3"}; + + private static Method hashesMethod; + @BeforeTest + private void setup() throws Exception { + if (Files.exists(jmods)) { + deleteDirectory(jmods); + } + Files.createDirectories(jmods); + + // build m2, m3 required by m1 + compileModule("m2", modSrc); + jmod("m2"); + + compileModule("m3", modSrc); + jmod("m3"); + + // build m1 + compileModule("m1", modSrc); + // no hash is recorded since m1 has outgoing edges + jmod("m1", "--modulepath", jmods.toString(), "--hash-modules", ".*"); + + // compile org.bar and org.foo + compileModule("org.bar", modSrc); + compileModule("org.foo", modSrc); + + try { + hashesMethod = ModuleDescriptor.class.getDeclaredMethod("hashes"); + hashesMethod.setAccessible(true); + } catch (ReflectiveOperationException x) { + throw new InternalError(x); + } + } + + @Test + public void test() throws Exception { + for (String mn : modules) { + assertFalse(hashes(mn).isPresent()); + } + + // hash m1 in m2 + jmod("m2", "--modulepath", jmods.toString(), "--hash-modules", "m1"); + checkHashes(hashes("m2").get(), "m1"); + + // hash m1 in m2 + jmod("m2", "--modulepath", jmods.toString(), "--hash-modules", ".*"); + checkHashes(hashes("m2").get(), "m1"); + + // create m2.jmod with no hash + jmod("m2"); + // run jmod hash command to hash m1 in m2 and m3 + runJmod(Arrays.asList("hash", "--modulepath", jmods.toString(), + "--hash-modules", ".*")); + checkHashes(hashes("m2").get(), "m1"); + checkHashes(hashes("m3").get(), "m1"); + + jmod("org.bar"); + jmod("org.foo"); + + jmod("org.bar", "--modulepath", jmods.toString(), "--hash-modules", "org.*"); + checkHashes(hashes("org.bar").get(), "org.foo"); + + jmod("m3", "--modulepath", jmods.toString(), "--hash-modules", ".*"); + checkHashes(hashes("m3").get(), "org.foo", "org.bar", "m1"); + } + + private void checkHashes(ModuleHashes hashes, String... hashModules) { + assertTrue(hashes.names().equals(Set.of(hashModules))); + } + + private Optional hashes(String name) throws Exception { + ModuleFinder finder = ModuleFinder.of(jmods.resolve(name + ".jmod")); + if (finder instanceof ConfigurableModuleFinder) { + ((ConfigurableModuleFinder) finder) + .configurePhase(ConfigurableModuleFinder.Phase.LINK_TIME); + } + ModuleReference mref = finder.find(name).orElseThrow(RuntimeException::new); + ModuleReader reader = mref.open(); + try (InputStream in = reader.open("module-info.class").get()) { + ModuleDescriptor md = ModuleDescriptor.read(in); + Optional hashes = + (Optional) hashesMethod.invoke(md); + System.out.format("hashes in module %s %s%n", name, + hashes.isPresent() ? "present" : "absent"); + if (hashes.isPresent()) { + hashes.get().names().stream() + .sorted() + .forEach(n -> System.out.format(" %s %s%n", n, hashes.get().hashFor(n))); + } + return hashes; + } finally { + reader.close(); + } + } + + private void deleteDirectory(Path dir) throws IOException { + Files.walkFileTree(dir, new SimpleFileVisitor() { + @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 compileModule(String moduleName, Path src) throws IOException { + Path msrc = src.resolve(moduleName); + assertTrue(CompilerUtils.compile(msrc, mods, "-modulesourcepath", src.toString())); + } + + private void jmod(String moduleName, String... options) throws IOException { + Path mclasses = mods.resolve(moduleName); + Path outfile = jmods.resolve(moduleName + ".jmod"); + List args = new ArrayList<>(); + args.add("create"); + Collections.addAll(args, options); + Collections.addAll(args, "--class-path", mclasses.toString(), + outfile.toString()); + + if (Files.exists(outfile)) + Files.delete(outfile); + + runJmod(args); + } + + private void runJmod(List args) { + 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); + } + } +} diff -r 82b8d12a553f -r 06f3783b338f jdk/test/tools/jmod/hashes/src/m1/module-info.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/tools/jmod/hashes/src/m1/module-info.java Tue May 03 11:45:56 2016 +0100 @@ -0,0 +1,27 @@ +/* + * 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. + */ + +module m1 { + requires m2; + requires m3; +} diff -r 82b8d12a553f -r 06f3783b338f jdk/test/tools/jmod/hashes/src/m1/org/m1/Main.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/tools/jmod/hashes/src/m1/org/m1/Main.java Tue May 03 11:45:56 2016 +0100 @@ -0,0 +1,34 @@ +/* + * 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. + */ + +package org.m1; + +import org.m2.Util; +import org.m3.Name; + +public class Main { + public static void main(String[] args) { + System.out.println(Util.timeOfDay()); + System.out.println(Name.name()); + } +} diff -r 82b8d12a553f -r 06f3783b338f jdk/test/tools/jmod/hashes/src/m2/module-info.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/tools/jmod/hashes/src/m2/module-info.java Tue May 03 11:45:56 2016 +0100 @@ -0,0 +1,26 @@ +/* + * 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. + */ + +module m2 { + exports org.m2; +} diff -r 82b8d12a553f -r 06f3783b338f jdk/test/tools/jmod/hashes/src/m2/org/m2/Util.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/tools/jmod/hashes/src/m2/org/m2/Util.java Tue May 03 11:45:56 2016 +0100 @@ -0,0 +1,32 @@ +/* + * 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. + */ + +package org.m2; + +public class Util { + private Util() { } + + public static String timeOfDay() { + return "Time for lunch"; + } +} diff -r 82b8d12a553f -r 06f3783b338f jdk/test/tools/jmod/hashes/src/m3/module-info.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/tools/jmod/hashes/src/m3/module-info.java Tue May 03 11:45:56 2016 +0100 @@ -0,0 +1,26 @@ +/* + * 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. + */ + +module m3 { + exports org.m3; +} diff -r 82b8d12a553f -r 06f3783b338f jdk/test/tools/jmod/hashes/src/m3/org/m3/Name.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/tools/jmod/hashes/src/m3/org/m3/Name.java Tue May 03 11:45:56 2016 +0100 @@ -0,0 +1,32 @@ +/* + * 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. + */ + +package org.m3; + +public class Name { + private Name() { } + + public static String name() { + return "m3"; + } +} diff -r 82b8d12a553f -r 06f3783b338f jdk/test/tools/jmod/hashes/src/org.bar/module-info.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/tools/jmod/hashes/src/org.bar/module-info.java Tue May 03 11:45:56 2016 +0100 @@ -0,0 +1,26 @@ +/* + * 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. + */ + +module org.bar { + requires public m1; +} diff -r 82b8d12a553f -r 06f3783b338f jdk/test/tools/jmod/hashes/src/org.foo/module-info.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/tools/jmod/hashes/src/org.foo/module-info.java Tue May 03 11:45:56 2016 +0100 @@ -0,0 +1,26 @@ +/* + * 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. + */ + +module org.foo { + requires public org.bar; +} diff -r 82b8d12a553f -r 06f3783b338f jdk/test/tools/launcher/ToolsOpts.java --- a/jdk/test/tools/launcher/ToolsOpts.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/test/tools/launcher/ToolsOpts.java Tue May 03 11:45:56 2016 +0100 @@ -151,29 +151,26 @@ init(); TestResult tr; int jpos = -1; + String xPatch = "-J-Xpatch:jdk.compiler=jdk.compiler"; for (String arg[] : optionPatterns) { jpos = indexOfJoption(arg); //Build a cmd string for output in results reporting. - String cmdString = javacCmd + " -J-Xpatch:."; + String cmdString = javacCmd + " " + xPatch; for (String opt : arg) { cmdString = cmdString.concat(" " + opt); } switch (arg.length) { case 1: - tr = doExec(javacCmd, "-J-Xpatch:.", - arg[0]); + tr = doExec(javacCmd, xPatch, arg[0]); break; case 2: - tr = doExec(javacCmd, "-J-Xpatch:.", - arg[0], arg[1]); + tr = doExec(javacCmd, xPatch, arg[0], arg[1]); break; case 3: - tr = doExec(javacCmd, "-J-Xpatch:.", - arg[0], arg[1], arg[2]); + tr = doExec(javacCmd, xPatch, arg[0], arg[1], arg[2]); break; case 4: - tr = doExec(javacCmd, "-J-Xpatch:.", - arg[0], arg[1], arg[2], arg[3]); + tr = doExec(javacCmd, xPatch, arg[0], arg[1], arg[2], arg[3]); break; default: tr = null; diff -r 82b8d12a553f -r 06f3783b338f jdk/test/tools/launcher/modules/addmods/AddModsTest.java --- a/jdk/test/tools/launcher/modules/addmods/AddModsTest.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/test/tools/launcher/modules/addmods/AddModsTest.java Tue May 03 11:45:56 2016 +0100 @@ -47,120 +47,179 @@ private static final String TEST_SRC = System.getProperty("test.src"); private static final Path SRC_DIR = Paths.get(TEST_SRC, "src"); - private static final Path MODS_DIR = Paths.get("mods"); - - // the module name of the library module - private static final String LIB_MODULE = "lib"; + private static final Path MODS1_DIR = Paths.get("mods1"); + private static final Path MODS2_DIR = Paths.get("mods2"); - // application source directory - private static final String APP_SRC = "app"; + // test module / main class + private static final String TEST_MODULE = "test"; + private static final String TEST_MAIN_CLASS = "test.Main"; + private static final String TEST_MID = TEST_MODULE + "/" + TEST_MAIN_CLASS; - // application is compiled to classes - private static final Path CLASSES_DIR = Paths.get("classes"); - - // application main class - private static final String MAIN_CLASS = "app.Main"; + // logger module + private static final String LOGGER_MODULE = "logger"; @BeforeTest public void compile() throws Exception { - - // javac -d mods/$LIB_MODULE src/$LIB_MODULE/** + // javac -d mods1/test src/test/** boolean compiled = CompilerUtils.compile( - SRC_DIR.resolve(LIB_MODULE), - MODS_DIR.resolve(LIB_MODULE) + SRC_DIR.resolve(TEST_MODULE), + MODS1_DIR.resolve(TEST_MODULE) ); - assertTrue(compiled, "library module did not compile"); + assertTrue(compiled, "test did not compile"); - // javac -d classes -mp mods src/$APP_DIR/** - compiled = CompilerUtils.compile( - SRC_DIR.resolve(APP_SRC), - CLASSES_DIR, - "-mp", MODS_DIR.toString(), - "-addmods", LIB_MODULE + // javac -d mods1/logger src/logger/** + compiled= CompilerUtils.compile( + SRC_DIR.resolve(LOGGER_MODULE), + MODS2_DIR.resolve(LOGGER_MODULE) ); - assertTrue(compiled, "app did not compile"); + assertTrue(compiled, "test did not compile"); } /** - * Basic test of -addmods ALL-SYSTEM, using the output of -listmods to - * check that the a sample of the system modules are resolved. + * Basic test of -addmods ALL-DEFAULT. Module java.sql should be + * resolved and the types in that module should be visible. */ - public void testAddSystemModules() throws Exception { - - executeTestJava("-addmods", "ALL-SYSTEM", - "-listmods", - "-m", "java.base") - .outputTo(System.out) - .errorTo(System.out) - .shouldContain("java.sql") - .shouldContain("java.corba"); + public void testAddDefaultModules1() throws Exception { - // no exit value to check as -m java.base will likely fail - } - - - /** - * Run application on class path that makes use of module on the - * application module path. Uses {@code -addmods lib} - */ - public void testRunWithAddMods() throws Exception { - - // java -mp mods -addmods lib -cp classes app.Main + // java -addmods ALL-DEFAULT -mp mods1 -m test ... int exitValue - = executeTestJava("-mp", MODS_DIR.toString(), - "-addmods", LIB_MODULE, - "-cp", CLASSES_DIR.toString(), - MAIN_CLASS) + = executeTestJava("-mp", MODS1_DIR.toString(), + "-addmods", "ALL-DEFAULT", + "-m", TEST_MID, + "java.sql.Connection") .outputTo(System.out) .errorTo(System.out) .getExitValue(); assertTrue(exitValue == 0); - } /** - * Run application on class path that makes use of module on the - * application module path. Uses {@code -addmods ALL-MODULE-PATH}. + * Basic test of -addmods ALL-DEFAULT. Module java.annotations.common + * should not resolved and so the types in that module should not be + * visible. */ - public void testAddAllModulePath() throws Exception { + public void testAddDefaultModules2() throws Exception { + + // java -addmods ALL-DEFAULT -mp mods1 -m test ... + int exitValue + = executeTestJava("-mp", MODS1_DIR.toString(), + "-addmods", "ALL-DEFAULT", + "-m", TEST_MID, + "javax.annotation.Generated") + .outputTo(System.out) + .errorTo(System.out) + .shouldContain("ClassNotFoundException") + .getExitValue(); - // java -mp mods -addmods lib -cp classes app.Main + assertTrue(exitValue != 0); + } + + /** + * Basic test of -addmods ALL-SYSTEM. All system modules should be resolved + * and thus all types in those modules should be visible. + */ + public void testAddSystemModules() throws Exception { + + // java -addmods ALL-SYSTEM -mp mods1 -m test ... int exitValue - = executeTestJava("-mp", MODS_DIR.toString(), - "-addmods", "ALL-MODULE-PATH", - "-cp", CLASSES_DIR.toString(), - MAIN_CLASS) + = executeTestJava("-mp", MODS1_DIR.toString(), + "-addmods", "ALL-SYSTEM", + "-m", TEST_MID, + "java.sql.Connection", + "javax.annotation.Generated") .outputTo(System.out) .errorTo(System.out) .getExitValue(); assertTrue(exitValue == 0); - } /** - * Run application on class path that makes use of module on the - * application module path. Does not use -addmods and so will - * fail at run-time. + * Run test on class path to load a type in a module on the application + * module path, uses {@code -addmods logger}. */ - public void testRunMissingAddMods() throws Exception { + public void testRunWithAddMods() throws Exception { - // java -mp mods -cp classes app.Main + // java -mp mods -addmods logger -cp classes test.Main + String classpath = MODS1_DIR.resolve(TEST_MODULE).toString(); + String modulepath = MODS2_DIR.toString(); int exitValue - = executeTestJava("-mp", MODS_DIR.toString(), - "-cp", CLASSES_DIR.toString(), - MAIN_CLASS) + = executeTestJava("-mp", modulepath, + "-addmods", LOGGER_MODULE, + "-cp", classpath, + TEST_MAIN_CLASS, + "logger.Logger") .outputTo(System.out) .errorTo(System.out) .getExitValue(); - // CNFE or other error/exception - assertTrue(exitValue != 0); + assertTrue(exitValue == 0); + } + + /** + * Run application on class path that makes use of module on the + * application module path. Does not use -addmods and so should + * fail at run-time. + */ + public void testRunMissingAddMods() throws Exception { + + // java -mp mods -cp classes test.Main + String classpath = MODS1_DIR.resolve(TEST_MODULE).toString(); + String modulepath = MODS1_DIR.toString(); + int exitValue + = executeTestJava("-mp", modulepath, + "-cp", classpath, + TEST_MAIN_CLASS, + "logger.Logger") + .outputTo(System.out) + .errorTo(System.out) + .shouldContain("ClassNotFoundException") + .getExitValue(); + + assertTrue(exitValue != 0); + } + + /** + * Run test on class path to load a type in a module on the application + * module path, uses {@code -addmods ALL-MODULE-PATH}. + */ + public void testAddAllModulePath() throws Exception { + + // java -mp mods -addmods ALL-MODULE-PATH -cp classes test.Main + String classpath = MODS1_DIR.resolve(TEST_MODULE).toString(); + String modulepath = MODS1_DIR.toString(); + int exitValue + = executeTestJava("-mp", modulepath, + "-addmods", "ALL-MODULE-PATH", + "-cp", classpath, + TEST_MAIN_CLASS) + .outputTo(System.out) + .errorTo(System.out) + .getExitValue(); + + assertTrue(exitValue == 0); + } + + + /** + * Test {@code -addmods ALL-MODULE-PATH} without {@code -modulepath}. + */ + public void testAddAllModulePathWithNoModulePath() throws Exception { + + // java -addmods ALL-MODULE-PATH -version + int exitValue + = executeTestJava("-addmods", "ALL-MODULE-PATH", + "-version") + .outputTo(System.out) + .errorTo(System.out) + .getExitValue(); + + assertTrue(exitValue == 0); } @@ -169,18 +228,17 @@ */ public void testRunWithBadAddMods() throws Exception { - // java -mp mods -addmods,DoesNotExist lib -cp classes app.Main + // java -mp mods -addmods DoesNotExist -m test ... int exitValue - = executeTestJava("-mp", MODS_DIR.toString(), - "-addmods", LIB_MODULE + ",DoesNotExist", - "-cp", CLASSES_DIR.toString(), - MAIN_CLASS) + = executeTestJava("-mp", MODS1_DIR.toString(), + "-addmods", "DoesNotExist", + "-m", TEST_MID) .outputTo(System.out) .errorTo(System.out) + .shouldContain("DoesNotExist") .getExitValue(); assertTrue(exitValue != 0); - } } diff -r 82b8d12a553f -r 06f3783b338f jdk/test/tools/launcher/modules/addmods/src/app/Main.java --- a/jdk/test/tools/launcher/modules/addmods/src/app/Main.java Wed Jul 05 21:39:33 2017 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2014, 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 app; - -import jdk.lib.Util; - -public class Main { - public static void main(String[] args) { - Object obj = Util.makeObject(); - } -} diff -r 82b8d12a553f -r 06f3783b338f jdk/test/tools/launcher/modules/addmods/src/lib/jdk/lib/Util.java --- a/jdk/test/tools/launcher/modules/addmods/src/lib/jdk/lib/Util.java Wed Jul 05 21:39:33 2017 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2014, 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 jdk.lib; - -public class Util { - private Util() { } - - public static Object makeObject() { - return new Object(); - } -} diff -r 82b8d12a553f -r 06f3783b338f jdk/test/tools/launcher/modules/addmods/src/lib/module-info.java --- a/jdk/test/tools/launcher/modules/addmods/src/lib/module-info.java Wed Jul 05 21:39:33 2017 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2014, 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. - */ - -module lib { - exports jdk.lib; -} diff -r 82b8d12a553f -r 06f3783b338f jdk/test/tools/launcher/modules/addmods/src/logger/logger/Logger.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/tools/launcher/modules/addmods/src/logger/logger/Logger.java Tue May 03 11:45:56 2016 +0100 @@ -0,0 +1,30 @@ +/* + * 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 logger; + +/** + * No-op user module for use by the {@code java -addmods} tests. + */ +public class Logger { +} diff -r 82b8d12a553f -r 06f3783b338f jdk/test/tools/launcher/modules/addmods/src/logger/module-info.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/tools/launcher/modules/addmods/src/logger/module-info.java Tue May 03 11:45:56 2016 +0100 @@ -0,0 +1,24 @@ +/** + * 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. + */ + +module logger { } diff -r 82b8d12a553f -r 06f3783b338f jdk/test/tools/launcher/modules/addmods/src/test/module-info.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/tools/launcher/modules/addmods/src/test/module-info.java Tue May 03 11:45:56 2016 +0100 @@ -0,0 +1,24 @@ +/** + * 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. + */ + +module test { } diff -r 82b8d12a553f -r 06f3783b338f jdk/test/tools/launcher/modules/addmods/src/test/test/Main.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/test/tools/launcher/modules/addmods/src/test/test/Main.java Tue May 03 11:45:56 2016 +0100 @@ -0,0 +1,37 @@ +/* + * 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 test; + +/** + * Invoked by tests for the {@code java -addmods} option to check that types + * are visible. + */ +public class Main { + public static void main(String[] args) throws Exception { + for (String cn : args) { + Class c = Class.forName(cn); + System.out.println("Loaded: " + c); + } + } +} diff -r 82b8d12a553f -r 06f3783b338f jdk/test/tools/launcher/modules/addreads/AddReadsTest.java --- a/jdk/test/tools/launcher/modules/addreads/AddReadsTest.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/test/tools/launcher/modules/addreads/AddReadsTest.java Tue May 03 11:45:56 2016 +0100 @@ -25,7 +25,7 @@ * @test * @library /lib/testlibrary * @modules jdk.compiler - * @build AddReadsTest CompilerUtils jdk.testlibrary.* + * @build AddReadsTest CompilerUtils JarUtils jdk.testlibrary.* * @run testng AddReadsTest * @summary Basic tests for java -XaddReads */ diff -r 82b8d12a553f -r 06f3783b338f jdk/test/tools/launcher/modules/patch/PatchTest.java --- a/jdk/test/tools/launcher/modules/patch/PatchTest.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/test/tools/launcher/modules/patch/PatchTest.java Tue May 03 11:45:56 2016 +0100 @@ -25,7 +25,7 @@ * @test * @library /lib/testlibrary * @modules jdk.compiler - * @build PatchTest CompilerUtils jdk.testlibrary.* + * @build PatchTest CompilerUtils JarUtils jdk.testlibrary.* * @run testng PatchTest * @summary Basic test for -Xpatch */ @@ -72,6 +72,9 @@ private static final Path SRC2_DIR = Paths.get(TEST_SRC, "src2"); private static final Path PATCHES2_DIR = Paths.get("patches2"); + // destination directory for patches packaged as JAR files + private static final Path PATCHES_DIR = Paths.get("patches"); + // the classes overridden or added with -Xpatch private static final String[] CLASSES = { @@ -95,7 +98,7 @@ @BeforeTest - public void compile() throws Exception { + public void setup() throws Exception { // javac -d mods/test src/test/** boolean compiled= CompilerUtils.compile(SRC_DIR.resolve("test"), @@ -103,36 +106,40 @@ assertTrue(compiled, "classes did not compile"); // javac -Xmodule:$MODULE -d patches1/$MODULE patches1/$MODULE/** + // jar cf patches/$MODULE-1.jar -C patches1/$MODULE . for (Path src : Files.newDirectoryStream(SRC1_DIR)) { Path output = PATCHES1_DIR.resolve(src.getFileName()); String mn = src.getFileName().toString(); compiled = CompilerUtils.compile(src, output, "-Xmodule:" + mn); assertTrue(compiled, "classes did not compile"); + JarUtils.createJarFile(PATCHES_DIR.resolve(mn + "-1.jar"), output); } // javac -Xmodule:$MODULE -d patches2/$MODULE patches2/$MODULE/** + // jar cf patches/$MODULE-2.jar -C patches2/$MODULE . for (Path src : Files.newDirectoryStream(SRC2_DIR)) { Path output = PATCHES2_DIR.resolve(src.getFileName()); String mn = src.getFileName().toString(); compiled = CompilerUtils.compile(src, output, "-Xmodule:" + mn); assertTrue(compiled, "classes did not compile"); + JarUtils.createJarFile(PATCHES_DIR.resolve(mn + "-2.jar"), output); } } /** - * Run the test with -Xpatch + * Run test with patches to java.base, jdk.naming.dns and jdk.compiler */ - public void testRunWithXPatch() throws Exception { - - // value for -Xpatch - String patchPath = PATCHES1_DIR + File.pathSeparator + PATCHES2_DIR; - + void runTest(String basePatches, String dnsPatches, String compilerPatches) + throws Exception + { // the argument to the test is the list of classes overridden or added String arg = Stream.of(CLASSES).collect(Collectors.joining(",")); int exitValue - = executeTestJava("-Xpatch:" + patchPath, + = executeTestJava("-Xpatch:java.base=" + basePatches, + "-Xpatch:jdk.naming.dns=" + dnsPatches, + "-Xpatch:jdk.compiler=" + compilerPatches, "-XaddExports:java.base/java.lang2=test", "-XaddExports:jdk.naming.dns/com.sun.jndi.dns=test", "-XaddExports:jdk.naming.dns/com.sun.jndi.dns2=test", @@ -145,6 +152,44 @@ .getExitValue(); assertTrue(exitValue == 0); + } + + + /** + * Run test with -Xpatch and exploded patches + */ + public void testWithExplodedPatches() throws Exception { + + // patches1/java.base:patches2/java.base + String basePatches = PATCHES1_DIR.resolve("java.base") + + File.pathSeparator + PATCHES2_DIR.resolve("java.base"); + + String dnsPatches = PATCHES1_DIR.resolve("jdk.naming.dns") + + File.pathSeparator + PATCHES2_DIR.resolve("jdk.naming.dns"); + + String compilerPatches = PATCHES1_DIR.resolve("jdk.compiler") + + File.pathSeparator + PATCHES2_DIR.resolve("jdk.compiler"); + + runTest(basePatches, dnsPatches, compilerPatches); + } + + + /** + * Run test with -Xpatch and patches in JAR files + */ + public void testWitJarPatches() throws Exception { + + // patches/java.base-1.jar:patches/java-base-2.jar + String basePatches = PATCHES_DIR.resolve("java.base-1.jar") + + File.pathSeparator + PATCHES_DIR.resolve("java.base-2.jar"); + + String dnsPatches = PATCHES_DIR.resolve("jdk.naming.dns-1.jar") + + File.pathSeparator + PATCHES_DIR.resolve("jdk.naming.dns-2.jar"); + + String compilerPatches = PATCHES_DIR.resolve("jdk.compiler-1.jar") + + File.pathSeparator + PATCHES_DIR.resolve("jdk.compiler-2.jar"); + + runTest(basePatches, dnsPatches, compilerPatches); } diff -r 82b8d12a553f -r 06f3783b338f jdk/test/tools/lib/tests/JImageGenerator.java --- a/jdk/test/tools/lib/tests/JImageGenerator.java Wed Jul 05 21:39:33 2017 +0200 +++ b/jdk/test/tools/lib/tests/JImageGenerator.java Tue May 03 11:45:56 2016 +0100 @@ -113,7 +113,7 @@ private static final String CMDS_OPTION = "--cmds"; private static final String CONFIG_OPTION = "--config"; - private static final String HASH_DEPENDENCIES_OPTION = "--hash-dependencies"; + private static final String HASH_MODULES_OPTION = "--hash-modules"; private static final String LIBS_OPTION = "--libs"; private static final String MODULE_VERSION_OPTION = "--module-version"; @@ -347,7 +347,7 @@ private final List jmods = new ArrayList<>(); private final List options = new ArrayList<>(); private Path output; - private String hashDependencies; + private String hashModules; private String mainClass; private String moduleVersion; @@ -356,8 +356,8 @@ return this; } - public JModTask hashDependencies(String hash) { - this.hashDependencies = hash; + public JModTask hashModules(String hash) { + this.hashModules = hash; return this; } @@ -430,9 +430,9 @@ options.add(CONFIG_OPTION); options.add(toPath(config)); } - if (hashDependencies != null) { - options.add(HASH_DEPENDENCIES_OPTION); - options.add(hashDependencies); + if (hashModules != null) { + options.add(HASH_MODULES_OPTION); + options.add(hashModules); } if (mainClass != null) { options.add(MAIN_CLASS_OPTION);