changeset 14357:5c412978857d

8154956: Module system implementation refresh (4/2016) Reviewed-by: alanb, mchung, chegar, redestad Contributed-by: alan.bateman@oracle.com, mandy.chung@oracle.com, erik.joelsson@oracle.com, chris.hegarty@oracle.com, peter.levart@gmail.com, sundararajan.athijegannathan@oracle.com
author alanb
date Tue, 03 May 2016 09:09:57 +0100
parents 56379812ec5e
children a8818f3e8c52
files make/Tools.gmk make/gendata/GendataBreakIterator.gmk make/launcher/Launcher-java.desktop.gmk make/launcher/Launcher-java.scripting.gmk make/launcher/Launcher-jdk.compiler.gmk make/launcher/Launcher-jdk.javadoc.gmk make/launcher/Launcher-jdk.jlink.gmk make/launcher/Launcher-jdk.scripting.nashorn.shell.gmk src/java.base/share/classes/java/lang/ClassLoader.java src/java.base/share/classes/java/lang/System.java src/java.base/share/classes/java/lang/module/Configuration.java src/java.base/share/classes/java/lang/module/ModuleDescriptor.java src/java.base/share/classes/java/lang/module/ModuleFinder.java src/java.base/share/classes/java/lang/module/ModuleInfo.java src/java.base/share/classes/java/lang/module/ModulePath.java src/java.base/share/classes/java/lang/module/ModuleReader.java src/java.base/share/classes/java/lang/module/ModuleReference.java src/java.base/share/classes/java/lang/module/ModuleReferences.java src/java.base/share/classes/java/lang/module/Resolver.java src/java.base/share/classes/java/lang/module/SystemModuleFinder.java src/java.base/share/classes/java/lang/reflect/Layer.java src/java.base/share/classes/java/lang/reflect/Module.java src/java.base/share/classes/java/lang/reflect/WeakPairMap.java src/java.base/share/classes/java/util/ServiceLoader.java src/java.base/share/classes/jdk/internal/loader/BootLoader.java src/java.base/share/classes/jdk/internal/loader/BuiltinClassLoader.java src/java.base/share/classes/jdk/internal/misc/JavaLangModuleAccess.java src/java.base/share/classes/jdk/internal/misc/JavaLangReflectModuleAccess.java src/java.base/share/classes/jdk/internal/module/Builder.java src/java.base/share/classes/jdk/internal/module/ClassFileAttributes.java src/java.base/share/classes/jdk/internal/module/Hasher.java src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java src/java.base/share/classes/jdk/internal/module/ModuleHashes.java src/java.base/share/classes/jdk/internal/module/ModuleInfoExtender.java src/java.base/share/classes/jdk/internal/module/ModuleInfoWriter.java src/java.base/share/classes/jdk/internal/module/ModulePatcher.java src/java.base/share/classes/jdk/internal/module/Modules.java src/java.base/share/classes/jdk/internal/module/ServicesCatalog.java src/java.base/share/classes/jdk/internal/module/SystemModules.java src/java.base/share/classes/module-info.java src/java.base/share/classes/sun/launcher/resources/launcher.properties src/jdk.jartool/share/classes/sun/tools/jar/GNUStyleOptions.java src/jdk.jartool/share/classes/sun/tools/jar/Main.java src/jdk.jartool/share/classes/sun/tools/jar/resources/jar.properties src/jdk.jlink/share/classes/jdk/tools/jlink/builder/DefaultImageBuilder.java src/jdk.jlink/share/classes/jdk/tools/jlink/builder/ImageBuilder.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImageFileCreator.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImagePluginConfiguration.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImagePluginStack.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/TaskHelper.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/packager/AppRuntimeImageBuilder.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/DefaultCompressPlugin.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/ExcludeVMPlugin.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/IncludeLocalesPlugin.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/OptimizationPlugin.java src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/SystemModuleDescriptorPlugin.java src/jdk.jlink/share/classes/jdk/tools/jlink/plugin/Plugin.java src/jdk.jlink/share/classes/jdk/tools/jlink/resources/jlink.properties src/jdk.jlink/share/classes/jdk/tools/jmod/JmodTask.java src/jdk.jlink/share/classes/jdk/tools/jmod/resources/jmod.properties test/TEST.ROOT test/com/sun/corba/5036554/TestCorbaBug.sh test/com/sun/corba/7130985/CorbaExceptionsCompileTest.java test/com/sun/corba/se/impl/io/HookPutFieldsTest.java test/com/sun/corba/se/impl/orb/SetDefaultORBTest.java test/com/sun/net/httpserver/bugs/B6373555.java test/java/lang/invoke/VarargsArrayTest.java test/java/lang/module/ModuleFinderTest.java test/java/lang/reflect/WeakPairMap/Driver.java test/java/lang/reflect/WeakPairMap/java.base/java/lang/reflect/WeakPairMapTest.java test/java/util/ResourceBundle/Bug6299235Test.sh test/javax/crypto/Cipher/CipherStreamClose.java test/javax/rmi/PortableRemoteObject/ConcurrentHashMapTest.java test/javax/smartcardio/CommandAPDUTest.java test/javax/smartcardio/HistoricalBytes.java test/javax/smartcardio/ResponseAPDUTest.java test/javax/smartcardio/Serialize.java test/javax/smartcardio/TerminalFactorySpiTest.java test/javax/smartcardio/TestCardPermission.java test/javax/smartcardio/TestCommandAPDU.java test/javax/transaction/testng/Driver.java test/javax/transaction/testng/TEST.properties test/javax/xml/bind/xjc/8032884/XjcOptionalPropertyTest.java test/javax/xml/jaxp/common/8035437/run.sh test/javax/xml/soap/XmlTest.java test/javax/xml/soap/spi/SAAJFactoryTest.java test/javax/xml/ws/8043129/MailTest.java test/javax/xml/ws/clientjar/TestWsImport.java test/javax/xml/ws/publish/WSTest.java test/javax/xml/ws/xsanymixed/Test.java test/sun/security/pkcs11/KeyStore/SecretKeysBasic.java test/sun/security/provider/PolicyFile/Modules.java test/tools/jar/modularJar/Basic.java test/tools/jar/modularJar/src/bar/jdk/test/bar/Bar.java test/tools/jlink/ImageFileCreatorTest.java test/tools/jlink/IntegrationTest.java test/tools/jlink/JLink2Test.java test/tools/jlink/JLinkTest.java test/tools/jlink/hashes/HashesTest.java test/tools/jlink/hashes/newsrc/m2/module-info.java test/tools/jlink/hashes/newsrc/m2/org/m2/Util.java test/tools/jlink/hashes/newsrc/not_matched/module-info.java test/tools/jlink/hashes/newsrc/not_matched/org/not_matched/Name.java test/tools/jlink/hashes/src/m1/module-info.java test/tools/jlink/hashes/src/m1/org/m1/Main.java test/tools/jlink/hashes/src/m2/module-info.java test/tools/jlink/hashes/src/m2/org/m2/Util.java test/tools/jlink/hashes/src/not_matched/module-info.java test/tools/jlink/hashes/src/not_matched/org/not_matched/Name.java test/tools/jlink/plugins/FileCopierPluginTest.java test/tools/jmod/JmodNegativeTest.java test/tools/jmod/hashes/HashesTest.java test/tools/jmod/hashes/src/m1/module-info.java test/tools/jmod/hashes/src/m1/org/m1/Main.java test/tools/jmod/hashes/src/m2/module-info.java test/tools/jmod/hashes/src/m2/org/m2/Util.java test/tools/jmod/hashes/src/m3/module-info.java test/tools/jmod/hashes/src/m3/org/m3/Name.java test/tools/jmod/hashes/src/org.bar/module-info.java test/tools/jmod/hashes/src/org.foo/module-info.java test/tools/launcher/ToolsOpts.java test/tools/launcher/modules/addmods/AddModsTest.java test/tools/launcher/modules/addmods/src/app/Main.java test/tools/launcher/modules/addmods/src/lib/jdk/lib/Util.java test/tools/launcher/modules/addmods/src/lib/module-info.java test/tools/launcher/modules/addmods/src/logger/logger/Logger.java test/tools/launcher/modules/addmods/src/logger/module-info.java test/tools/launcher/modules/addmods/src/test/module-info.java test/tools/launcher/modules/addmods/src/test/test/Main.java test/tools/launcher/modules/addreads/AddReadsTest.java test/tools/launcher/modules/patch/PatchTest.java test/tools/lib/tests/JImageGenerator.java
diffstat 133 files changed, 3581 insertions(+), 2096 deletions(-) [+]
line wrap: on
line diff
--- a/make/Tools.gmk	Thu Apr 28 08:26:38 2016 -0700
+++ b/make/Tools.gmk	Tue May 03 09:09:57 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 \
--- a/make/gendata/GendataBreakIterator.gmk	Thu Apr 28 08:26:38 2016 -0700
+++ b/make/gendata/GendataBreakIterator.gmk	Tue May 03 09:09:57 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 \
--- a/make/launcher/Launcher-java.desktop.gmk	Thu Apr 28 08:26:38 2016 -0700
+++ b/make/launcher/Launcher-java.desktop.gmk	Tue May 03 09:09:57 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
--- a/make/launcher/Launcher-java.scripting.gmk	Thu Apr 28 08:26:38 2016 -0700
+++ b/make/launcher/Launcher-java.scripting.gmk	Tue May 03 09:09:57 2016 +0100
@@ -27,4 +27,5 @@
 
 $(eval $(call SetupBuildLauncher, jrunscript, \
     MAIN_CLASS := com.sun.tools.script.shell.Main, \
+    JAVA_ARGS := -addmods ALL-DEFAULT, \
 ))
--- a/make/launcher/Launcher-jdk.compiler.gmk	Thu Apr 28 08:26:38 2016 -0700
+++ b/make/launcher/Launcher-jdk.compiler.gmk	Tue May 03 09:09:57 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, \
 ))
 
--- a/make/launcher/Launcher-jdk.javadoc.gmk	Thu Apr 28 08:26:38 2016 -0700
+++ b/make/launcher/Launcher-jdk.javadoc.gmk	Tue May 03 09:09:57 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, \
 ))
--- a/make/launcher/Launcher-jdk.jlink.gmk	Thu Apr 28 08:26:38 2016 -0700
+++ b/make/launcher/Launcher-jdk.jlink.gmk	Tue May 03 09:09:57 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, \
--- a/make/launcher/Launcher-jdk.scripting.nashorn.shell.gmk	Thu Apr 28 08:26:38 2016 -0700
+++ b/make/launcher/Launcher-jdk.scripting.nashorn.shell.gmk	Tue May 03 09:09:57 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, \
 ))
--- a/src/java.base/share/classes/java/lang/ClassLoader.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/src/java.base/share/classes/java/lang/ClassLoader.java	Tue May 03 09:09:57 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
--- a/src/java.base/share/classes/java/lang/System.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/src/java.base/share/classes/java/lang/System.java	Tue May 03 09:09:57 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;
 
 /**
@@ -1917,10 +1916,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);
     }
--- a/src/java.base/share/classes/java/lang/module/Configuration.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/src/java.base/share/classes/java/lang/module/Configuration.java	Tue May 03 09:09:57 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<ResolvedModule, Set<ResolvedModule>> graph = resolver.finish(this);
+    private Configuration(Configuration parent,
+                          Resolver resolver,
+                          boolean check)
+    {
+        Map<ResolvedModule, Set<ResolvedModule>> g = resolver.finish(this, check);
 
         Map<String, ResolvedModule> nameToModule = new HashMap<>();
-        for (ResolvedModule resolvedModule : graph.keySet()) {
+        for (ResolvedModule resolvedModule : g.keySet()) {
             nameToModule.put(resolvedModule.name(), resolvedModule);
         }
 
         this.parent = parent;
-        this.graph = graph;
-        this.modules = Collections.unmodifiableSet(graph.keySet());
+        this.graph = g;
+        this.modules = Collections.unmodifiableSet(g.keySet());
         this.nameToModule = Collections.unmodifiableMap(nameToModule);
     }
 
@@ -283,10 +287,10 @@
         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<String> roots,
+                                                boolean check,
+                                                PrintStream traceOutput)
+    {
+        Configuration parent = empty();
+
+        Resolver resolver
+            = new Resolver(finder, parent, ModuleFinder.empty(), traceOutput);
+        resolver.resolveRequires(roots).resolveUses();
+
+        return new Configuration(parent, resolver, check);
     }
 
 
--- a/src/java.base/share/classes/java/lang/module/ModuleDescriptor.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/src/java.base/share/classes/java/lang/module/ModuleDescriptor.java	Tue May 03 09:09:57 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<String> providers, boolean check) {
             this.service = check ? requireServiceTypeName(service) : service;
-            providers = check ? Collections.unmodifiableSet(new HashSet<>(providers))
-                              : Collections.unmodifiableSet(providers);
+            providers = check
+                ? Collections.unmodifiableSet(new LinkedHashSet<>(providers))
+                : Collections.unmodifiableSet(providers);
             if (providers.isEmpty())
                 throw new IllegalArgumentException("Empty providers set");
             if (check)
@@ -787,7 +792,7 @@
     private final String osVersion;
     private final Set<String> conceals;
     private final Set<String> packages;
-    private final DependencyHashes hashes;
+    private final ModuleHashes hashes;
 
     private ModuleDescriptor(String name,
                              boolean automatic,
@@ -802,7 +807,7 @@
                              String osArch,
                              String osVersion,
                              Set<String> conceals,
-                             DependencyHashes hashes)
+                             ModuleHashes hashes)
     {
 
         this.name = name;
@@ -878,7 +883,8 @@
                      String osArch,
                      String osVersion,
                      Set<String> conceals,
-                     Set<String> packages) {
+                     Set<String> packages,
+                     ModuleHashes hashes) {
         this.name = name;
         this.automatic = automatic;
         this.synthetic = synthetic;
@@ -894,7 +900,7 @@
         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<DependencyHashes> hashes() {
+    Optional<ModuleHashes> 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<Requires.Modifier> ms, String mn) {
                     return new Requires(ms, mn, false);
@@ -1974,7 +1983,8 @@
                                                             String osArch,
                                                             String osVersion,
                                                             Set<String> conceals,
-                                                            Set<String> packages) {
+                                                            Set<String> packages,
+                                                            ModuleHashes hashes) {
                     return new ModuleDescriptor(name,
                                                 automatic,
                                                 synthetic,
@@ -1988,7 +1998,29 @@
                                                 osArch,
                                                 osVersion,
                                                 conceals,
-                                                packages);
+                                                packages,
+                                                hashes);
+                }
+
+                @Override
+                public Configuration resolveRequiresAndUses(ModuleFinder finder,
+                                                            Collection<String> roots,
+                                                            boolean check,
+                                                            PrintStream traceOutput)
+                {
+                    return Configuration.resolveRequiresAndUses(finder, roots, check, traceOutput);
+                }
+
+                @Override
+                public ModuleReference newPatchedModule(ModuleDescriptor descriptor,
+                                                        URI location,
+                                                        Supplier<ModuleReader> s) {
+                    return new ModuleReference(descriptor, location, s, true, null);
+                }
+
+                @Override
+                public Optional<ModuleHashes> hashes(ModuleDescriptor descriptor) {
+                    return descriptor.hashes();
                 }
             });
     }
--- a/src/java.base/share/classes/java/lang/module/ModuleFinder.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/src/java.base/share/classes/java/lang/module/ModuleFinder.java	Tue May 03 09:09:57 2016 +0100
@@ -205,11 +205,14 @@
      *
      * <p> The module finder returned by this method supports modules that are
      * packaged as JAR files. A JAR file with a {@code module-info.class} in
-     * the top-level directory of the JAR file is a modular JAR and is an
-     * <em>explicit module</em>. A JAR file that does not have a {@code
-     * module-info.class} in the top-level directory is an {@link
-     * ModuleDescriptor#isAutomatic automatic} module. The {@link
-     * ModuleDescriptor} for an automatic module is created as follows:
+     * the top-level directory of the JAR file (or overridden by a versioned
+     * entry in a {@link java.util.jar.JarFile#isMultiRelease() multi-release}
+     * JAR file) is a modular JAR and is an <em>explicit module</em>.
+     *
+     * A JAR file that does not have a {@code module-info.class} in the
+     * top-level directory is an {@link ModuleDescriptor#isAutomatic automatic}
+     * module. The {@link ModuleDescriptor} for an automatic module is created as
+     * follows:
      *
      * <ul>
      *
--- a/src/java.base/share/classes/java/lang/module/ModuleInfo.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/src/java.base/share/classes/java/lang/module/ModuleInfo.java	Tue May 03 09:09:57 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<String> providers = pm.get(sn);
                 if (providers == null) {
-                    providers = new HashSet<>();
+                    providers = new LinkedHashSet<>(); // preserve order
                     pm.put(sn, providers);
                 }
                 providers.add(cn);
@@ -425,7 +426,7 @@
             map.put(dn, hash);
         }
 
-        builder.hashes(new DependencyHashes(algorithm, map));
+        builder.hashes(new ModuleHashes(algorithm, map));
     }
 
 
--- a/src/java.base/share/classes/java/lang/module/ModulePath.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/src/java.base/share/classes/java/lang/module/ModulePath.java	Tue May 03 09:09:57 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<String> stream = jf.stream()
-            .map(e -> e.getName())
-            .filter(e -> (e.endsWith(".class") || e.startsWith(SERVICES_PREFIX)))
-            .distinct();
-        Map<Boolean, Set<String>> map
-            = stream.collect(Collectors.partitioningBy(s -> s.endsWith(".class"),
-                             Collectors.toSet()));
+        Map<Boolean, Set<String>> map =
+            jf.stream()
+              .map(JarEntry::getName)
+              .filter(s -> (s.endsWith(".class") ^ s.startsWith(SERVICES_PREFIX)))
+              .collect(Collectors.partitioningBy(s -> s.endsWith(".class"),
+                                                 Collectors.toSet()));
         Set<String> classFiles = map.get(Boolean.TRUE);
         Set<String> configFiles = map.get(Boolean.FALSE);
 
@@ -433,19 +440,18 @@
         classFiles.stream()
             .map(c -> toPackageName(c))
             .distinct()
-            .forEach(p -> builder.exports(p));
+            .forEach(builder::exports);
 
         // map names of service configuration files to service names
         Set<String> serviceNames = configFiles.stream()
             .map(this::toServiceName)
-            .filter(Optional::isPresent)
-            .map(Optional::get)
+            .flatMap(Optional::stream)
             .collect(Collectors.toSet());
 
         // parse each service configuration file
         for (String sn : serviceNames) {
             JarEntry entry = jf.getJarEntry(SERVICES_PREFIX + sn);
-            Set<String> providerClasses = new HashSet<>();
+            Set<String> providerClasses = new LinkedHashSet<>();
             try (InputStream in = jf.getInputStream(entry)) {
                 BufferedReader reader
                     = new BufferedReader(new InputStreamReader(in, "UTF-8"));
@@ -475,19 +481,25 @@
     private Set<String> jarPackages(JarFile jf) {
         return jf.stream()
             .filter(e -> e.getName().endsWith(".class"))
-            .map(e -> toPackageName(e))
+            .map(e -> toPackageName(e.getName()))
             .filter(pkg -> pkg.length() > 0)   // module-info
-            .distinct()
             .collect(Collectors.toSet());
     }
 
     /**
      * Returns a {@code ModuleReference} to a module in modular JAR file on
      * the file system.
+     *
+     * @throws IOException
+     * @throws FindException
+     * @throws InvalidModuleDescriptorException
      */
     private ModuleReference readJar(Path file) throws IOException {
-        try (JarFile jf = new JarFile(file.toString())) {
-
+        try (JarFile jf = new JarFile(file.toFile(),
+                                      true,               // verify
+                                      ZipFile.OPEN_READ,
+                                      JarFile.Release.RUNTIME))
+        {
             ModuleDescriptor md;
             JarEntry entry = jf.getJarEntry(MODULE_INFO);
             if (entry == null) {
@@ -520,7 +532,6 @@
                                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");
--- a/src/java.base/share/classes/java/lang/module/ModuleReader.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/src/java.base/share/classes/java/lang/module/ModuleReader.java	Tue May 03 09:09:57 2016 +0100
@@ -142,10 +142,11 @@
      * @see ClassLoader#defineClass(String, ByteBuffer, java.security.ProtectionDomain)
      */
     default Optional<ByteBuffer> read(String name) throws IOException {
-        Optional<InputStream> in = open(name);
-        if (in.isPresent()) {
-            byte[] bytes = in.get().readAllBytes();
-            return Optional.of(ByteBuffer.wrap(bytes));
+        Optional<InputStream> oin = open(name);
+        if (oin.isPresent()) {
+            try (InputStream in = oin.get()) {
+                return Optional.of(ByteBuffer.wrap(in.readAllBytes()));
+            }
         } else {
             return Optional.empty();
         }
--- a/src/java.base/share/classes/java/lang/module/ModuleReference.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/src/java.base/share/classes/java/lang/module/ModuleReference.java	Tue May 03 09:09:57 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<ModuleReader> readerSupplier;
 
+    // true if this is a reference to a patched module
+    private boolean patched;
+
     // the function that computes the hash of this module reference
     private final HashSupplier hasher;
 
     // cached hash string to avoid needing to compute it many times
     private String cachedHash;
 
+
+    /**
+     * Constructs a new instance of this class.
+     */
+    ModuleReference(ModuleDescriptor descriptor,
+                    URI location,
+                    Supplier<ModuleReader> readerSupplier,
+                    boolean patched,
+                    HashSupplier hasher)
+
+    {
+        this.descriptor = Objects.requireNonNull(descriptor);
+        this.location = location;
+        this.readerSupplier = Objects.requireNonNull(readerSupplier);
+        this.patched = patched;
+        this.hasher = hasher;
+    }
+
     /**
      * Constructs a new instance of this class.
      */
@@ -67,11 +88,9 @@
                     URI location,
                     Supplier<ModuleReader> readerSupplier,
                     HashSupplier hasher)
+
     {
-        this.descriptor = Objects.requireNonNull(descriptor);
-        this.location = location;
-        this.readerSupplier = Objects.requireNonNull(readerSupplier);
-        this.hasher = hasher;
+        this(descriptor, location, readerSupplier, false, hasher);
     }
 
 
@@ -96,10 +115,9 @@
                            URI location,
                            Supplier<ModuleReader> 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;
     }
 
     /**
--- a/src/java.base/share/classes/java/lang/module/ModuleReferences.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/src/java.base/share/classes/java/lang/module/ModuleReferences.java	Tue May 03 09:09:57 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<ModuleReader> supplier = () -> new JarModuleReader(file, uri);
-        HashSupplier hasher = (algorithm) -> Hasher.generate(file, algorithm);
+        HashSupplier hasher = (a) -> ModuleHashes.computeHashAsString(file, a);
         return newModule(md, uri, supplier, hasher);
     }
 
@@ -99,7 +99,7 @@
     static ModuleReference newJModModule(ModuleDescriptor md, Path file) {
         URI uri = file.toUri();
         Supplier<ModuleReader> supplier = () -> new JModModuleReader(file, uri);
-        HashSupplier hasher = (algorithm) -> Hasher.generate(file, algorithm);
+        HashSupplier hasher = (a) -> ModuleHashes.computeHashAsString(file, a);
         return newModule(md, file.toUri(), supplier, hasher);
     }
 
@@ -122,7 +122,7 @@
         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();
--- a/src/java.base/share/classes/java/lang/module/Resolver.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/src/java.base/share/classes/java/lang/module/Resolver.java	Tue May 03 09:09:57 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<String, ModuleReference> 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<String> roots) {
 
-        long start = trace_start("Resolve");
-
         // create the visit stack to get us started
         Deque<ModuleDescriptor> q = new ArrayDeque<>();
         for (String root : roots) {
@@ -95,10 +96,9 @@
                 }
             }
 
-            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<String> names = nameToReference.keySet();
-            trace("Resolver completed in %s ms", duration);
-            names.stream().sorted().forEach(name -> trace("  %s", name));
-        }
-
         return this;
     }
 
@@ -153,11 +146,10 @@
                     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<String> names = nameToReference.keySet();
-            trace("Bind completed in %s ms", duration);
-            names.stream().sorted().forEach(name -> trace("  %s", name));
-        }
-
         return this;
     }
 
@@ -264,23 +246,33 @@
      * Execute post-resolution checks and returns the module graph of resolved
      * modules as {@code Map}. The resolved modules will be in the given
      * configuration.
+     *
+     * @param check {@true} to execute the post resolution checks
      */
-    Map<ResolvedModule, Set<ResolvedModule>> finish(Configuration cf) {
+    Map<ResolvedModule, Set<ResolvedModule>> finish(Configuration cf,
+                                                    boolean check)
+    {
+        if (isTracing()) {
+            trace("Result:");
+            Set<String> names = nameToReference.keySet();
+            names.stream().sorted().forEach(name -> trace("  %s", name));
+        }
 
-        detectCycles();
-
-        checkPlatformConstraints();
-
-        checkHashes();
+        if (check) {
+            detectCycles();
+            checkPlatformConstraints();
+            checkHashes();
+        }
 
         Map<ResolvedModule, Set<ResolvedModule>> graph = makeGraph(cf);
 
-        checkExportSuppliers(graph);
+        if (check) {
+            checkExportSuppliers(graph);
+        }
 
         return graph;
     }
 
-
     /**
      * Checks the given module graph for cycles.
      *
@@ -420,52 +412,44 @@
 
     }
 
-
     /**
      * Checks the hashes in the module descriptor to ensure that they match
-     * the hash of the dependency's module reference.
+     * any recorded hashes.
      */
     private void checkHashes() {
-
         for (ModuleReference mref : nameToReference.values()) {
             ModuleDescriptor descriptor = mref.descriptor();
 
-            // get map of module names to hash
-            Optional<Hasher.DependencyHashes> ohashes = descriptor.hashes();
+            // get map of module hashes
+            Optional<ModuleHashes> ohashes = descriptor.hashes();
             if (!ohashes.isPresent())
                 continue;
-            Hasher.DependencyHashes hashes = ohashes.get();
+            ModuleHashes hashes = ohashes.get();
 
-            // check dependences
-            for (ModuleDescriptor.Requires d : descriptor.requires()) {
-                String dn = d.name();
-                String recordedHash = hashes.hashFor(dn);
+            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);
+                }
 
-                if (recordedHash != null) {
-
-                    ModuleReference other = nameToReference.get(dn);
-                    if (other == null) {
-                        other = parent.findModule(dn)
-                                .map(ResolvedModule::reference)
-                                .orElse(null);
-                    }
-                    if (other == null)
-                        throw new InternalError(dn + " not found");
-
-                    String actualHash = other.computeHash(hashes.algorithm());
+                // skip checking the hash if the module has been patched
+                if (other != null && !other.isPatched()) {
+                    String recordedHash = hashes.hashFor(dn);
+                    String actualHash = other.computeHash(algorithm);
                     if (actualHash == null)
                         fail("Unable to compute the hash of module %s", dn);
+                    if (!recordedHash.equals(actualHash)) {
+                        fail("Hash of %s (%s) differs to expected hash (%s)" +
+                             " recorded in %s", dn, actualHash, recordedHash,
+                             descriptor.name());
+                    }
+                }
+            }
 
-                    if (!recordedHash.equals(actualHash)) {
-                        fail("Hash of %s (%s) differs to expected hash (%s)",
-                                dn, actualHash, recordedHash);
-                    }
-
-                }
-
-            }
         }
-
     }
 
 
@@ -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<String, ModuleDescriptor.Provides> entry :
-                    descriptor1.provides().entrySet()) {
-                String service = entry.getKey();
-                ModuleDescriptor.Provides provides = entry.getValue();
-
-                String pn = packageName(service);
-                if (!packageToExporter.containsKey(pn)) {
-                    fail("Module %s does not read a module that exports %s",
-                            descriptor1.name(), pn);
+                // 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<String, ModuleDescriptor.Provides> entry :
+                        descriptor1.provides().entrySet()) {
+                    String service = entry.getKey();
+                    ModuleDescriptor.Provides provides = entry.getValue();
+
+                    String pn = packageName(service);
+                    if (!packageToExporter.containsKey(pn)) {
+                        fail("Module %s does not read a module that exports %s",
+                             descriptor1.name(), pn);
+                    }
+
+                    for (String provider : provides.providers()) {
+                        if (!packages.contains(packageName(provider))) {
+                            fail("Provider %s not in module %s",
+                                 provider, descriptor1.name());
+                        }
                     }
                 }
+
             }
 
         }
@@ -796,27 +785,18 @@
         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();
         }
     }
 
--- a/src/java.base/share/classes/java/lang/module/SystemModuleFinder.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/src/java.base/share/classes/java/lang/module/SystemModuleFinder.java	Tue May 03 09:09:57 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
--- a/src/java.base/share/classes/java/lang/reflect/Layer.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/src/java.base/share/classes/java/lang/reflect/Layer.java	Tue May 03 09:09:57 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<String, Set<ServiceProvider>> map = new HashMap<>();
+        for (Module m : nameToModule.values()) {
+            ModuleDescriptor descriptor = m.getDescriptor();
+            for (Provides provides : descriptor.provides().values()) {
+                String service = provides.service();
+                Set<ServiceProvider> providers
+                    = map.computeIfAbsent(service, k -> new HashSet<>());
+                for (String pn : provides.providers()) {
+                    providers.add(new ServiceProvider(m, pn));
+                }
+            }
+        }
+
+        ServicesCatalog catalog = new ServicesCatalog() {
+            @Override
+            public void register(Module module) {
+                throw new UnsupportedOperationException();
+            }
+            @Override
+            public Set<ServiceProvider> findServices(String service) {
+                Set<ServiceProvider> providers = map.get(service);
+                if (providers == null) {
+                    return Collections.emptySet();
+                } else {
+                    return Collections.unmodifiableSet(providers);
+                }
+            }
+        };
+
+        synchronized (this) {
+            servicesCatalog = this.servicesCatalog;
+            if (servicesCatalog == null) {
+                this.servicesCatalog = servicesCatalog = catalog;
+            }
+        }
+
+        return servicesCatalog;
+    }
+
+    private volatile ServicesCatalog servicesCatalog;
 }
--- a/src/java.base/share/classes/java/lang/reflect/Module.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/src/java.base/share/classes/java/lang/reflect/Module.java	Tue May 03 09:09:57 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 @@
     }
 
 
+    // --
+
+    // the special Module to mean reads or exported to "all unnamed modules"
+    private static final Module ALL_UNNAMED_MODULE = new Module(null);
+
+    // special Module to mean exported to "everyone"
+    private static final Module EVERYONE_MODULE = new Module(null);
+
+    // exported to all modules
+    private static final Set<Module> EVERYONE = Collections.singleton(EVERYONE_MODULE);
+
+
     // -- readability --
 
-    // true if this module reads all unnamed modules (a.k.a. loose module)
-    private volatile boolean loose;
-
     // the modules that this module permanently reads
     // (will be final when the modules are defined in reverse topology order)
     private volatile Set<Module> reads;
 
-    // created lazily, additional modules that this module reflectively reads
-    private volatile WeakSet<Module> transientReads;
+    // additional module (2nd key) that some module (1st key) reflectively reads
+    private static final WeakPairMap<Module, Module, Boolean> transientReads
+        = new WeakPairMap<>();
 
 
     /**
@@ -284,22 +287,19 @@
 
         // check if this module reads other
         if (other.isNamed()) {
-
             Set<Module> reads = this.reads; // volatile read
             if (reads != null && reads.contains(other))
                 return true;
-
-        } else {
-
-            // loose modules read all unnamed modules
-            if (this.loose)
-                return true;
-
         }
 
         // check if this module reads the other module reflectively
-        WeakSet<Module> tr = this.transientReads; // volatile read
-        if (tr != null && tr.contains(other))
+        if (transientReads.containsKeyPair(this, other))
+            return true;
+
+        // if other is an unnamed module then check if this module reads
+        // all unnamed modules
+        if (!other.isNamed()
+            && transientReads.containsKeyPair(this, ALL_UNNAMED_MODULE))
             return true;
 
         return false;
@@ -346,8 +346,7 @@
     }
 
     /**
-     * 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<Module> 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<Module> 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<String, Set<Module>> exports;
 
-    // created lazily, additional exports added at run-time
-    private volatile Map<String, WeakSet<Module>> transientExports;
-
-    // the special Module to mean exported to all modules
-    private static final Module EVERYONE_MODULE = new Module(null);
-    private static final Set<Module> EVERYONE = Collections.singleton(EVERYONE_MODULE);
-
-    // the special Module to mean exported to all unnamed modules
-    private static final Module ALL_UNNAMED_MODULE = new Module(null);
+    // additional exports added at run-time
+    // this module (1st key), other module (2nd key), exported packages (value)
+    private static final WeakPairMap<Module, Module, Map<String, Boolean>>
+        transientExports = new WeakPairMap<>();
 
 
     /**
@@ -489,23 +471,9 @@
         if (exports != null) {
             Set<Module> targets = exports.get(pn);
 
-            if (targets != null) {
-
-                // exported to all modules
-                if (targets.contains(EVERYONE_MODULE))
-                    return true;
-
-                if (other != EVERYONE_MODULE) {
-                    // exported to other
-                    if (targets.contains(other))
-                        return true;
-
-                    // other is an unnamed module && exported to all unnamed
-                    if (!other.isNamed() && targets.contains(ALL_UNNAMED_MODULE))
-                        return true;
-                }
-
-            }
+            if ((targets != null)
+                && (targets.contains(other) || targets.contains(EVERYONE_MODULE)))
+                return true;
         }
         return false;
     }
@@ -515,29 +483,27 @@
      * package package to the given module.
      */
     private boolean isExportedReflectively(String pn, Module other) {
-        Map<String, WeakSet<Module>> te = this.transientExports;
-        if (te != null) {
-            WeakSet<Module> targets = te.get(pn);
+        // exported to all modules
+        Map<String, ?> exports = transientExports.get(this, EVERYONE_MODULE);
+        if (exports != null && exports.containsKey(pn))
+            return true;
 
-            if (targets != null) {
+        if (other != EVERYONE_MODULE) {
 
-                // exported to all modules
-                if (targets.contains(EVERYONE_MODULE))
+            // exported to other
+            exports = transientExports.get(this, other);
+            if (exports != null && exports.containsKey(pn))
+                return true;
+
+            // other is an unnamed module && exported to all unnamed
+            if (!other.isNamed()) {
+                exports = transientExports.get(this, ALL_UNNAMED_MODULE);
+                if (exports != null && exports.containsKey(pn))
                     return true;
-
-                if (other != EVERYONE_MODULE) {
-
-                    // exported to other
-                    if (targets.contains(other))
-                        return true;
-
-                    // other is an unnamed module && exported to all unnamed
-                    if (!other.isNamed() && targets.contains(ALL_UNNAMED_MODULE))
-                        return true;
-                }
             }
 
         }
+
         return false;
     }
 
@@ -638,34 +604,19 @@
             }
         }
 
-        // create transientExports if needed
-        Map<String, WeakSet<Module>> te = this.transientExports; // read
-        if (te == null) {
-            synchronized (this) {
-                te = this.transientExports;
-                if (te == null) {
-                    te = new ConcurrentHashMap<>();
-                    this.transientExports = te;  // volatile write
-                }
-            }
-        }
-
         // add package name to transientExports if absent
-        WeakSet<Module> s = te.get(pn);
-        if (s == null) {
-            s = new WeakSet<>();
-            WeakSet<Module> prev = te.putIfAbsent(pn, s);
-            if (prev != null)
-                s = prev;
-        }
-        s.add(other);
+        transientExports
+            .computeIfAbsent(this, other,
+                             (_this, _other) -> new ConcurrentHashMap<>())
+            .putIfAbsent(pn, Boolean.TRUE);
     }
 
 
     // -- services --
 
-    // created lazily, additional service types that this module uses
-    private volatile WeakSet<Class<?>> transientUses;
+    // additional service type (2nd key) that some module (1st key) uses
+    private static final WeakPairMap<Module, Class<?>, Boolean> transientUses
+        = new WeakPairMap<>();
 
     /**
      * If the caller's module is this module then update this module to add a
@@ -702,17 +653,7 @@
             }
 
             if (!canUse(st)) {
-                WeakSet<Class<?>> uses = this.transientUses;
-                if (uses == null) {
-                    synchronized (this) {
-                        uses = this.transientUses;
-                        if (uses == null) {
-                            uses = new WeakSet<>();
-                            this.transientUses = uses;
-                        }
-                    }
-                }
-                uses.add(st);
+                transientUses.putIfAbsent(this, st, Boolean.TRUE);
             }
 
         }
@@ -746,11 +687,7 @@
             return true;
 
         // uses added via addUses
-        WeakSet<Class<?>> uses = this.transientUses;
-        if (uses != null && uses.contains(st))
-            return true;
-
-        return false;
+        return transientUses.containsKeyPair(this, st);
     }
 
 
@@ -885,7 +822,7 @@
     // -- 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<E> {
-        private final ReadWriteLock lock = new ReentrantReadWriteLock();
-        private final Lock readLock = lock.readLock();
-        private final Lock writeLock = lock.writeLock();
-
-        private final WeakHashMap<E, Boolean> map = new WeakHashMap<>();
-
-        /**
-         * Adds the specified element to the set.
-         */
-        void add(E e) {
-            writeLock.lock();
-            try {
-                map.put(e, Boolean.TRUE);
-            } finally {
-                writeLock.unlock();
-            }
-        }
-
-        /**
-         * Returns {@code true} if this set contains the specified element.
-         */
-        boolean contains(E e) {
-            readLock.lock();
-            try {
-                return map.containsKey(e);
-            } finally {
-                readLock.unlock();
-            }
-        }
-    }
-
-
     // -- native methods --
 
     // JVM_DefineModule
@@ -1196,8 +1093,12 @@
                     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();
+                }
             });
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/java/lang/reflect/WeakPairMap.java	Tue May 03 09:09:57 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 <K1> the type of 1st key in key pair
+ * @param <K2> the type of 2nd key in key pair
+ * @param <V>  the type of value
+ * @author Peter Levart
+ */
+final class WeakPairMap<K1, K2, V> {
+
+    private final ConcurrentHashMap<Pair<K1, K2>, V> map = new ConcurrentHashMap<>();
+    private final ReferenceQueue<Object> queue = new ReferenceQueue<>();
+
+    /**
+     * Tests if the specified pair of keys are associated with a value
+     * in the WeakPairMap.
+     *
+     * @param k1 the 1st of the pair of keys
+     * @param k2 the 2nd of the pair of keys
+     * @return true if and only if the specified key pair is in this WeakPairMap,
+     * as determined by the identity comparison; false otherwise
+     * @throws NullPointerException if any of the specified keys is null
+     */
+    public boolean containsKeyPair(K1 k1, K2 k2) {
+        expungeStaleAssociations();
+        return map.containsKey(Pair.lookup(k1, k2));
+    }
+
+    /**
+     * Returns the value to which the specified pair of keys is mapped, or null
+     * if this WeakPairMap contains no mapping for the key pair.
+     * <p>More formally, if this WeakPairMap contains a mapping from a key pair
+     * {@code (_k1, _k2)} to a value {@code v} such that
+     * {@code k1 == _k1 && k2 == _k2}, then this method returns {@code v};
+     * otherwise it returns {@code null}.
+     * (There can be at most one such mapping.)
+     *
+     * @param k1 the 1st of the pair of keys for which the mapped value is to
+     *           be returned
+     * @param k2 the 2nd of the pair of keys for which the mapped value is to
+     *           be returned
+     * @return the value to which the specified key pair is mapped, or null if
+     * this map contains no mapping for the key pair
+     * @throws NullPointerException if any of the specified keys is null
+     */
+    public V get(K1 k1, K2 k2) {
+        expungeStaleAssociations();
+        return map.get(Pair.lookup(k1, k2));
+    }
+
+    /**
+     * Maps the specified key pair to the specified value in this WeakPairMap.
+     * Neither the keys nor the value can be null.
+     * <p>The value can be retrieved by calling the {@link #get} method
+     * with the the same keys (compared by identity).
+     *
+     * @param k1 the 1st of the pair of keys with which the specified value is to
+     *           be associated
+     * @param k2 the 2nd of the pair of keys with which the specified value is to
+     *           be associated
+     * @param v  value to be associated with the specified key pair
+     * @return the previous value associated with key pair, or {@code null} if
+     * there was no mapping for key pair
+     * @throws NullPointerException if any of the specified keys or value is null
+     */
+    public V put(K1 k1, K2 k2, V v) {
+        expungeStaleAssociations();
+        return map.put(Pair.weak(k1, k2, queue), v);
+    }
+
+    /**
+     * If the specified key pair is not already associated with a value,
+     * associates it with the given value and returns {@code null}, else does
+     * nothing and returns the currently associated value.
+     *
+     * @param k1 the 1st of the pair of keys with which the specified value is to
+     *           be associated
+     * @param k2 the 2nd of the pair of keys with which the specified value is to
+     *           be associated
+     * @param v  value to be associated with the specified key pair
+     * @return the previous value associated with key pair, or {@code null} if
+     * there was no mapping for key pair
+     * @throws NullPointerException if any of the specified keys or value is null
+     */
+    public V putIfAbsent(K1 k1, K2 k2, V v) {
+        expungeStaleAssociations();
+        return map.putIfAbsent(Pair.weak(k1, k2, queue), v);
+    }
+
+    /**
+     * If the specified key pair is not already associated with a value,
+     * attempts to compute its value using the given mapping function
+     * and enters it into this WeakPairMap unless {@code null}. The entire
+     * method invocation is performed atomically, so the function is
+     * applied at most once per key pair. Some attempted update operations
+     * on this WeakPairMap by other threads may be blocked while computation
+     * is in progress, so the computation should be short and simple,
+     * and must not attempt to update any other mappings of this WeakPairMap.
+     *
+     * @param k1              the 1st of the pair of keys with which the
+     *                        computed value is to be associated
+     * @param k2              the 2nd of the pair of keys with which the
+     *                        computed value is to be associated
+     * @param mappingFunction the function to compute a value
+     * @return the current (existing or computed) value associated with
+     * the specified key pair, or null if the computed value is null
+     * @throws NullPointerException  if any of the specified keys or
+     *                               mappingFunction is null
+     * @throws IllegalStateException if the computation detectably
+     *                               attempts a recursive update to this map
+     *                               that would otherwise never complete
+     * @throws RuntimeException      or Error if the mappingFunction does so, in
+     *                               which case the mapping is left unestablished
+     */
+    public V computeIfAbsent(K1 k1, K2 k2,
+                             BiFunction<? super K1, ? super K2, ? extends V>
+                                 mappingFunction) {
+        expungeStaleAssociations();
+        try {
+            return map.computeIfAbsent(
+                Pair.weak(k1, k2, queue),
+                pair -> mappingFunction.apply(pair.first(), pair.second()));
+        } finally {
+            Reference.reachabilityFence(k1);
+            Reference.reachabilityFence(k2);
+        }
+    }
+
+    /**
+     * Returns a {@link Collection} view of the values contained in this
+     * WeakPairMap. The collection is backed by the WeakPairMap, so changes to
+     * the map are reflected in the collection, and vice-versa.  The collection
+     * supports element removal, which removes the corresponding
+     * mapping from this map, via the {@code Iterator.remove},
+     * {@code Collection.remove}, {@code removeAll},
+     * {@code retainAll}, and {@code clear} operations.  It does not
+     * support the {@code add} or {@code addAll} operations.
+     *
+     * @return the collection view
+     */
+    public Collection<V> values() {
+        expungeStaleAssociations();
+        return map.values();
+    }
+
+    /**
+     * Removes associations from this WeakPairMap for which at least one of the
+     * keys in key pair has been found weakly-reachable and corresponding
+     * WeakRefPeer(s) enqueued. Called as part of each public operation.
+     */
+    private void expungeStaleAssociations() {
+        WeakRefPeer<?> peer;
+        while ((peer = (WeakRefPeer<?>) queue.poll()) != null) {
+            map.remove(peer.weakPair());
+        }
+    }
+
+    /**
+     * Common interface of both {@link Weak} and {@link Lookup} key pairs.
+     */
+    private interface Pair<K1, K2> {
+
+        static <K1, K2> Pair<K1, K2> weak(K1 k1, K2 k2,
+                                          ReferenceQueue<Object> queue) {
+            return new Weak<>(k1, k2, queue);
+        }
+
+        static <K1, K2> Pair<K1, K2> lookup(K1 k1, K2 k2) {
+            return new Lookup<>(k1, k2);
+        }
+
+        /**
+         * @return The 1st of the pair of keys (may be null for {@link Weak}
+         * when it gets cleared)
+         */
+        K1 first();
+
+        /**
+         * @return The 2nd of the pair of keys (may be null for {@link Weak}
+         * when it gets cleared)
+         */
+        K2 second();
+
+        static int hashCode(Object first, Object second) {
+            // assert first != null && second != null;
+            return System.identityHashCode(first) ^
+                   System.identityHashCode(second);
+        }
+
+        static boolean equals(Object first, Object second, Pair<?, ?> p) {
+            return first != null && second != null &&
+                   first == p.first() && second == p.second();
+        }
+
+        /**
+         * A Pair where both keys are weakly-referenced.
+         * It is composed of two instances of {@link WeakRefPeer}s:
+         * <pre>{@code
+         *
+         *     +-referent-> [K1]                +-referent-> [K2]
+         *     |                                |
+         *   +----------------+               +----------------+
+         *   | Pair.Weak <:   |-----peer----->| (anonymous) <: |
+         *   | WeakRefPeer,   |               | WeakRefPeer    |
+         *   | Pair           |<--weakPair()--|                |
+         *   +----------------+               +----------------+
+         *     |            ^
+         *     |            |
+         *     +-weakPair()-+
+         *
+         * }</pre>
+         * <p>
+         * Pair.Weak is used for CHM keys. Both peers are associated with the
+         * same {@link ReferenceQueue} so when either of their referents
+         * becomes weakly-reachable, the corresponding entries can be
+         * {@link #expungeStaleAssociations() expunged} from the map.
+         */
+        final class Weak<K1, K2> extends WeakRefPeer<K1> implements Pair<K1, K2> {
+
+            // saved hash so it can be retrieved after the reference is cleared
+            private final int hash;
+            // link to <K2> peer
+            private final WeakRefPeer<K2> peer;
+
+            Weak(K1 k1, K2 k2, ReferenceQueue<Object> queue) {
+                super(k1, queue);
+                hash = Pair.hashCode(k1, k2);
+                peer = new WeakRefPeer<>(k2, queue) {
+                    // link back to <K1> peer
+                    @Override
+                    Weak<?, ?> weakPair() { return Weak.this; }
+                };
+            }
+
+            @Override
+            Weak<?, ?> weakPair() {
+                return this;
+            }
+
+            @Override
+            public K1 first() {
+                return get();
+            }
+
+            @Override
+            public K2 second() {
+                return peer.get();
+            }
+
+            @Override
+            public int hashCode() {
+                return hash;
+            }
+
+            @Override
+            public boolean equals(Object obj) {
+                return this == obj ||
+                       (obj instanceof Pair &&
+                        Pair.equals(first(), second(), (Pair<?, ?>) obj));
+            }
+        }
+
+        /**
+         * Optimized lookup Pair, used as lookup key in methods like
+         * {@link java.util.Map#get(Object)} or
+         * {@link java.util.Map#containsKey(Object)}) where
+         * there is a great chance its allocation is eliminated
+         * by escape analysis when such lookups are inlined by JIT.
+         * All its methods are purposely designed so that 'this' is never
+         * passed to any other method or used as identity.
+         */
+        final class Lookup<K1, K2> implements Pair<K1, K2> {
+            private final K1 k1;
+            private final K2 k2;
+
+            Lookup(K1 k1, K2 k2) {
+                this.k1 = Objects.requireNonNull(k1);
+                this.k2 = Objects.requireNonNull(k2);
+            }
+
+            @Override
+            public K1 first() {
+                return k1;
+            }
+
+            @Override
+            public K2 second() {
+                return k2;
+            }
+
+            @Override
+            public int hashCode() {
+                return Pair.hashCode(k1, k2);
+            }
+
+            @Override
+            public boolean equals(Object obj) {
+                return obj instanceof Pair &&
+                       Pair.equals(k1, k2, (Pair<?, ?>) obj);
+            }
+        }
+    }
+
+    /**
+     * Common abstract supertype of a pair of WeakReference peers.
+     */
+    private static abstract class WeakRefPeer<K> extends WeakReference<K> {
+
+        WeakRefPeer(K k, ReferenceQueue<Object> queue) {
+            super(Objects.requireNonNull(k), queue);
+        }
+
+        /**
+         * @return the {@link Pair.Weak} side of the pair of peers.
+         */
+        abstract Pair.Weak<?, ?> weakPair();
+    }
+}
--- a/src/java.base/share/classes/java/util/ServiceLoader.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/src/java.base/share/classes/java/util/ServiceLoader.java	Tue May 03 09:09:57 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 <i>uses</i> clause
  * in its <i>module descriptor</i> to declare that the module uses
  * implementations of the service. A corresponding requirement is that a
- * provider deployed as a named modules must have an appropriate
+ * provider deployed as a named module must have an appropriate
  * <i>provides</i> clause in its module descriptor to declare that the module
  * provides an implementation of the service. The <i>uses</i> and
  * <i>provides</i> allow consumers of a service to be <i>linked</i> to
@@ -550,35 +548,29 @@
     /**
      * Implements lazy service provider lookup of service providers that
      * are provided by modules in a module Layer.
-     *
-     * For now, this iterator examines all modules in each Layer. This will
-     * be replaced once we decide on how the service-use graph is exposed
-     * in the module API.
      */
     private class LayerLookupIterator
         extends RestrictedIterator<S>
     {
         final String serviceName;
         Layer currentLayer;
-        Iterator<ModuleDescriptor> descriptorIterator;
-        Iterator<String> providersIterator;
-
-        Module nextModule;
-        String nextProvider;
+        Iterator<ServiceProvider> iterator;
+        ServiceProvider nextProvider;
 
         LayerLookupIterator() {
             serviceName = service.getName();
             currentLayer = layer;
 
             // need to get us started
-            descriptorIterator = descriptors(layer, serviceName);
+            iterator = providers(currentLayer, serviceName);
         }
 
-        Iterator<ModuleDescriptor> descriptors(Layer layer, String service) {
-            return layer.modules().stream()
-                    .map(Module::getDescriptor)
-                    .filter(d -> d.provides().get(service) != null)
-                    .iterator();
+        Iterator<ServiceProvider> providers(Layer layer, String service) {
+            ServicesCatalog catalog = SharedSecrets
+                    .getJavaLangReflectModuleAccess()
+                    .getServicesCatalog(layer);
+
+            return catalog.findServices(serviceName).iterator();
         }
 
         @Override
@@ -591,30 +583,18 @@
             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))
--- a/src/java.base/share/classes/jdk/internal/loader/BootLoader.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/src/java.base/share/classes/jdk/internal/loader/BootLoader.java	Tue May 03 09:09:57 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 =
--- a/src/java.base/share/classes/jdk/internal/loader/BuiltinClassLoader.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/src/java.base/share/classes/jdk/internal/loader/BuiltinClassLoader.java	Tue May 03 09:09:57 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;
--- a/src/java.base/share/classes/jdk/internal/misc/JavaLangModuleAccess.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/src/java.base/share/classes/jdk/internal/misc/JavaLangModuleAccess.java	Tue May 03 09:09:57 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<String> conceals,
-                                         Set<String> packages);
+                                         Set<String> packages,
+                                         ModuleHashes hashes);
+
+    /**
+     * Resolves a collection of root modules, with service binding
+     * and the empty configuration as the parent. The post resolution
+     * checks are optionally run.
+     */
+    Configuration resolveRequiresAndUses(ModuleFinder finder,
+                                         Collection<String> roots,
+                                         boolean check,
+                                         PrintStream traceOutput);
+
+    /**
+     * Creates a ModuleReference to a "patched" module.
+     */
+    ModuleReference newPatchedModule(ModuleDescriptor descriptor,
+                                     URI location,
+                                     Supplier<ModuleReader> readerSupplier);
+
+    /**
+     * Returns the object with the hashes of other modules
+     */
+    Optional<ModuleHashes> hashes(ModuleDescriptor descriptor);
+
 }
--- a/src/java.base/share/classes/jdk/internal/misc/JavaLangReflectModuleAccess.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/src/java.base/share/classes/jdk/internal/misc/JavaLangReflectModuleAccess.java	Tue May 03 09:09:57 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
--- a/src/java.base/share/classes/jdk/internal/module/Builder.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/src/java.base/share/classes/jdk/internal/module/Builder.java	Tue May 03 09:09:57 2016 +0100
@@ -74,6 +74,8 @@
     String osName;
     String osArch;
     String osVersion;
+    String algorithm;
+    Map<String, String> hashes;
 
     Builder(String name, int reqs, int exports,
             int provides, int conceals, int packages) {
@@ -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);
     }
 }
--- a/src/java.base/share/classes/jdk/internal/module/ClassFileAttributes.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/src/java.base/share/classes/jdk/internal/module/ClassFileAttributes.java	Tue May 03 09:09:57 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<provides_count; i++) {
                     String sn = cr.readClass(off, buf).replace('/', '.');
                     String cn = cr.readClass(off + 2, buf).replace('/', '.');
-                    provides.computeIfAbsent(sn, k -> new HashSet<>()).add(cn);
+                    provides.computeIfAbsent(sn, k -> new LinkedHashSet<>()).add(cn);
                     off += 4;
                 }
                 provides.entrySet().forEach(e -> builder.provides(e.getKey(),
@@ -281,10 +281,10 @@
      *   u4 attribute_length;
      *
      *   // the number of entries in the packages table
-     *   u2 package_count;
+     *   u2 packages_count;
      *   { // index to CONSTANT_CONSTANT_utf8_info structure with the package name
      *     u2 package_index
-     *   } package[package_count];
+     *   } packages[package_count];
      *
      * }</pre>
      */
@@ -579,9 +579,9 @@
      * 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);
         }
--- a/src/java.base/share/classes/jdk/internal/module/Hasher.java	Thu Apr 28 08:26:38 2016 -0700
+++ /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<String, String> nameToHash;
-
-        public DependencyHashes(String algorithm, Map<String, String> nameToHash) {
-            this.algorithm = algorithm;
-            this.nameToHash = nameToHash;
-        }
-
-        /**
-         * Returns the algorithm used to hash the dependences ("SHA-256" or
-         * "MD5" for example).
-         */
-        public String algorithm() {
-            return algorithm;
-        }
-
-        /**
-         * Returns the set of module names for which hashes are recorded.
-         */
-        public Set<String> names() {
-            return nameToHash.keySet();
-        }
-
-        /**
-         * Retruns the hash string for the given module name, {@code null}
-         * if there is no hash recorded for the module.
-         */
-        public String hashFor(String dn) {
-            return nameToHash.get(dn);
-        }
-    }
-
-
-    /**
-     * 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<String, Path> map, String algorithm) {
-        Map<String, String> nameToHash = new HashMap<>();
-        for (Map.Entry<String, Path> entry: map.entrySet()) {
-            String name = entry.getKey();
-            Path path = entry.getValue();
-            nameToHash.put(name, generate(path, algorithm));
-        }
-        return new DependencyHashes(algorithm, nameToHash);
-    }
-}
--- a/src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java	Tue May 03 09:09:57 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();
 
-        // Once we have the system module path then we define the base module.
-        // We do this here so that java.base is defined to the VM as early as
+        PerfCounters.systemModulesTime.addElapsedTimeFrom(t0);
+
+
+        long t1 = System.nanoTime();
+
+        // Once we have the system modules then we define the base module to
+        // the VM. We do this here so that java.base is defined as early as
         // possible and also that resources in the base module can be located
         // for error messages that may happen from here on.
-        Optional<ModuleReference> obase = systemModulePath.find(JAVA_BASE);
-        if (!obase.isPresent())
+        ModuleReference base = systemModules.find(JAVA_BASE).orElse(null);
+        if (base == null)
             throw new InternalError(JAVA_BASE + " not found");
-        ModuleReference base = obase.get();
+        URI baseUri = base.location().orElse(null);
+        if (baseUri == null)
+            throw new InternalError(JAVA_BASE + " does not have a location");
         BootLoader.loadModule(base);
-        Modules.defineModule(null, base.descriptor(), base.location().orElse(null));
+        Modules.defineModule(null, base.descriptor(), baseUri);
 
+        PerfCounters.defineBaseTime.addElapsedTimeFrom(t1);
+
+
+        long t2 = System.nanoTime();
 
         // -upgrademodulepath option specified to launcher
         ModuleFinder upgradeModulePath
             = createModulePathFinder("jdk.upgrade.module.path");
+        if (upgradeModulePath != null)
+            systemModules = ModuleFinder.compose(upgradeModulePath, systemModules);
 
         // -modulepath option specified to the launcher
         ModuleFinder appModulePath = createModulePathFinder("jdk.module.path");
 
-        // The module finder: [-upgrademodulepath] system-module-path [-modulepath]
-        ModuleFinder finder = systemModulePath;
-        if (upgradeModulePath != null)
-            finder = ModuleFinder.compose(upgradeModulePath, finder);
+        // The module finder: [-upgrademodulepath] system [-modulepath]
+        ModuleFinder finder = systemModules;
         if (appModulePath != null)
             finder = ModuleFinder.compose(finder, appModulePath);
 
-        // launcher -m option to specify the initial module
+        // The root modules to resolve
+        Set<String> roots = new HashSet<>();
+
+        // launcher -m option to specify the main/initial module
         String mainModule = System.getProperty("jdk.module.main");
+        if (mainModule != null)
+            roots.add(mainModule);
 
         // additional module(s) specified by -addmods
+        boolean addAllDefaultModules = false;
         boolean addAllSystemModules = false;
         boolean addAllApplicationModules = false;
-        Set<String> addModules = null;
         String propValue = System.getProperty("jdk.launcher.addmods");
         if (propValue != null) {
-            addModules = new HashSet<>();
             for (String mod: propValue.split(",")) {
                 switch (mod) {
+                    case ALL_DEFAULT:
+                        addAllDefaultModules = true;
+                        break;
                     case ALL_SYSTEM:
                         addAllSystemModules = true;
                         break;
@@ -142,28 +167,12 @@
                         addAllApplicationModules = true;
                         break;
                     default :
-                        addModules.add(mod);
+                        roots.add(mod);
                 }
             }
         }
 
-        // The root modules to resolve
-        Set<String> roots = new HashSet<>();
-
-        // main/initial module
-        if (mainModule != null) {
-            roots.add(mainModule);
-            if (addAllApplicationModules)
-                fail(ALL_MODULE_PATH + " not allowed with initial module");
-        }
-
-        // If -addmods is specified then those modules need to be resolved
-        if (addModules != null)
-            roots.addAll(addModules);
-
-
         // -limitmods
-        boolean limitmods = false;
         propValue = System.getProperty("jdk.launcher.limitmods");
         if (propValue != null) {
             Set<String> mods = new HashSet<>();
@@ -171,62 +180,101 @@
                 mods.add(mod);
             }
             finder = limitFinder(finder, mods, roots);
-            limitmods = true;
         }
 
-
-        // If there is no initial module specified then assume that the
-        // initial module is the unnamed module of the application class
-        // loader. By convention, and for compatibility, this is
-        // implemented by putting the names of all modules on the system
-        // module path into the set of modules to resolve.
-        //
-        // If `-addmods ALL-SYSTEM` is used then all modules on the system
-        // module path will be resolved, irrespective of whether an initial
-        // module is specified.
-        //
-        // If `-addmods ALL-MODULE-PATH` is used, and no initial module is
-        // specified, then all modules on the application module path will
-        // be resolved.
-        //
-        if (mainModule == null || addAllSystemModules) {
-            Set<ModuleReference> mrefs;
-            if (addAllApplicationModules) {
-                assert mainModule == null;
-                mrefs = finder.findAll();
-            } else {
-                mrefs = systemModulePath.findAll();
-                if (limitmods) {
-                    ModuleFinder f = finder;
-                    mrefs = mrefs.stream()
-                        .filter(m -> f.find(m.descriptor().name()).isPresent())
-                        .collect(Collectors.toSet());
+        // If there is no initial module specified then assume that the initial
+        // module is the unnamed module of the application class loader. This
+        // is implemented by resolving "java.se" and all (non-java.*) modules
+        // that export an API. If "java.se" is not observable then all java.*
+        // modules are resolved.
+        if (mainModule == null || addAllDefaultModules) {
+            boolean hasJava = false;
+            if (systemModules.find(JAVA_SE).isPresent()) {
+                // java.se is a system module
+                if (finder == systemModules || finder.find(JAVA_SE).isPresent()) {
+                    // java.se is observable
+                    hasJava = true;
+                    roots.add(JAVA_SE);
                 }
             }
-            // map to module names
-            for (ModuleReference mref : mrefs) {
-                roots.add(mref.descriptor().name());
+
+            for (ModuleReference mref : systemModules.findAll()) {
+                String mn = mref.descriptor().name();
+                if (hasJava && mn.startsWith("java."))
+                    continue;
+
+                // add as root if observable and exports at least one package
+                if ((finder == systemModules || finder.find(mn).isPresent())) {
+                    ModuleDescriptor descriptor = mref.descriptor();
+                    for (ModuleDescriptor.Exports e : descriptor.exports()) {
+                        if (!e.isQualified()) {
+                            roots.add(mn);
+                            break;
+                        }
+                    }
+                }
             }
         }
 
-        long t1 = System.nanoTime();
+        // If `-addmods ALL-SYSTEM` is specified then all observable system
+        // modules will be resolved.
+        if (addAllSystemModules) {
+            ModuleFinder f = finder;  // observable modules
+            systemModules.findAll()
+                .stream()
+                .map(ModuleReference::descriptor)
+                .map(ModuleDescriptor::name)
+                .filter(mn -> f.find(mn).isPresent())  // observable
+                .forEach(mn -> roots.add(mn));
+        }
+
+        // If `-addmods ALL-MODULE-PATH` is specified then all observable
+        // modules on the application module path will be resolved.
+        if  (appModulePath != null && addAllApplicationModules) {
+            ModuleFinder f = finder;  // observable modules
+            appModulePath.findAll()
+                .stream()
+                .map(ModuleReference::descriptor)
+                .map(ModuleDescriptor::name)
+                .filter(mn -> f.find(mn).isPresent())  // observable
+                .forEach(mn -> roots.add(mn));
+        }
+
+        PerfCounters.optionsAndRootsTime.addElapsedTimeFrom(t2);
+
+
+        long t3 = System.nanoTime();
+
+        // determine if post resolution checks are needed
+        boolean needPostResolutionChecks = true;
+        if (baseUri.getScheme().equals("jrt")   // toLowerCase not needed here
+                && (upgradeModulePath == null)
+                && (appModulePath == null)
+                && (System.getProperty("jdk.launcher.patch.0") == null)) {
+            needPostResolutionChecks = false;
+        }
+
+        PrintStream traceOutput = null;
+        if (Boolean.getBoolean("jdk.launcher.traceResolver"))
+            traceOutput = System.out;
 
         // run the resolver to create the configuration
-
-        Configuration cf = Configuration.empty()
+        Configuration cf = SharedSecrets.getJavaLangModuleAccess()
                 .resolveRequiresAndUses(finder,
-                                        ModuleFinder.empty(),
-                                        roots);
+                                        roots,
+                                        needPostResolutionChecks,
+                                        traceOutput);
 
         // time to create configuration
-        PerfCounters.resolveTime.addElapsedTimeFrom(t1);
+        PerfCounters.resolveTime.addElapsedTimeFrom(t3);
+
 
         // mapping of modules to class loaders
         Function<String, ClassLoader> clf = ModuleLoaderMap.mappingFunction(cf);
 
         // check that all modules to be mapped to the boot loader will be
-        // loaded from the system module path
-        if (finder != systemModulePath) {
+        // loaded from the runtime image
+        if (needPostResolutionChecks) {
             for (ResolvedModule resolvedModule : cf.modules()) {
                 ModuleReference mref = resolvedModule.reference();
                 String name = mref.descriptor().name();
@@ -237,20 +285,22 @@
                             && 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<String, ModuleReference> map = new HashMap<>();
+
+        // root modules and their transitive dependences
         cf.modules().stream()
             .map(ResolvedModule::reference)
             .forEach(mref -> map.put(mref.descriptor().name(), mref));
 
+        // additional modules
+        otherMods.stream()
+            .map(finder::find)
+            .flatMap(Optional::stream)
+            .forEach(mref -> map.putIfAbsent(mref.descriptor().name(), mref));
+
         // set of modules that are observable
         Set<ModuleReference> mrefs = new HashSet<>(map.values());
 
-        // add the other modules
-        for (String mod : otherMods) {
-            Optional<ModuleReference> omref = finder.find(mod);
-            if (omref.isPresent()) {
-                ModuleReference mref = omref.get();
-                map.putIfAbsent(mod, mref);
-                mrefs.add(mref);
-            } else {
-                // no need to fail
-            }
-        }
-
         return new ModuleFinder() {
             @Override
             public Optional<ModuleReference> find(String name) {
@@ -369,15 +416,15 @@
 
                 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<String, Set<String>> 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);
+            // value is <module>(,<module>)*
+            if (map.containsKey(key))
+                fail(key + " specified more than once");
 
-            if (pos == -1) {
-
-                // new format: $KEY=$MODULE(,$MODULE)*
-
-                Set<String> values = map.get(key);
-                if (values != null)
-                    fail(key + " specified more than once");
-
-                values = new HashSet<>();
-                map.put(key, values);
-                for (String s : rhs.split(",")) {
-                    if (s.length() > 0) values.add(s);
-                }
-
-            } else {
-
-                // old format: $KEY=$MODULE(,$KEY=$MODULE)*
-
-                assert index == 0;  // old format only allowed in first usage
-
-                for (String expr : value.split(",")) {
-                    if (expr.length() > 0) {
-                        String[] s = expr.split("=");
-                        if (s.length != 2)
-                            fail("Unable to parse: " + expr);
-
-                        map.computeIfAbsent(s[0], k -> new HashSet<>()).add(s[1]);
-                    }
-                }
+            Set<String> values = new HashSet<>();
+            map.put(key, values);
+            for (String s : rhs.split(",")) {
+                if (s.length() > 0) values.add(s);
             }
 
             index++;
@@ -521,6 +537,13 @@
     }
 
     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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/jdk/internal/module/ModuleHashes.java	Tue May 03 09:09:57 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<String, String> 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<String, String> nameToHash) {
+        this.algorithm = algorithm;
+        this.nameToHash = Collections.unmodifiableMap(nameToHash);
+    }
+
+    /**
+     * Returns the algorithm used to hash the modules ("SHA-256" for example).
+     */
+    public String algorithm() {
+        return algorithm;
+    }
+
+    /**
+     * Returns the set of module names for which hashes are recorded.
+     */
+    public Set<String> names() {
+        return nameToHash.keySet();
+    }
+
+    /**
+     * Returns the hash string for the given module name, {@code null}
+     * if there is no hash recorded for the module.
+     */
+    public String hashFor(String mn) {
+        return nameToHash.get(mn);
+    }
+
+    /**
+     * Returns unmodifiable map of module name to hash string.
+     */
+    public Map<String, String> hashes() {
+        return nameToHash;
+    }
+
+    /**
+     * Computes the hash for the given file with the given message digest
+     * 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<String, Path> map, String algorithm) {
+        Map<String, String> nameToHash = new HashMap<>();
+        for (Map.Entry<String, Path> entry: map.entrySet()) {
+            String name = entry.getKey();
+            Path path = entry.getValue();
+            nameToHash.put(name, computeHashAsString(path, algorithm));
+        }
+        return new ModuleHashes(algorithm, nameToHash);
+    }
+}
--- a/src/java.base/share/classes/jdk/internal/module/ModuleInfoExtender.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/src/java.base/share/classes/jdk/internal/module/ModuleInfoExtender.java	Tue May 03 09:09:57 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;
     }
--- a/src/java.base/share/classes/jdk/internal/module/ModuleInfoWriter.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/src/java.base/share/classes/jdk/internal/module/ModuleInfoWriter.java	Tue May 03 09:09:57 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<Version> oversion = descriptor.version();
-        if (oversion.isPresent())
-            cw.visitAttribute(new VersionAttribute(oversion.get()));
-
-        Optional<String> omain = descriptor.mainClass();
-        if (omain.isPresent())
-            cw.visitAttribute(new MainClassAttribute(omain.get()));
+        cw.visitAttribute(new ModuleAttribute(md));
+        cw.visitAttribute(new ConcealedPackagesAttribute(md.conceals()));
+        md.version().ifPresent(v -> cw.visitAttribute(new VersionAttribute(v)));
+        md.mainClass().ifPresent(mc -> cw.visitAttribute(new MainClassAttribute(mc)));
 
         // write the TargetPlatform attribute if have any of OS name/arch/version
-        String osName = descriptor.osName().orElse(null);
-        String osArch = descriptor.osArch().orElse(null);
-        String osVersion = descriptor.osVersion().orElse(null);
+        String osName = md.osName().orElse(null);
+        String osArch = md.osArch().orElse(null);
+        String osVersion = md.osVersion().orElse(null);
         if (osName != null || osArch != null || osVersion != null) {
             cw.visitAttribute(new TargetPlatformAttribute(osName,
                                                           osArch,
--- a/src/java.base/share/classes/jdk/internal/module/ModulePatcher.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/src/java.base/share/classes/jdk/internal/module/ModulePatcher.java	Tue May 03 09:09:57 2016 +0100
@@ -91,56 +91,29 @@
 
         Map<String, List<Path>> map = new HashMap<>();
         while (value != null) {
+
+            // <module>=<file>(:<file>)*
+
             int pos = value.indexOf('=');
-
-            if (pos == -1 && index > 0)
+            if (pos == -1)
                 throwIAE("Unable to parse: " + value);
-
             if (pos == 0)
                 throwIAE("Missing module name: " + value);
 
-            if (pos > 0) {
+            String mn = value.substring(0, pos);
+            List<Path> list = map.get(mn);
+            if (list != null)
+                throwIAE("Module " + mn + " specified more than once");
+            list = new ArrayList<>();
+            map.put(mn, list);
 
-                // new format: <module>=<file>(:<file>)*
-
-                String mn = value.substring(0, pos);
-                List<Path> list = map.get(mn);
-                if (list != null)
-                    throwIAE("Module " + mn + " specified more than once");
-                list = new ArrayList<>();
-                map.put(mn, list);
-
-                String paths = value.substring(pos+1);
-                for (String path : paths.split(File.pathSeparator)) {
-                    if (!path.isEmpty()) {
-                        list.add(Paths.get(path));
-                    }
+            String paths = value.substring(pos+1);
+            for (String path : paths.split(File.pathSeparator)) {
+                if (!path.isEmpty()) {
+                    list.add(Paths.get(path));
                 }
-
-            } else {
-
-                // old format: <dir>(:<dir>)*
-
-                assert index == 0; // old format only allowed in first -Xpatch
-
-                String[] dirs = value.split(File.pathSeparator);
-                for (String d : dirs) {
-                    if (d.length() > 0) {
-                        Path top = Paths.get(d);
-                        try {
-                            Files.list(top).forEach(e -> {
-                                String mn = e.getFileName().toString();
-                                Path dir = top.resolve(mn);
-                                map.computeIfAbsent(mn, k -> new ArrayList<>())
-                                    .add(dir);
-                            });
-                        } catch (IOException ignore) { }
-                    }
-                }
-
             }
 
-
             index++;
             value = System.getProperty(PATCH_PROPERTY_PREFIX + index);
         }
@@ -175,7 +148,8 @@
             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));
 
     }
 
--- a/src/java.base/share/classes/jdk/internal/module/Modules.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/src/java.base/share/classes/jdk/internal/module/Modules.java	Tue May 03 09:09:57 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.
      */
--- a/src/java.base/share/classes/jdk/internal/module/ServicesCatalog.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/src/java.base/share/classes/jdk/internal/module/ServicesCatalog.java	Tue May 03 09:09:57 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 <em>services catalog</em>. Each {@code ClassLoader} and {@code Layer} has
+ * an optional {@code ServicesCatalog} for modules that provide services.
+ *
+ * @see java.util.ServiceLoader
  */
-public class ServicesCatalog {
-
-    // use RW locks as register is rare
-    private final ReadWriteLock lock = new ReentrantReadWriteLock();
-    private final Lock readLock = lock.readLock();
-    private final Lock writeLock = lock.writeLock();
+public interface ServicesCatalog {
 
     /**
      * Represents a service provider in the services catalog.
      */
-    public class ServiceProvider {
+    public final class ServiceProvider {
         private final Module module;
         private final String providerName;
-        ServiceProvider(Module module, String providerName) {
+
+        public ServiceProvider(Module module, String providerName) {
             this.module = module;
             this.providerName = providerName;
         }
+
         public Module module() {
             return module;
         }
+
         public String providerName() {
             return providerName;
         }
-    }
 
-    // service providers
-    private final Map<String, Set<ServiceProvider>> loaderServices = new HashMap<>();
+        @Override
+        public int hashCode() {
+            return Objects.hash(module, providerName);
+        }
 
-    /**
-     * Creates a new module catalog.
-     */
-    public ServicesCatalog() { }
-
-    /**
-     * Registers the module in this module catalog.
-     */
-    public void register(Module m) {
-        ModuleDescriptor descriptor = m.getDescriptor();
-
-        writeLock.lock();
-        try {
-            // extend the services map
-            for (Provides ps : descriptor.provides().values()) {
-                String service = ps.service();
-                Set<String> providerNames = ps.providers();
-
-                // create a new set to replace the existing
-                Set<ServiceProvider> result = new HashSet<>();
-                Set<ServiceProvider> providers = loaderServices.get(service);
-                if (providers != null) {
-                    result.addAll(providers);
-                }
-                for (String pn : providerNames) {
-                    result.add(new ServiceProvider(m, pn));
-                }
-                loaderServices.put(service, Collections.unmodifiableSet(result));
-            }
-
-        } finally {
-            writeLock.unlock();
+        @Override
+        public boolean equals(Object ob) {
+            if (!(ob instanceof ServiceProvider))
+                return false;
+            ServiceProvider that = (ServiceProvider)ob;
+            return Objects.equals(this.module, that.module)
+                    && Objects.equals(this.providerName, that.providerName);
         }
     }
 
     /**
+     * Registers the providers in the given module in this services catalog.
+     *
+     * @throws UnsupportedOperationException
+     *         If this services catalog is immutable
+     */
+    void register(Module module);
+
+    /**
      * Returns the (possibly empty) set of service providers that implement the
      * given service type.
-     *
-     * @see java.util.ServiceLoader
      */
-    public Set<ServiceProvider> findServices(String service) {
-        readLock.lock();
-        try {
-            return loaderServices.getOrDefault(service, Collections.emptySet());
-        } finally {
-            readLock.unlock();
-        }
+    Set<ServiceProvider> findServices(String service);
+
+    /**
+     * Creates a ServicesCatalog that supports concurrent registration and
+     * and lookup.
+     */
+    static ServicesCatalog create() {
+        return new ServicesCatalog() {
+
+            private Map<String, Set<ServiceProvider>> map = new ConcurrentHashMap<>();
+
+            @Override
+            public void register(Module m) {
+                ModuleDescriptor descriptor = m.getDescriptor();
+
+                for (Provides provides : descriptor.provides().values()) {
+                    String service = provides.service();
+                    Set<String> providerNames = provides.providers();
+
+                    // create a new set to replace the existing
+                    Set<ServiceProvider> result = new HashSet<>();
+                    Set<ServiceProvider> providers = map.get(service);
+                    if (providers != null) {
+                        result.addAll(providers);
+                    }
+                    for (String pn : providerNames) {
+                        result.add(new ServiceProvider(m, pn));
+                    }
+                    map.put(service, Collections.unmodifiableSet(result));
+                }
+
+            }
+
+            @Override
+            public Set<ServiceProvider> findServices(String service) {
+                return map.getOrDefault(service, Collections.emptySet());
+            }
+
+        };
     }
-}
+}
\ No newline at end of file
--- a/src/java.base/share/classes/jdk/internal/module/SystemModules.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/src/java.base/share/classes/jdk/internal/module/SystemModules.java	Tue May 03 09:09:57 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
+
+}
--- a/src/java.base/share/classes/module-info.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/src/java.base/share/classes/module-info.java	Tue May 03 09:09:57 2016 +0100
@@ -168,6 +168,8 @@
         java.sql,
         java.xml,
         jdk.charsets,
+        jdk.jartool,
+        jdk.jlink,
         jdk.scripting.nashorn,
         jdk.unsupported,
         jdk.vm.ci;
--- a/src/java.base/share/classes/sun/launcher/resources/launcher.properties	Thu Apr 28 08:26:38 2016 -0700
+++ b/src/java.base/share/classes/sun/launcher/resources/launcher.properties	Tue May 03 09:09:57 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 <modulepath> -m <modulename> | <modulename>/<mainclass>\n\
+\   or  {0} [options] -mp <modulepath> -m <modulename>[/<mainclass>] [args...]\n\
 \           (to execute the main class in a module)\n\
 where options include:\n
 
@@ -51,8 +51,9 @@
 \                  A {0} separated list of directories, each directory\n\
 \                  is a directory of modules that replace upgradeable\n\
 \                  modules in the runtime image\n\
-\    -m <modulename> | <modulename>/<mainclass>\n\
-\                  the initial or main module to resolve\n\
+\    -m <modulename>[/<mainclass>]\n\
+\                  the initial module to resolve, and the name of the main class\n\
+\                  to execute if not specified by the module\n\
 \    -addmods <modulename>[,<modulename>...]\n\
 \                  root modules to resolve in addition to the initial module\n\
 \    -limitmods <modulename>[,<modulename>...]\n\
--- a/src/jdk.jartool/share/classes/sun/tools/jar/GNUStyleOptions.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/src/jdk.jartool/share/classes/sun/tools/jar/GNUStyleOptions.java	Tue May 03 09:09:57 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);
                     }
--- a/src/jdk.jartool/share/classes/sun/tools/jar/Main.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/src/jdk.jartool/share/classes/sun/tools/jar/Main.java	Tue May 03 09:09:57 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 <T> String toString(Set<T> set) {
+        if (set.isEmpty()) { return ""; }
+        return set.stream().map(e -> e.toString().toLowerCase(Locale.ROOT))
+                  .collect(joining(" "));
+    }
+
+    private static final JavaLangModuleAccess JLMA = SharedSecrets.getJavaLangModuleAccess();
+
     private void printModuleDescriptor(InputStream entryInputStream)
         throws IOException
     {
         ModuleDescriptor md = ModuleDescriptor.read(entryInputStream);
         StringBuilder sb = new StringBuilder();
-        sb.append("\nName:\n  " + md.toNameAndVersion());
+        sb.append("\n").append(md.toNameAndVersion());
 
-        Set<Requires> requires = md.requires();
-        if (!requires.isEmpty()) {
-            sb.append("\nRequires:");
-            requires.forEach(r ->
-                    sb.append("\n  ").append(r.name())
-                            .append(toString(r.modifiers(), " [ ", " ]")));
-        }
+        md.requires().stream()
+            .sorted(Comparator.comparing(Requires::name))
+            .forEach(r -> {
+                sb.append("\n  requires ");
+                if (!r.modifiers().isEmpty())
+                    sb.append(toString(r.modifiers())).append(" ");
+                sb.append(r.name());
+            });
 
-        Set<String> s = md.uses();
-        if (!s.isEmpty()) {
-            sb.append("\nUses: ");
-            s.forEach(sv -> sb.append("\n  ").append(sv));
-        }
+        md.uses().stream().sorted()
+            .forEach(p -> sb.append("\n  uses ").append(p));
 
-        Set<Exports> exports = md.exports();
-        if (!exports.isEmpty()) {
-            sb.append("\nExports:");
-            exports.forEach(sv -> sb.append("\n  ").append(sv));
-        }
+        md.exports().stream()
+            .sorted(Comparator.comparing(Exports::source))
+            .forEach(p -> sb.append("\n  exports ").append(p));
 
-        Map<String,Provides> provides = md.provides();
-        if (!provides.isEmpty()) {
-            sb.append("\nProvides: ");
-            provides.values().forEach(p ->
-                    sb.append("\n  ").append(p.service())
-                      .append(" with ")
-                      .append(toString(p.providers(), "", "")));
-        }
+        md.conceals().stream().sorted()
+            .forEach(p -> sb.append("\n  conceals ").append(p));
 
-        Optional<String> mc = md.mainClass();
-        if (mc.isPresent())
-            sb.append("\nMain class:\n  " + mc.get());
+        md.provides().values().stream()
+            .sorted(Comparator.comparing(Provides::service))
+            .forEach(p -> sb.append("\n  provides ").append(p.service())
+                            .append(" with ")
+                            .append(toString(p.providers())));
 
-        s = md.conceals();
-        if (!s.isEmpty()) {
-            sb.append("\nConceals:");
-            s.forEach(p -> sb.append("\n  ").append(p));
-        }
+        md.mainClass().ifPresent(v -> sb.append("\n  main-class " + v));
 
-        try {
-            Method m = ModuleDescriptor.class.getDeclaredMethod("hashes");
-            m.setAccessible(true);
-            Optional<Hasher.DependencyHashes> optHashes =
-                    (Optional<Hasher.DependencyHashes>) m.invoke(md);
+        md.osName().ifPresent(v -> sb.append("\n  operating-system-name " + v));
 
-            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);
-        }
+        md.osArch().ifPresent(v -> sb.append("\n  operating-system-architecture " + v));
+
+        md.osVersion().ifPresent(v -> sb.append("\n  operating-system-version " + v));
+
+        JLMA.hashes(md).ifPresent(hashes ->
+                hashes.names().stream().sorted().forEach(
+                    mod -> sb.append("\n  hashes ").append(mod).append(" ")
+                             .append(hashes.algorithm()).append(" ")
+                             .append(hashes.hashFor(mod))));
+
         output(sb.toString());
     }
 
@@ -1751,7 +1748,6 @@
             md = ModuleDescriptor.read(in);
         }
         String name = md.name();
-        Set<ModuleDescriptor.Requires> dependences = md.requires();
         Set<String> exported = md.exports()
                                  .stream()
                                  .map(ModuleDescriptor.Exports::source)
@@ -1778,9 +1774,17 @@
             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<ModuleDescriptor.Requires> moduleDependences)
-        throws IOException
-    {
-        Map<String, Path> map = new HashMap<>();
-        Matcher matcher = dependenciesToHash.matcher("");
-        for (ModuleDescriptor.Requires md: moduleDependences) {
-            String dn = md.name();
-            if (matcher.reset(dn).find()) {
-                Optional<ModuleReference> omref = moduleFinder.find(dn);
-                if (!omref.isPresent()) {
-                    throw new IOException(formatMsg2("error.hash.dep", name , dn));
-                }
-                map.put(dn, modRefToPath(omref.get()));
+    private class Hasher {
+        final ModuleFinder finder;
+        final Map<String, Path> moduleNameToPath;
+        final Set<String> modules;
+        final Configuration configuration;
+        Hasher(ModuleDescriptor descriptor, String fname) throws IOException {
+            // Create a module finder that finds the modular JAR
+            // being created/updated
+            URI uri = Paths.get(fname).toUri();
+            ModuleReference mref = new ModuleReference(descriptor, uri,
+                new Supplier<>() {
+                    @Override
+                    public ModuleReader get() {
+                        throw new UnsupportedOperationException("should not reach here");
+                    }
+                });
+
+            // Compose a module finder with the module path and
+            // the modular JAR being created or updated
+            this.finder = ModuleFinder.compose(moduleFinder,
+                new ModuleFinder() {
+                    @Override
+                    public Optional<ModuleReference> find(String name) {
+                        if (descriptor.name().equals(name))
+                            return Optional.of(mref);
+                        else
+                            return Optional.empty();
+                    }
+
+                    @Override
+                    public Set<ModuleReference> findAll() {
+                        return Collections.singleton(mref);
+                    }
+                });
+
+            // Determine the modules that matches the modulesToHash pattern
+            this.modules = moduleFinder.findAll().stream()
+                .map(moduleReference -> moduleReference.descriptor().name())
+                .filter(mn -> modulesToHash.matcher(mn).find())
+                .collect(Collectors.toSet());
+
+            // a map from a module name to Path of the modular JAR
+            this.moduleNameToPath = moduleFinder.findAll().stream()
+                .map(ModuleReference::descriptor)
+                .map(ModuleDescriptor::name)
+                .collect(Collectors.toMap(Function.identity(), mn -> moduleToPath(mn)));
+
+            Configuration config = null;
+            try {
+                config = Configuration.empty()
+                    .resolveRequires(ModuleFinder.ofSystem(), finder, modules);
+            } catch (ResolutionException e) {
+                // should it throw an error?  or emit a warning
+                System.out.println("warning: " + e.getMessage());
             }
+            this.configuration = config;
         }
 
-        if (map.size() == 0) {
-            return null;
-        } else {
-            return Hasher.generate(map, "SHA-256");
+        /**
+         * Compute hashes of the modules that depend upon the specified
+         * module directly or indirectly.
+         */
+        ModuleHashes computeHashes(String name) {
+            // the transposed graph includes all modules in the resolved graph
+            Map<String, Set<String>> graph = transpose();
+
+            // find the modules that transitively depend upon the specified name
+            Deque<String> deque = new ArrayDeque<>();
+            deque.add(name);
+            Set<String> mods = visitNodes(graph, deque);
+
+            // filter modules matching the pattern specified --hash-modules
+            // as well as itself as the jmod file is being generated
+            Map<String, Path> modulesForHash = mods.stream()
+                .filter(mn -> !mn.equals(name) && modules.contains(mn))
+                .collect(Collectors.toMap(Function.identity(), moduleNameToPath::get));
+
+            if (modulesForHash.isEmpty())
+                return null;
+
+            return ModuleHashes.generate(modulesForHash, "SHA-256");
+        }
+
+        /**
+         * Returns all nodes traversed from the given roots.
+         */
+        private Set<String> visitNodes(Map<String, Set<String>> graph,
+                                       Deque<String> roots) {
+            Set<String> visited = new HashSet<>();
+            while (!roots.isEmpty()) {
+                String mn = roots.pop();
+                if (!visited.contains(mn)) {
+                    visited.add(mn);
+
+                    // the given roots may not be part of the graph
+                    if (graph.containsKey(mn)) {
+                        for (String dm : graph.get(mn)) {
+                            if (!visited.contains(dm))
+                                roots.push(dm);
+                        }
+                    }
+                }
+            }
+            return visited;
+        }
+
+        /**
+         * Returns a transposed graph from the resolved module graph.
+         */
+        private Map<String, Set<String>> transpose() {
+            Map<String, Set<String>> transposedGraph = new HashMap<>();
+            Deque<String> deque = new ArrayDeque<>(modules);
+
+            Set<String> visited = new HashSet<>();
+            while (!deque.isEmpty()) {
+                String mn = deque.pop();
+                if (!visited.contains(mn)) {
+                    visited.add(mn);
+
+                    // add an empty set
+                    transposedGraph.computeIfAbsent(mn, _k -> new HashSet<>());
+
+                    ResolvedModule resolvedModule = configuration.findModule(mn).get();
+                    for (ResolvedModule dm : resolvedModule.reads()) {
+                        String name = dm.name();
+                        if (!visited.contains(name)) {
+                            deque.push(name);
+                        }
+                        // reverse edge
+                        transposedGraph.computeIfAbsent(name, _k -> new HashSet<>())
+                                       .add(mn);
+                    }
+                }
+            }
+            return transposedGraph;
+        }
+
+        private Path moduleToPath(String name) {
+            ModuleReference mref = moduleFinder.find(name).orElseThrow(
+                () -> new InternalError(formatMsg2("error.hash.dep",name , name)));
+
+            URI uri = mref.location().get();
+            Path path = Paths.get(uri);
+            String fn = path.getFileName().toString();
+            if (!fn.endsWith(".jar")) {
+                throw new UnsupportedOperationException(path + " is not a modular JAR");
+            }
+            return path;
         }
     }
-
-    private static Path modRefToPath(ModuleReference mref) {
-        URI location = mref.location().get();
-        return Paths.get(location);
-    }
 }
--- a/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar.properties	Thu Apr 28 08:26:38 2016 -0700
+++ b/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar.properties	Tue May 03 09:09:57 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
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/builder/DefaultImageBuilder.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/builder/DefaultImageBuilder.java	Tue May 03 09:09:57 2016 +0100
@@ -95,27 +95,23 @@
 
     private final Path root;
     private final Path mdir;
-    private final boolean genBom;
     private final Set<String> modules = new HashSet<>();
 
     /**
      * Default image builder constructor.
      *
-     * @param genBom true, generates a bom file.
      * @param root The image root directory.
      * @throws IOException
      */
-    public DefaultImageBuilder(boolean genBom, Path root) throws IOException {
+    public DefaultImageBuilder(Path root) throws IOException {
         Objects.requireNonNull(root);
 
-        this.genBom = genBom;
-
         this.root = root;
         this.mdir = root.resolve("lib");
         Files.createDirectories(mdir);
     }
 
-    private void storeFiles(Set<String> modules, String bom, Properties release) throws IOException {
+    private void storeFiles(Set<String> modules, Properties release) throws IOException {
         if (release != null) {
             addModules(release, modules);
             File r = new File(root.toFile(), "release");
@@ -123,11 +119,6 @@
                 release.store(fo, null);
             }
         }
-        // Generate bom
-        if (genBom) {
-            File bomFile = new File(root.toFile(), "bom");
-            createUtf8File(bomFile, bom);
-        }
     }
 
     private void addModules(Properties release, Set<String> modules) throws IOException {
@@ -144,7 +135,7 @@
     }
 
     @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);
-                    }
                 }
             }
         }
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/builder/ImageBuilder.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/builder/ImageBuilder.java	Tue May 03 09:09:57 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");
     }
 
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImageFileCreator.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImageFileCreator.java	Tue May 03 09:09:57 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<Archive> archives,
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImagePluginConfiguration.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImagePluginConfiguration.java	Tue May 03 09:09:57 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<Plugin.CATEGORY, List<Plugin>> 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);
     }
 }
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImagePluginStack.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImagePluginStack.java	Tue May 03 09:09:57 2016 +0100
@@ -167,28 +167,25 @@
 
     private final ImageBuilder imageBuilder;
     private final Properties release;
-    private final String bom;
 
-    public ImagePluginStack(String bom) {
+    public ImagePluginStack() {
         this(null, Collections.emptyList(), null,
-                Collections.emptyList(), null, bom);
+                Collections.emptyList(), null);
+    }
+
+    public ImagePluginStack(ImageBuilder imageBuilder,
+            List<TransformerPlugin> contentPlugins,
+            Plugin lastSorter,
+            List<PostProcessorPlugin> postprocessingPlugins) {
+        this(imageBuilder, contentPlugins, lastSorter,
+            postprocessingPlugins, null);
     }
 
     public ImagePluginStack(ImageBuilder imageBuilder,
             List<TransformerPlugin> contentPlugins,
             Plugin lastSorter,
             List<PostProcessorPlugin> postprocessingPlugins,
-            String bom) {
-        this(imageBuilder, contentPlugins, lastSorter,
-            postprocessingPlugins, null, bom);
-    }
-
-    public ImagePluginStack(ImageBuilder imageBuilder,
-            List<TransformerPlugin> contentPlugins,
-            Plugin lastSorter,
-            List<PostProcessorPlugin> postprocessingPlugins,
-            PluginContext ctxt,
-            String bom) {
+            PluginContext ctxt) {
         Objects.requireNonNull(contentPlugins);
         this.lastSorter = lastSorter;
         for (TransformerPlugin p : contentPlugins) {
@@ -204,7 +201,6 @@
         }
         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 {
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/JlinkTask.java	Tue May 03 09:09:57 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 <T extends Throwable> void fail(Class<T> type,
             String format,
@@ -142,9 +143,6 @@
             }
             task.options.packagedModulesPath = path;
         }, true, "--keep-packaged-modules"),
-        new Option<JlinkTask>(false, (task, opt, arg) -> {
-            task.options.genbom = true;
-        }, true, "--genbom"),
         new Option<JlinkTask>(true, (task, opt, arg) -> {
             task.options.saveoptsfile = arg;
         }, "--saveopts"),
@@ -175,7 +173,6 @@
 
     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<String, Path> modulesToPath(Configuration cf) {
-        Map<String, Path> modPaths = new HashMap<>();
-        for (ResolvedModule resolvedModule : cf.modules()) {
-            ModuleReference mref = resolvedModule.reference();
-            URI uri = mref.location().get();
-            modPaths.put(mref.descriptor().name(), Paths.get(uri));
-        }
-        return modPaths;
-    }
-
     /*
      * Jlink API entry point.
      */
@@ -275,8 +268,7 @@
                                       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<URI> ouri = m.reference().location();
+        if (!ouri.isPresent())
+            throw new InternalError(m + " does not have a location");
+        URI uri = ouri.get();
+        return Paths.get(uri);
+    }
+
     private static ImageProvider createImageProvider(ModuleFinder finder,
                                                      Set<String> addMods,
                                                      Set<String> limitMods,
@@ -374,7 +374,8 @@
                                  ModuleFinder.empty(),
                                  addMods);
 
-        Map<String, Path> mods = modulesToPath(cf);
+        Map<String, Path> mods = cf.modules().stream()
+            .collect(Collectors.toMap(ResolvedModule::name, JlinkTask::toPathLocation));
         return new ImageHelper(cf, mods, order, retainModulesPath);
     }
 
@@ -399,21 +400,15 @@
             map.put(mref.descriptor().name(), mref);
         });
 
+        // add the other modules
+        otherMods.stream()
+            .map(finder::find)
+            .flatMap(Optional::stream)
+            .forEach(mref -> map.putIfAbsent(mref.descriptor().name(), mref));
+
         // set of modules that are observable
         Set<ModuleReference> mrefs = new HashSet<>(map.values());
 
-        // add the other modules
-        for (String mod : otherMods) {
-            Optional<ModuleReference> omref = finder.find(mod);
-            if (omref.isPresent()) {
-                ModuleReference mref = omref.get();
-                map.putIfAbsent(mod, mref);
-                mrefs.add(mref);
-            } else {
-                // no need to fail
-            }
-        }
-
         return new ModuleFinder() {
             @Override
             public Optional<ModuleReference> find(String name) {
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/TaskHelper.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/TaskHelper.java	Tue May 03 09:09:57 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() {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/packager/AppRuntimeImageBuilder.java	Tue May 03 09:09:57 2016 +0100
@@ -0,0 +1,146 @@
+/*
+ * 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 jdk.tools.jlink.internal.packager;
+
+
+import jdk.tools.jlink.Jlink;
+import jdk.tools.jlink.builder.ImageBuilder;
+import jdk.tools.jlink.plugin.Plugin;
+import jdk.tools.jlink.builder.*;
+import jdk.tools.jlink.plugin.Pool;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintWriter;
+import java.io.StringReader;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.ResourceBundle;
+import java.util.Set;
+import java.util.StringTokenizer;
+import java.util.TreeSet;
+import java.util.stream.Collectors;
+
+/**
+ * AppRuntimeImageBuilder is a private API used only by the Java Packager to generate
+ * a Java runtime image using jlink. AppRuntimeImageBuilder encapsulates the
+ * arguments that jlink requires to generate this image. To create the image call the
+ * build() method.
+ */
+public final class AppRuntimeImageBuilder {
+    private Path outputDir = null;
+    private List<Path> modulePath = null;
+    private Set<String> addModules = null;
+    private Set<String> limitModules = null;
+    private String excludeFileList = null;
+    private Map<String, String> userArguments = null;
+    private Boolean stripNativeCommands = null;
+
+    public AppRuntimeImageBuilder() {}
+
+    public void setOutputDir(Path value) {
+        outputDir = value;
+    }
+
+    public void setModulePath(List<Path> value) {
+        modulePath = value;
+    }
+
+    public void setAddModules(Set<String> value) {
+        addModules = value;
+    }
+
+    public void setLimitModules(Set<String> value) {
+        limitModules = value;
+    }
+
+    public void setExcludeFileList(String value) {
+        excludeFileList = value;
+    }
+
+    public void setStripNativeCommands(boolean value) {
+        stripNativeCommands = value;
+    }
+
+    public void setUserArguments(Map<String, String> value) {
+        userArguments = value;
+    }
+
+    public void build() throws IOException {
+        // jlink main arguments
+        Jlink.JlinkConfiguration jlinkConfig = new Jlink.JlinkConfiguration(
+            new File("").toPath(), // Unused
+            modulePath, addModules, limitModules);
+
+        // plugin configuration
+        List<Plugin> plugins = new ArrayList<Plugin>();
+
+        if (stripNativeCommands) {
+            plugins.add(Jlink.newPlugin(
+                        "strip-native-commands",
+                        Collections.singletonMap("strip-native-commands", "on"),
+                        null));
+        }
+
+        if (excludeFileList != null && !excludeFileList.isEmpty()) {
+            plugins.add(Jlink.newPlugin(
+                        "exclude-files",
+                        Collections.singletonMap("exclude-files", excludeFileList),
+                        null));
+        }
+
+        // add user supplied jlink arguments
+        for (Map.Entry<String, String> entry : userArguments.entrySet()) {
+            String key = entry.getKey();
+            String value = entry.getValue();
+            plugins.add(Jlink.newPlugin(key,
+                                        Collections.singletonMap(key, value),
+                                        null));
+        }
+
+        plugins.add(Jlink.newPlugin("installed-modules", Collections.emptyMap(), null));
+
+        // build the image
+        Jlink.PluginsConfiguration pluginConfig = new Jlink.PluginsConfiguration(
+            plugins, new DefaultImageBuilder(outputDir), null);
+        Jlink jlink = new Jlink();
+        jlink.build(jlinkConfig, pluginConfig);
+    }
+}
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/DefaultCompressPlugin.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/DefaultCompressPlugin.java	Tue May 03 09:09:57 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);
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/ExcludeVMPlugin.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/ExcludeVMPlugin.java	Tue May 03 09:09:57 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);
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/IncludeLocalesPlugin.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/IncludeLocalesPlugin.java	Tue May 03 09:09:57 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));
                 }
             })
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/OptimizationPlugin.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/OptimizationPlugin.java	Tue May 03 09:09:57 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);
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/SystemModuleDescriptorPlugin.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/SystemModuleDescriptorPlugin.java	Tue May 03 09:09:57 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<ModuleDescriptorBuilder> builders = new ArrayList<>();
 
+        // module name to hash
+        private final Map<String, String> modulesToHash = new HashMap<>();
+
         // map Set<String> to a specialized builder to allow them to be
         // deduplicated as they are requested
         private final Map<Set<String>, StringSetBuilder> stringSets = new HashMap<>();
@@ -268,6 +287,11 @@
                     "[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);
 
-                if (md.mainClass().isPresent()) {
-                    mainClass(md.mainClass().get());
-                }
+                // main class
+                md.mainClass().ifPresent(this::mainClass);
+
+                // hashes
+                JLMA.hashes(md).ifPresent(mh -> {
+                    algorithm(mh.algorithm());
+                    mh.names().forEach(mn -> moduleHash(mn, mh.hashFor(mn)));
+                });
 
                 putModuleDescriptor();
             }
@@ -603,7 +655,7 @@
             /*
              * Invoke Builder.provides(String service, Set<String> providers)
              *
-             * Set<String> providers = new HashSet<>();
+             * Set<String> providers = new LinkedHashSet<>();
              * providers.add(impl);
              * :
              * :
@@ -652,6 +704,22 @@
                 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<String> names;
+            final boolean linked;
             int refCount;
             int localVarIndex;
+
+            StringSetBuilder(Set<String> names, boolean linked) {
+                this.names = names;
+                this.linked = linked;
+            }
+
             StringSetBuilder(Set<String> 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",
-                                "<init>", "(I)V", false);
+                        mv.visitMethodInsn(INVOKESPECIAL, cn, "<init>", "(I)V", false);
 
                         mv.visitVarInsn(ASTORE, index);
                         for (String t : names) {
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/plugin/Plugin.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/plugin/Plugin.java	Tue May 03 09:09:57 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<String, String> 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<String, String> config, PluginContext ctx) {
         configure(config);
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/jlink.properties	Thu Apr 28 08:26:38 2016 -0700
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/resources/jlink.properties	Tue May 03 09:09:57 2016 +0100
@@ -33,9 +33,6 @@
 main.opt.endian=\
 \  --endian <little|big>             Byte order of generated jimage (default:native)
 
-main.opt.genbom=\
-\  --genbom                          Generate a bom file containing jlink info
-
 main.opt.saveopts=\
 \  --saveopts <filename>             Save jlink options in the given file
 
--- a/src/jdk.jlink/share/classes/jdk/tools/jmod/JmodTask.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/src/jdk.jlink/share/classes/jdk/tools/jmod/JmodTask.java	Tue May 03 09:09:57 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 <T extends Throwable> void fail(Class<T> type,
-                                           String format,
-                                           Object... args) throws T {
-        String msg = new Formatter().format(format, args).toString();
-        try {
-            T t = type.getConstructor(String.class).newInstance(msg);
-            throw t;
-        } catch (InstantiationException |
-                 InvocationTargetException |
-                 NoSuchMethodException |
-                 IllegalAccessException e) {
-            throw new InternalError("Unable to create an instance of " + type, e);
-        }
-    }
-
     private static final String PROGNAME = "jmod";
     private static final String MODULE_INFO = "module-info.class";
 
@@ -161,7 +152,8 @@
     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<PathMatcher> 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<String, Path> modulesToPath(Set<ModuleDescriptor> modules) {
-        ModuleFinder finder = options.moduleFinder;
-
-        Map<String,Path> modPaths = new HashMap<>();
-        for (ModuleDescriptor m : modules) {
-            String name = m.name();
-
-            Optional<ModuleReference> omref = finder.find(name);
-            if (!omref.isPresent()) {
-                // this should not happen, module path bug?
-                fail(InternalError.class,
-                     "Selected module %s not on module path",
-                     name);
-            }
-
-            URI uri = omref.get().location().get();
-            modPaths.put(name, Paths.get(uri));
-
-        }
-        return modPaths;
+    private boolean hashModules() {
+        return new Hasher(options.moduleFinder).run();
     }
 
     private boolean describe() throws IOException {
@@ -297,6 +275,8 @@
                   .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> requires = md.requires().stream().sorted().collect(toList());
-                    if (!requires.isEmpty()) {
-                        requires.forEach(r -> {
-                                sb.append("\n  requires ");
-                                if (!r.modifiers().isEmpty())
-                                  sb.append(toString(r.modifiers())).append(" ");
-                                sb.append(r.name());
-                            });
-                    }
+                    md.requires().stream()
+                        .sorted(Comparator.comparing(Requires::name))
+                        .forEach(r -> {
+                            sb.append("\n  requires ");
+                            if (!r.modifiers().isEmpty())
+                                sb.append(toString(r.modifiers())).append(" ");
+                            sb.append(r.name());
+                        });
 
-                    List<String> l = md.uses().stream().sorted().collect(toList());
-                    if (!l.isEmpty()) {
-                        l.forEach(sv -> sb.append("\n  uses ").append(sv));
-                    }
+                    md.uses().stream().sorted()
+                        .forEach(s -> sb.append("\n  uses ").append(s));
 
-                    List<ModuleDescriptor.Exports> exports = sortExports(md.exports());
-                    if (!exports.isEmpty()) {
-                        exports.forEach(ex -> sb.append("\n  exports ").append(ex));
-                    }
+                    md.exports().stream()
+                        .sorted(Comparator.comparing(Exports::source))
+                        .forEach(p -> sb.append("\n  exports ").append(p));
 
-                    l = md.conceals().stream().sorted().collect(toList());
-                    if (!l.isEmpty()) {
-                        l.forEach(p -> sb.append("\n  conceals ").append(p));
-                    }
+                    md.conceals().stream().sorted()
+                        .forEach(p -> sb.append("\n  conceals ").append(p));
 
-                    Map<String, ModuleDescriptor.Provides> provides = md.provides();
-                    if (!provides.isEmpty()) {
-                        provides.values().forEach(p ->
-                                sb.append("\n  provides ").append(p.service())
-                                  .append(" with ")
-                                  .append(toString(p.providers())));
-                    }
+                    md.provides().values().stream()
+                        .sorted(Comparator.comparing(Provides::service))
+                        .forEach(p -> sb.append("\n  provides ").append(p.service())
+                                        .append(" with ")
+                                        .append(toString(p.providers())));
 
-                    Optional<String> mc = md.mainClass();
-                    if (mc.isPresent())
-                        sb.append("\n  main-class " + mc.get());
+                    md.mainClass().ifPresent(v -> sb.append("\n  main-class " + v));
 
+                    md.osName().ifPresent(v -> sb.append("\n  operating-system-name " + v));
 
+                    md.osArch().ifPresent(v -> sb.append("\n  operating-system-architecture " + v));
 
-                    Optional<String> osname = md.osName();
-                    if (osname.isPresent())
-                        sb.append("\n  operating-system-name " + osname.get());
+                    md.osVersion().ifPresent(v -> sb.append("\n  operating-system-version " + v));
 
-                    Optional<String> osarch = md.osArch();
-                    if (osarch.isPresent())
-                        sb.append("\n  operating-system-architecture " + osarch.get());
+                    JLMA.hashes(md).ifPresent(
+                            hashes -> hashes.names().stream().sorted().forEach(
+                                    mod -> sb.append("\n  hashes ").append(mod).append(" ")
+                                             .append(hashes.algorithm()).append(" ")
+                                             .append(hashes.hashFor(mod))));
 
-                    Optional<String> osversion = md.osVersion();
-                    if (osversion.isPresent())
-                        sb.append("\n  operating-system-version " + osversion.get());
-
-                    try {
-                        Method m = ModuleDescriptor.class.getDeclaredMethod("hashes");
-                        m.setAccessible(true);
-                        @SuppressWarnings("unchecked")
-                        Optional<Hasher.DependencyHashes> optHashes =
-                                (Optional<Hasher.DependencyHashes>) m.invoke(md);
-
-                        if (optHashes.isPresent()) {
-                            Hasher.DependencyHashes hashes = optHashes.get();
-                            hashes.names().stream().forEach(mod ->
-                                    sb.append("\n  hashes ").append(mod).append(" ")
-                                      .append(hashes.algorithm()).append(" ")
-                                      .append(hashes.hashFor(mod)));
-                        }
-                    } catch (ReflectiveOperationException x) {
-                        throw new InternalError(x);
-                    }
                     out.println(sb.toString());
                     return true;
                 }
@@ -387,21 +338,6 @@
         return false;
     }
 
-    static List<ModuleDescriptor.Exports> sortExports(Set<ModuleDescriptor.Exports> exports) {
-        Map<String,ModuleDescriptor.Exports> map =
-                exports.stream()
-                       .collect(toMap(ModuleDescriptor.Exports::source,
-                                      identity()));
-        List<String> sources = exports.stream()
-                                      .map(ModuleDescriptor.Exports::source)
-                                      .sorted()
-                                      .collect(toList());
-
-        List<ModuleDescriptor.Exports> l = new ArrayList<>();
-        sources.forEach(e -> l.add(map.get(e)));
-        return l;
-    }
-
     private boolean create() throws IOException {
         JmodFileWriter jmod = new JmodFileWriter();
 
@@ -410,8 +346,9 @@
         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<Path> cmds = options.cmds;
         final List<Path> libs = options.libs;
         final List<Path> 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<PathMatcher> 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<Requires> dependences = descriptor.requires();
-                    extender.hashes(hashDependences(name, dependences));
+                if (hasher != null) {
+                    ModuleHashes moduleHashes = hasher.computeHashes(descriptor.name());
+                    if (moduleHashes != null) {
+                        extender.hashes(moduleHashes);
+                    } else {
+                        warning("warn.no.module.hashes", descriptor.name());
+                    }
                 }
 
                 // write the (possibly extended or modified) module-info.class
@@ -561,38 +499,56 @@
             }
         }
 
-        /**
-         * Examines the module dependences of the given module
-         * and computes the hash of any module that matches the
-         * pattern {@code dependenciesToHash}.
+        /*
+         * Hasher resolves a module graph using the --hash-modules PATTERN
+         * as the roots.
+         *
+         * The jmod file is being created and does not exist in the
+         * given modulepath.
          */
-        DependencyHashes hashDependences(String name, Set<Requires> moduleDependences)
-            throws IOException
-        {
-            Set<ModuleDescriptor> descriptors = new HashSet<>();
-            for (Requires md: moduleDependences) {
-                String dn = md.name();
-                if (dependenciesToHash.matcher(dn).find()) {
-                    try {
-                        Optional<ModuleReference> omref = moduleFinder.find(dn);
-                        if (!omref.isPresent()) {
-                            throw new RuntimeException("Hashing module " + name
-                                + " dependencies, unable to find module " + dn
-                                + " on module path");
+        private Hasher hasher() {
+            if (options.modulesToHash == null)
+                return null;
+
+            try {
+                Supplier<InputStream> miSupplier = newModuleInfoSupplier();
+                if (miSupplier == null) {
+                    throw new IOException(MODULE_INFO + " not found");
+                }
+
+                ModuleDescriptor descriptor;
+                try (InputStream in = miSupplier.get()) {
+                    descriptor = ModuleDescriptor.read(in);
+                }
+
+                URI uri = options.jmodFile.toUri();
+                ModuleReference mref = new ModuleReference(descriptor, uri, new Supplier<>() {
+                    @Override
+                    public ModuleReader get() {
+                        throw new UnsupportedOperationException();
+                    }
+                });
+
+                // compose a module finder with the module path and also
+                // a module finder that can find the jmod file being created
+                ModuleFinder finder = ModuleFinder.compose(options.moduleFinder,
+                    new ModuleFinder() {
+                        @Override
+                        public Optional<ModuleReference> find(String name) {
+                            if (descriptor.name().equals(name))
+                                return Optional.of(mref);
+                            else return Optional.empty();
                         }
-                        descriptors.add(omref.get().descriptor());
-                    } catch (FindException x) {
-                        throw new IOException("error reading module path", x);
-                    }
-                }
-            }
 
-            Map<String, Path> map = modulesToPath(descriptors);
-            if (map.size() == 0) {
-                return null;
-            } else {
-                // use SHA-256 for now, easy to make this configurable if needed
-                return Hasher.generate(map, "SHA-256");
+                        @Override
+                        public Set<ModuleReference> 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<String, Path> moduleNameToPath;
+        final Set<String> modules;
+        final Configuration configuration;
+        final boolean dryrun = options.dryrun;
+        Hasher(ModuleFinder finder) {
+            this.moduleFinder = finder;
+            // Determine the modules that matches the pattern {@code modulesToHash}
+            this.modules = moduleFinder.findAll().stream()
+                .map(mref -> mref.descriptor().name())
+                .filter(mn -> options.modulesToHash.matcher(mn).find())
+                .collect(Collectors.toSet());
+
+            // a map from a module name to Path of the packaged module
+            this.moduleNameToPath = moduleFinder.findAll().stream()
+                .map(mref -> mref.descriptor().name())
+                .collect(Collectors.toMap(Function.identity(), mn -> moduleToPath(mn)));
+
+            // get a resolved module graph
+            Configuration config = null;
+            try {
+                config = Configuration.empty()
+                    .resolveRequires(ModuleFinder.ofSystem(), moduleFinder, modules);
+            } catch (ResolutionException e) {
+                warning("warn.module.resolution.fail", e.getMessage());
+            }
+            this.configuration = config;
+        }
+
+        /**
+         * This method is for jmod hash command.
+         *
+         * Identify the base modules in the module graph, i.e. no outgoing edge
+         * to any of the modules to be hashed.
+         *
+         * For each base module M, compute the hashes of all modules that depend
+         * upon M directly or indirectly.  Then update M's module-info.class
+         * to record the hashes.
+         */
+        boolean run() {
+            if (configuration == null)
+                return false;
+
+            // transposed graph containing the the packaged modules and
+            // its transitive dependences matching --hash-modules
+            Map<String, Set<String>> graph = new HashMap<>();
+            for (String root : modules) {
+                Deque<String> deque = new ArrayDeque<>();
+                deque.add(root);
+                Set<String> visited = new HashSet<>();
+                while (!deque.isEmpty()) {
+                    String mn = deque.pop();
+                    if (!visited.contains(mn)) {
+                        visited.add(mn);
+
+                        if (modules.contains(mn))
+                            graph.computeIfAbsent(mn, _k -> new HashSet<>());
+
+                        ResolvedModule resolvedModule = configuration.findModule(mn).get();
+                        for (ResolvedModule dm : resolvedModule.reads()) {
+                            String name = dm.name();
+                            if (!visited.contains(name)) {
+                                deque.push(name);
+                            }
+
+                            // reverse edge
+                            if (modules.contains(name) && modules.contains(mn)) {
+                                graph.computeIfAbsent(name, _k -> new HashSet<>()).add(mn);
+                            }
+                        }
+                    }
+                }
+            }
+
+            if (dryrun)
+                out.println("Dry run:");
+
+            // each node in a transposed graph is a matching packaged module
+            // in which the hash of the modules that depend upon it is recorded
+            graph.entrySet().stream()
+                .filter(e -> !e.getValue().isEmpty())
+                .forEach(e -> {
+                    String mn = e.getKey();
+                    Map<String, Path> modulesForHash = e.getValue().stream()
+                            .collect(Collectors.toMap(Function.identity(),
+                                                      moduleNameToPath::get));
+                    ModuleHashes hashes = ModuleHashes.generate(modulesForHash, "SHA-256");
+                    if (dryrun) {
+                        out.format("%s%n", mn);
+                        hashes.names().stream()
+                              .sorted()
+                              .forEach(name -> out.format("  hashes %s %s %s%n",
+                                  name, hashes.algorithm(), hashes.hashFor(name)));
+                    } else {
+                        try {
+                            updateModuleInfo(mn, hashes);
+                        } catch (IOException ex) {
+                            throw new UncheckedIOException(ex);
+                        }
+                    }
+                });
+            return true;
+        }
+
+        /**
+         * Compute hashes of the specified module.
+         *
+         * It records the hashing modules that depend upon the specified
+         * module directly or indirectly.
+         */
+        ModuleHashes computeHashes(String name) {
+            if (configuration == null)
+                return null;
+
+            // the transposed graph includes all modules in the resolved graph
+            Map<String, Set<String>> graph = transpose();
+
+            // find the modules that transitively depend upon the specified name
+            Deque<String> deque = new ArrayDeque<>();
+            deque.add(name);
+            Set<String> mods = visitNodes(graph, deque);
+
+            // filter modules matching the pattern specified --hash-modules
+            // as well as itself as the jmod file is being generated
+            Map<String, Path> modulesForHash = mods.stream()
+                .filter(mn -> !mn.equals(name) && modules.contains(mn))
+                .collect(Collectors.toMap(Function.identity(), moduleNameToPath::get));
+
+            if (modulesForHash.isEmpty())
+                return null;
+
+           return ModuleHashes.generate(modulesForHash, "SHA-256");
+        }
+
+        /**
+         * Returns all nodes traversed from the given roots.
+         */
+        private Set<String> visitNodes(Map<String, Set<String>> graph,
+                                       Deque<String> roots) {
+            Set<String> visited = new HashSet<>();
+            while (!roots.isEmpty()) {
+                String mn = roots.pop();
+                if (!visited.contains(mn)) {
+                    visited.add(mn);
+                    // the given roots may not be part of the graph
+                    if (graph.containsKey(mn)) {
+                        for (String dm : graph.get(mn)) {
+                            if (!visited.contains(dm)) {
+                                roots.push(dm);
+                            }
+                        }
+                    }
+                }
+            }
+            return visited;
+        }
+
+        /**
+         * Returns a transposed graph from the resolved module graph.
+         */
+        private Map<String, Set<String>> transpose() {
+            Map<String, Set<String>> transposedGraph = new HashMap<>();
+            Deque<String> deque = new ArrayDeque<>(modules);
+
+            Set<String> visited = new HashSet<>();
+            while (!deque.isEmpty()) {
+                String mn = deque.pop();
+                if (!visited.contains(mn)) {
+                    visited.add(mn);
+
+                    transposedGraph.computeIfAbsent(mn, _k -> new HashSet<>());
+
+                    ResolvedModule resolvedModule = configuration.findModule(mn).get();
+                    for (ResolvedModule dm : resolvedModule.reads()) {
+                        String name = dm.name();
+                        if (!visited.contains(name)) {
+                            deque.push(name);
+                        }
+
+                        // reverse edge
+                        transposedGraph.computeIfAbsent(name, _k -> new HashSet<>())
+                                .add(mn);
+                    }
+                }
+            }
+            return transposedGraph;
+        }
+
+        /**
+         * Reads the given input stream of module-info.class and write
+         * the extended module-info.class with the given ModuleHashes
+         *
+         * @param in       InputStream of module-info.class
+         * @param out      OutputStream to write the extended module-info.class
+         * @param hashes   ModuleHashes
+         */
+        private void recordHashes(InputStream in, OutputStream out, ModuleHashes hashes)
+            throws IOException
+        {
+            ModuleInfoExtender extender = ModuleInfoExtender.newExtender(in);
+            extender.hashes(hashes);
+            extender.write(out);
+        }
+
+        private void updateModuleInfo(String name, ModuleHashes moduleHashes)
+            throws IOException
+        {
+            Path target = moduleNameToPath.get(name);
+            Path tempTarget = target.resolveSibling(target.getFileName() + ".tmp");
+            ZipFile zip = new ZipFile(target.toFile());
+            try {
+                try (OutputStream out = Files.newOutputStream(tempTarget);
+                     ZipOutputStream zos = new ZipOutputStream(out)) {
+                    zip.stream().forEach(e -> {
+                        try {
+                            InputStream in = zip.getInputStream(e);
+                            if (e.getName().equals(MODULE_INFO) ||
+                                e.getName().equals(Section.CLASSES.jmodDir() + "/" + MODULE_INFO)) {
+                                ZipEntry ze = new ZipEntry(e.getName());
+                                ze.setTime(System.currentTimeMillis());
+                                zos.putNextEntry(ze);
+                                recordHashes(in, zos, moduleHashes);
+                                zos.closeEntry();
+                            } else {
+                                zos.putNextEntry(e);
+                                zos.write(in.readAllBytes());
+                                zos.closeEntry();
+                            }
+                        } catch (IOException x) {
+                            throw new UncheckedIOException(x);
+                        }
+                    });
+                }
+            } catch (IOException|RuntimeException e) {
+                if (Files.exists(tempTarget)) {
+                    try {
+                        Files.delete(tempTarget);
+                    } catch (IOException ioe) {
+                        e.addSuppressed(ioe);
+                    }
+                }
+                throw e;
+            } finally {
+                zip.close();
+            }
+            out.println(getMessage("module.hashes.recorded", name));
+            Files.move(tempTarget, target, StandardCopyOption.REPLACE_EXISTING);
+        }
+
+        private Path moduleToPath(String name) {
+            ModuleReference mref = moduleFinder.find(name).orElseThrow(
+                () -> new InternalError("Selected module " + name + " not on module path"));
+
+            URI uri = mref.location().get();
+            Path path = Paths.get(uri);
+            String fn = path.getFileName().toString();
+            if (!fn.endsWith(".jar") && !fn.endsWith(".jmod")) {
+                throw new InternalError(path + " is not a modular JAR or jmod file");
+            }
+            return path;
+        }
+    }
+
     enum Section {
         NATIVE_LIBS("native"),
         NATIVE_CMDS("bin"),
@@ -921,7 +1144,8 @@
             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<Void> dryrun
+            = parser.accepts("dry-run", getMessage("main.opt.dry-run"));
+
         OptionSpec<PathMatcher> excludes
                 = parser.accepts("exclude", getMessage("main.opt.exclude"))
                         .withRequiredArg()
                         .withValuesConvertedBy(new GlobConverter());
 
-        OptionSpec<Pattern> hashDependencies
-                = parser.accepts("hash-dependencies", getMessage("main.opt.hash-dependencies"))
+        OptionSpec<Pattern> hashModules
+                = parser.accepts("hash-modules", getMessage("main.opt.hash-modules"))
                         .withRequiredArg()
                         .withValuesConvertedBy(new PatternConverter());
 
@@ -1049,6 +1276,8 @@
                 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);
--- a/src/jdk.jlink/share/classes/jdk/tools/jmod/resources/jmod.properties	Thu Apr 28 08:26:38 2016 -0700
+++ b/src/jdk.jlink/share/classes/jdk/tools/jmod/resources/jmod.properties	Tue May 03 09:09:57 2016 +0100
@@ -1,9 +1,9 @@
 main.usage.summary=\
-Usage: {0} (create|list|describe) <OPTIONS> <jmod-file>\n\
+Usage: {0} (create|list|describe|hash) <OPTIONS> <jmod-file>\n\
 use --help for a list of possible options
 
 main.usage=\
-Usage: {0} (create|list|describe) <OPTIONS> <jmod-file>
+Usage: {0} (create|list|describe|hash) <OPTIONS> <jmod-file>
 
 error.prefix=Error:
 warn.prefix=Warning:
@@ -14,6 +14,8 @@
 \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}
+
--- a/test/TEST.ROOT	Thu Apr 28 08:26:38 2016 -0700
+++ b/test/TEST.ROOT	Tue May 03 09:09:57 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
--- a/test/com/sun/corba/5036554/TestCorbaBug.sh	Thu Apr 28 08:26:38 2016 -0700
+++ b/test/com/sun/corba/5036554/TestCorbaBug.sh	Tue May 03 09:09:57 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
 
--- a/test/com/sun/corba/7130985/CorbaExceptionsCompileTest.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/test/com/sun/corba/7130985/CorbaExceptionsCompileTest.java	Tue May 03 09:09:57 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.*;
--- a/test/com/sun/corba/se/impl/io/HookPutFieldsTest.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/test/com/sun/corba/se/impl/io/HookPutFieldsTest.java	Tue May 03 09:09:57 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;
--- a/test/com/sun/corba/se/impl/orb/SetDefaultORBTest.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/test/com/sun/corba/se/impl/orb/SetDefaultORBTest.java	Tue May 03 09:09:57 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
  *
  */
 
--- a/test/com/sun/net/httpserver/bugs/B6373555.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/test/com/sun/net/httpserver/bugs/B6373555.java	Tue May 03 09:09:57 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.*;
--- a/test/java/lang/invoke/VarargsArrayTest.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/test/java/lang/invoke/VarargsArrayTest.java	Tue May 03 09:09:57 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
  */
 
--- a/test/java/lang/module/ModuleFinderTest.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/test/java/lang/module/ModuleFinderTest.java	Tue May 03 09:09:57 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
      */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/lang/reflect/WeakPairMap/Driver.java	Tue May 03 09:09:57 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);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/lang/reflect/WeakPairMap/java.base/java/lang/reflect/WeakPairMapTest.java	Tue May 03 09:09:57 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<Object, Object, String> pm = new WeakPairMap<>();
+        Object key1 = new Object();
+        Object key2 = new Object();
+
+        // check for emptiness
+        assertEquals(pm.containsKeyPair(key1, key2), false);
+        assertEquals(pm.get(key1, key2), null);
+
+        // check for NPE(s)
+        for (Object k1 : new Object[]{null, key1}) {
+            for (Object k2 : new Object[]{null, key2}) {
+                for (String v : new String[]{null, "abc"}) {
+
+                    if (k1 != null && k2 != null && v != null) {
+                        // skip non-null args
+                        continue;
+                    }
+
+                    try {
+                        pm.put(k1, k2, v);
+                        throw new AssertionError("Unexpected code path, k1=" +
+                                                 k1 + ", k2=" + k2 + ", v=" + v);
+                    } catch (NullPointerException e) {
+                        // expected
+                    }
+
+                    try {
+                        pm.putIfAbsent(k1, k2, v);
+                        throw new AssertionError("Unexpected code path, k1=" +
+                                                 k1 + ", k2=" + k2 + ", v=" + v);
+                    } catch (NullPointerException e) {
+                        // expected
+                    }
+
+                    if (k1 != null && k2 != null) {
+                        // skip non-null args
+                        continue;
+                    }
+
+                    try {
+                        pm.computeIfAbsent(k1, k2, (_k1, _k2) -> v);
+                        throw new AssertionError("Unexpected code path, k1=" +
+                                                 k1 + ", k2=" + k2 + ", v=" + v);
+                    } catch (NullPointerException e) {
+                        // expected
+                    }
+
+                    try {
+                        pm.containsKeyPair(k1, k2);
+                        throw new AssertionError("Unexpected code path, k1=" +
+                                                 k1 + ", k2=" + k2);
+                    } catch (NullPointerException e) {
+                        // expected
+                    }
+
+                    try {
+                        pm.get(k1, k2);
+                        throw new AssertionError("Unexpected code path, k1=" +
+                                                 k1 + ", k2=" + k2);
+                    } catch (NullPointerException e) {
+                        // expected
+                    }
+                }
+            }
+        }
+
+        // how much to wait when it is expected for entry to be retained
+        final long retentionTimeout = 500L;
+        // how much to wait when it is expected for entry to be removed
+        final long cleanupTimeout = 30_000L;
+
+        // check insertion
+        assertEquals(pm.putIfAbsent(key1, key2, "abc"), null);
+        assertEquals(pm.get(key1, key2), "abc");
+
+        // check retention while both keys are still reachable
+        assertEquals(gcAndWaitRemoved(pm, "abc", retentionTimeout), false);
+        assertEquals(pm.get(key1, key2), "abc");
+
+        // check cleanup when both keys are unreachable
+        key1 = null;
+        key2 = null;
+        assertEquals(gcAndWaitRemoved(pm, "abc", cleanupTimeout), true);
+
+        // new insertion
+        key1 = new Object();
+        key2 = new Object();
+        assertEquals(pm.putIfAbsent(key1, key2, "abc"), null);
+        assertEquals(pm.get(key1, key2), "abc");
+
+        // check retention while both keys are still reachable
+        assertEquals(gcAndWaitRemoved(pm, "abc", retentionTimeout), false);
+        assertEquals(pm.get(key1, key2), "abc");
+
+        // check cleanup when 1st key is unreachable
+        key1 = null;
+        assertEquals(gcAndWaitRemoved(pm, "abc", cleanupTimeout), true);
+        Reference.reachabilityFence(key2);
+
+        // new insertion
+        key1 = new Object();
+        key2 = new Object();
+        assertEquals(pm.putIfAbsent(key1, key2, "abc"), null);
+        assertEquals(pm.get(key1, key2), "abc");
+
+        // check retention while both keys are still reachable
+        assertEquals(gcAndWaitRemoved(pm, "abc", retentionTimeout), false);
+        assertEquals(pm.get(key1, key2), "abc");
+
+        // check cleanup when 2nd key is unreachable
+        key2 = null;
+        assertEquals(gcAndWaitRemoved(pm, "abc", cleanupTimeout), true);
+        Reference.reachabilityFence(key1);
+    }
+
+    /**
+     * Trigger GC and wait for at most {@code millis} ms for given value to
+     * be removed from given WeakPairMap.
+     *
+     * @return true if element has been removed or false if not
+     */
+    static <V> boolean gcAndWaitRemoved(WeakPairMap<?, ?, V> pm, V value,
+                                        long millis) {
+        System.gc();
+        for (int i = 0; i < (millis + 99) / 100 && pm.values().contains(value); i++) {
+            try {
+                Thread.sleep(100L);
+            } catch (InterruptedException e) {
+                throw new AssertionError("Interrupted");
+            }
+        }
+        return !pm.values().contains(value);
+    }
+
+    static void assertEquals(Object actual, Object expected) {
+        if (!Objects.equals(actual, expected)) {
+            throw new AssertionError("Expected: " + expected + ", actual: " + actual);
+        }
+    }
+}
--- a/test/java/util/ResourceBundle/Bug6299235Test.sh	Thu Apr 28 08:26:38 2016 -0700
+++ b/test/java/util/ResourceBundle/Bug6299235Test.sh	Tue May 03 09:09:57 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 ]
--- a/test/javax/crypto/Cipher/CipherStreamClose.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/test/javax/crypto/Cipher/CipherStreamClose.java	Tue May 03 09:09:57 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.*;
--- a/test/javax/rmi/PortableRemoteObject/ConcurrentHashMapTest.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/test/javax/rmi/PortableRemoteObject/ConcurrentHashMapTest.java	Tue May 03 09:09:57 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<String> commands = new ArrayList<>();
         commands.add(ConcurrentHashMapTest.JAVA);
+        commands.add("-addmods");
+        commands.add("java.corba");
         commands.add("-Djava.naming.factory.initial=com.sun.jndi.cosnaming.CNCtxFactory");
         commands.add("-Djava.naming.provider.url=iiop://localhost:1050");
         commands.add("-cp");
--- a/test/javax/smartcardio/CommandAPDUTest.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/test/javax/smartcardio/CommandAPDUTest.java	Tue May 03 09:09:57 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;
--- a/test/javax/smartcardio/HistoricalBytes.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/test/javax/smartcardio/HistoricalBytes.java	Tue May 03 09:09:57 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;
 
--- a/test/javax/smartcardio/ResponseAPDUTest.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/test/javax/smartcardio/ResponseAPDUTest.java	Tue May 03 09:09:57 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.*;
--- a/test/javax/smartcardio/Serialize.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/test/javax/smartcardio/Serialize.java	Tue May 03 09:09:57 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.*;
--- a/test/javax/smartcardio/TerminalFactorySpiTest.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/test/javax/smartcardio/TerminalFactorySpiTest.java	Tue May 03 09:09:57 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;
--- a/test/javax/smartcardio/TestCardPermission.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/test/javax/smartcardio/TestCardPermission.java	Tue May 03 09:09:57 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.*;
--- a/test/javax/smartcardio/TestCommandAPDU.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/test/javax/smartcardio/TestCommandAPDU.java	Tue May 03 09:09:57 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.*;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/javax/transaction/testng/Driver.java	Tue May 03 09:09:57 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
+ */
--- a/test/javax/transaction/testng/TEST.properties	Thu Apr 28 08:26:38 2016 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,4 +0,0 @@
-# JDBC unit tests uses TestNG
-TestNG.dirs= .
-othervm.dirs= .
-
--- a/test/javax/xml/bind/xjc/8032884/XjcOptionalPropertyTest.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/test/javax/xml/bind/xjc/8032884/XjcOptionalPropertyTest.java	Tue May 03 09:09:57 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
  */
 
--- a/test/javax/xml/jaxp/common/8035437/run.sh	Thu Apr 28 08:26:38 2016 -0700
+++ b/test/javax/xml/jaxp/common/8035437/run.sh	Tue May 03 09:09:57 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
 
--- a/test/javax/xml/soap/XmlTest.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/test/javax/xml/soap/XmlTest.java	Tue May 03 09:09:57 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
  */
--- a/test/javax/xml/soap/spi/SAAJFactoryTest.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/test/javax/xml/soap/spi/SAAJFactoryTest.java	Tue May 03 09:09:57 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 {
 
--- a/test/javax/xml/ws/8043129/MailTest.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/test/javax/xml/ws/8043129/MailTest.java	Tue May 03 09:09:57 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;
--- a/test/javax/xml/ws/clientjar/TestWsImport.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/test/javax/xml/ws/clientjar/TestWsImport.java	Tue May 03 09:09:57 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;
--- a/test/javax/xml/ws/publish/WSTest.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/test/javax/xml/ws/publish/WSTest.java	Tue May 03 09:09:57 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;
--- a/test/javax/xml/ws/xsanymixed/Test.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/test/javax/xml/ws/xsanymixed/Test.java	Tue May 03 09:09:57 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;
--- a/test/sun/security/pkcs11/KeyStore/SecretKeysBasic.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/test/sun/security/pkcs11/KeyStore/SecretKeysBasic.java	Tue May 03 09:09:57 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");
         }
--- a/test/sun/security/provider/PolicyFile/Modules.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/test/sun/security/provider/PolicyFile/Modules.java	Tue May 03 09:09:57 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;
--- a/test/tools/jar/modularJar/Basic.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/test/tools/jar/modularJar/Basic.java	Tue May 03 09:09:57 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 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,
+            "--no-manifest",
+            "-C", barClasses.toString(), ".").assertSuccess();
+
         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,
+            "--modulepath=" + mp.toString(),
+            "--hash-modules=" + "bar",
             "--no-manifest",
             "-C", fooClasses.toString(), ".").assertSuccess();
 
-        Path barClasses = MODULE_CLASSES.resolve(BAR.moduleName);
-        Path barJar = mp.resolve(BAR.moduleName + ".jar");
+        // 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,
-            "--modulepath=" + mp.toString(),
-            "--hash-dependencies=" + "foo",  // dependency on foo
+            "--module-version=" + BAR.version + ".1", // a newer version
             "--no-manifest",
             "-C", barClasses.toString(), ".").assertSuccess();
 
-        // Rebuild foo.jar with a change that will cause its hash to be different
-        FileUtils.deleteFileWithRetry(fooJar);
-        jar("--create",
-            "--file=" + fooJar.toString(),
-            "--main-class=" + FOO.mainClass,
-            "--module-version=" + FOO.version + ".1", // a newer version
-            "--no-manifest",
-            "-C", fooClasses.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
     }
--- a/test/tools/jar/modularJar/src/bar/jdk/test/bar/Bar.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/test/tools/jar/modularJar/src/bar/jdk/test/bar/Bar.java	Tue May 03 09:09:57 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<Hasher.DependencyHashes> optHashes =
-                (Optional<Hasher.DependencyHashes>) m.invoke(md);
+        ModuleDescriptor foo = jdk.test.foo.Foo.class.getModule().getDescriptor();
+        Optional<ModuleHashes> oHashes =
+                (Optional<ModuleHashes>) m.invoke(foo);
 
-        System.out.println("hashes:" + optHashes.get().hashFor("foo"));
+        System.out.println("hashes:" + oHashes.get().hashFor("bar"));
 
         StringJoiner sj = new StringJoiner(",");
         md.conceals().forEach(sj::add);
--- a/test/tools/jlink/ImageFileCreatorTest.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/test/tools/jlink/ImageFileCreatorTest.java	Tue May 03 09:09:57 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);
     }
--- a/test/tools/jlink/IntegrationTest.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/test/tools/jlink/IntegrationTest.java	Tue May 03 09:09:57 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;
--- a/test/tools/jlink/JLink2Test.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/test/tools/jlink/JLink2Test.java	Tue May 03 09:09:57 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<Plugin> builtInPlugins = new ArrayList<>();
         builtInPlugins.addAll(PluginRepository.getPlugins(Layer.boot()));
--- a/test/tools/jlink/JLinkTest.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/test/tools/jlink/JLinkTest.java	Tue May 03 09:09:57 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
--- a/test/tools/jlink/hashes/HashesTest.java	Thu Apr 28 08:26:38 2016 -0700
+++ /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<Path>() {
-            @Override
-            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
-                Files.delete(file);
-                return FileVisitResult.CONTINUE;
-            }
-
-            @Override
-            public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
-                Files.delete(dir);
-                return FileVisitResult.CONTINUE;
-            }
-        });
-    }
-
-    private void runJava(int expectedExitCode, String... args) throws Exception {
-        OutputAnalyzer analyzer = ProcessTools.executeTestJava(args);
-        if (analyzer.getExitValue() != expectedExitCode) {
-            throw new AssertionError("Expected exit code: " + expectedExitCode +
-                    ", got: " + analyzer.getExitValue());
-        }
-    }
-
-    private void compileClasses(Path src, Path output, String... options) throws IOException {
-        List<String> args = new ArrayList<>();
-        Collections.addAll(args, options);
-        Collections.addAll(args, "-d", output.toString());
-        args.add(src.toString());
-        System.out.println("javac options: " + args.stream().collect(Collectors.joining(" ")));
-        if (!CompilerUtils.compile(src.resolve(output.getFileName()), output, options)) {
-            throw new AssertionError("Compilation failure. See log.");
-        }
-    }
-
-    private void runJmod(String cp, String modName, String... options) {
-        List<String> args = new ArrayList<>();
-        args.add("create");
-        Collections.addAll(args, options);
-        Collections.addAll(args, "--class-path", cp,
-                jmods + File.separator + modName + ".jmod");
-        int rc = jdk.tools.jmod.Main.run(args.toArray(new String[args.size()]), System.out);
-        System.out.println("jmod options: " + args.stream().collect(Collectors.joining(" ")));
-        if (rc != 0) {
-            throw new AssertionError("Jmod failed: rc = " + rc);
-        }
-    }
-}
--- a/test/tools/jlink/hashes/newsrc/m2/module-info.java	Thu Apr 28 08:26:38 2016 -0700
+++ /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;
-}
--- a/test/tools/jlink/hashes/newsrc/m2/org/m2/Util.java	Thu Apr 28 08:26:38 2016 -0700
+++ /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";
-    }
-}
--- a/test/tools/jlink/hashes/newsrc/not_matched/module-info.java	Thu Apr 28 08:26:38 2016 -0700
+++ /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;
-}
--- a/test/tools/jlink/hashes/newsrc/not_matched/org/not_matched/Name.java	Thu Apr 28 08:26:38 2016 -0700
+++ /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";
-    }
-}
--- a/test/tools/jlink/hashes/src/m1/module-info.java	Thu Apr 28 08:26:38 2016 -0700
+++ /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;
-}
--- a/test/tools/jlink/hashes/src/m1/org/m1/Main.java	Thu Apr 28 08:26:38 2016 -0700
+++ /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());
-    }
-}
--- a/test/tools/jlink/hashes/src/m2/module-info.java	Thu Apr 28 08:26:38 2016 -0700
+++ /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;
-}
--- a/test/tools/jlink/hashes/src/m2/org/m2/Util.java	Thu Apr 28 08:26:38 2016 -0700
+++ /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";
-    }
-}
--- a/test/tools/jlink/hashes/src/not_matched/module-info.java	Thu Apr 28 08:26:38 2016 -0700
+++ /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;
-}
--- a/test/tools/jlink/hashes/src/not_matched/org/not_matched/Name.java	Thu Apr 28 08:26:38 2016 -0700
+++ /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";
-    }
-}
--- a/test/tools/jlink/plugins/FileCopierPluginTest.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/test/tools/jlink/plugins/FileCopierPluginTest.java	Tue May 03 09:09:57 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");
--- a/test/tools/jmod/JmodNegativeTest.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/test/tools/jmod/JmodNegativeTest.java	Tue May 03 09:09:57 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<Supplier<JmodResult>> tasks = Arrays.asList(
                 () -> jmod("create",
-                           "--hash-dependencies", "anyPattern",
+                           "--hash-modules", "anyPattern",
                            "--modulepath", "doesNotExist",
                            "output.jmod"),
                 () -> jmod("create",
@@ -436,7 +432,7 @@
 
         List<Supplier<JmodResult>> tasks = Arrays.asList(
             () -> jmod("create",
-                       "--hash-dependencies", "anyPattern",
+                       "--hash-modules", "anyPattern",
                        "--modulepath","empty" + pathSeparator + "doesNotExist",
                        "output.jmod"),
             () -> jmod("create",
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/jmod/hashes/HashesTest.java	Tue May 03 09:09:57 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<ModuleHashes> 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<ModuleHashes> hashes =
+                (Optional<ModuleHashes>) 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<Path>() {
+            @Override
+            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
+                throws IOException
+            {
+                Files.delete(file);
+                return FileVisitResult.CONTINUE;
+            }
+
+            @Override
+            public FileVisitResult postVisitDirectory(Path dir, IOException exc)
+                throws IOException
+            {
+                Files.delete(dir);
+                return FileVisitResult.CONTINUE;
+            }
+        });
+    }
+
+    private void 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<String> 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<String> 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);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/jmod/hashes/src/m1/module-info.java	Tue May 03 09:09:57 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;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/jmod/hashes/src/m1/org/m1/Main.java	Tue May 03 09:09:57 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());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/jmod/hashes/src/m2/module-info.java	Tue May 03 09:09:57 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;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/jmod/hashes/src/m2/org/m2/Util.java	Tue May 03 09:09:57 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";
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/jmod/hashes/src/m3/module-info.java	Tue May 03 09:09:57 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;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/jmod/hashes/src/m3/org/m3/Name.java	Tue May 03 09:09:57 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";
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/jmod/hashes/src/org.bar/module-info.java	Tue May 03 09:09:57 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;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/jmod/hashes/src/org.foo/module-info.java	Tue May 03 09:09:57 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;
+}
--- a/test/tools/launcher/ToolsOpts.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/test/tools/launcher/ToolsOpts.java	Tue May 03 09:09:57 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;
--- a/test/tools/launcher/modules/addmods/AddModsTest.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/test/tools/launcher/modules/addmods/AddModsTest.java	Tue May 03 09:09:57 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");
+    private static final Path MODS1_DIR = Paths.get("mods1");
+    private static final Path MODS2_DIR = Paths.get("mods2");
 
-    // the module name of the library module
-    private static final String LIB_MODULE = "lib";
+    // 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 source directory
-    private static final String APP_SRC = "app";
-
-    // 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 mods1/test src/test/**
+        boolean compiled = CompilerUtils.compile(
+            SRC_DIR.resolve(TEST_MODULE),
+            MODS1_DIR.resolve(TEST_MODULE)
+        );
+        assertTrue(compiled, "test did not compile");
 
-        // javac -d mods/$LIB_MODULE src/$LIB_MODULE/**
-        boolean compiled = CompilerUtils.compile(
-            SRC_DIR.resolve(LIB_MODULE),
-            MODS_DIR.resolve(LIB_MODULE)
+        // javac -d mods1/logger src/logger/**
+        compiled= CompilerUtils.compile(
+            SRC_DIR.resolve(LOGGER_MODULE),
+            MODS2_DIR.resolve(LOGGER_MODULE)
         );
-        assertTrue(compiled, "library module 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
-        );
-        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 {
+    public void testAddDefaultModules1() throws Exception {
 
-        executeTestJava("-addmods", "ALL-SYSTEM",
-                        "-listmods",
-                        "-m", "java.base")
-            .outputTo(System.out)
-            .errorTo(System.out)
-            .shouldContain("java.sql")
-            .shouldContain("java.corba");
-
-        // 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 -mp mods -addmods lib -cp classes app.Main
+        // java -addmods ALL-DEFAULT -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-DEFAULT",
+                              "-m", TEST_MID,
+                              "javax.annotation.Generated")
+                .outputTo(System.out)
+                .errorTo(System.out)
+                .shouldContain("ClassNotFoundException")
+                .getExitValue();
+
+        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", 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);
-
     }
 
 }
--- a/test/tools/launcher/modules/addmods/src/app/Main.java	Thu Apr 28 08:26:38 2016 -0700
+++ /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();
-    }
-}
--- a/test/tools/launcher/modules/addmods/src/lib/jdk/lib/Util.java	Thu Apr 28 08:26:38 2016 -0700
+++ /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();
-    }
-}
--- a/test/tools/launcher/modules/addmods/src/lib/module-info.java	Thu Apr 28 08:26:38 2016 -0700
+++ /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;
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/launcher/modules/addmods/src/logger/logger/Logger.java	Tue May 03 09:09:57 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 {
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/launcher/modules/addmods/src/logger/module-info.java	Tue May 03 09:09:57 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 { }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/launcher/modules/addmods/src/test/module-info.java	Tue May 03 09:09:57 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 { }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/launcher/modules/addmods/src/test/test/Main.java	Tue May 03 09:09:57 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);
+        }
+    }
+}
--- a/test/tools/launcher/modules/addreads/AddReadsTest.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/test/tools/launcher/modules/addreads/AddReadsTest.java	Tue May 03 09:09:57 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
  */
--- a/test/tools/launcher/modules/patch/PatchTest.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/test/tools/launcher/modules/patch/PatchTest.java	Tue May 03 09:09:57 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);
 
     }
 
--- a/test/tools/lib/tests/JImageGenerator.java	Thu Apr 28 08:26:38 2016 -0700
+++ b/test/tools/lib/tests/JImageGenerator.java	Tue May 03 09:09:57 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<Path> jmods = new ArrayList<>();
         private final List<String> 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);