8178380: Module system implementation refresh (5/2017)
authoralanb
Thu, 04 May 2017 07:26:55 +0000
changeset 45004 ea3137042a61
parent 44789 73fd39e0702e
child 45005 467584d298c4
8178380: Module system implementation refresh (5/2017) Reviewed-by: mchung, alanb, sspitsyn Contributed-by: alan.bateman@oracle.com, mandy.chung@oracle.com, chris.hegarty@oracle.com
jdk/make/mapfiles/libinstrument/mapfile-vers
jdk/src/java.base/share/classes/java/lang/ClassLoader.java
jdk/src/java.base/share/classes/java/lang/Module.java
jdk/src/java.base/share/classes/java/lang/SecurityManager.java
jdk/src/java.base/share/classes/java/lang/System.java
jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java
jdk/src/java.base/share/classes/java/lang/module/Configuration.java
jdk/src/java.base/share/classes/java/lang/module/ModuleDescriptor.java
jdk/src/java.base/share/classes/java/lang/module/ModuleFinder.java
jdk/src/java.base/share/classes/java/lang/module/Resolver.java
jdk/src/java.base/share/classes/java/nio/file/FileSystems.java
jdk/src/java.base/share/classes/jdk/internal/loader/BuiltinClassLoader.java
jdk/src/java.base/share/classes/jdk/internal/misc/JavaLangAccess.java
jdk/src/java.base/share/classes/jdk/internal/module/Checks.java
jdk/src/java.base/share/classes/jdk/internal/module/ClassFileAttributes.java
jdk/src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java
jdk/src/java.base/share/classes/jdk/internal/module/ModuleHashesBuilder.java
jdk/src/java.base/share/classes/jdk/internal/module/ModuleInfo.java
jdk/src/java.base/share/classes/jdk/internal/module/ModuleInfoExtender.java
jdk/src/java.base/share/classes/jdk/internal/module/ModuleInfoWriter.java
jdk/src/java.base/share/classes/jdk/internal/module/ModuleLoaderMap.java
jdk/src/java.base/share/classes/jdk/internal/module/ModulePatcher.java
jdk/src/java.base/share/classes/jdk/internal/module/ModulePath.java
jdk/src/java.base/share/classes/jdk/internal/module/ModuleReferences.java
jdk/src/java.base/share/classes/jdk/internal/module/ModuleResolution.java
jdk/src/java.base/share/classes/jdk/internal/module/ModuleTarget.java
jdk/src/java.base/share/classes/jdk/internal/module/Modules.java
jdk/src/java.base/share/classes/jdk/internal/module/Resources.java
jdk/src/java.base/share/classes/module-info.java
jdk/src/java.base/share/classes/sun/launcher/LauncherHelper.java
jdk/src/java.base/share/classes/sun/launcher/resources/launcher.properties
jdk/src/java.base/share/native/libjli/emessages.h
jdk/src/java.base/share/native/libjli/java.c
jdk/src/java.instrument/share/classes/java/lang/instrument/Instrumentation.java
jdk/src/java.instrument/share/classes/java/lang/instrument/package.html
jdk/src/java.instrument/share/classes/module-info.java
jdk/src/java.instrument/share/classes/sun/instrument/InstrumentationImpl.java
jdk/src/java.instrument/share/native/libinstrument/InstrumentationImplNativeMethods.c
jdk/src/java.instrument/share/native/libinstrument/InvocationAdapter.c
jdk/src/java.management/share/classes/java/lang/management/ThreadInfo.java
jdk/src/jdk.attach/aix/classes/sun/tools/attach/VirtualMachineImpl.java
jdk/src/jdk.attach/linux/classes/sun/tools/attach/VirtualMachineImpl.java
jdk/src/jdk.attach/macosx/classes/sun/tools/attach/VirtualMachineImpl.java
jdk/src/jdk.attach/share/classes/sun/tools/attach/HotSpotVirtualMachine.java
jdk/src/jdk.attach/solaris/classes/sun/tools/attach/VirtualMachineImpl.java
jdk/src/jdk.attach/windows/classes/sun/tools/attach/VirtualMachineImpl.java
jdk/src/jdk.jartool/share/classes/sun/tools/jar/Main.java
jdk/src/jdk.jartool/share/classes/sun/tools/jar/Validator.java
jdk/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar.properties
jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/builder/DefaultImageBuilder.java
jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImagePluginStack.java
jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/Platform.java
jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ResourcePoolManager.java
jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/ReleaseInfoPlugin.java
jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/SystemModulesPlugin.java
jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/plugin/ResourcePoolModule.java
jdk/src/jdk.jlink/share/classes/jdk/tools/jmod/JmodTask.java
jdk/src/jdk.jlink/share/classes/jdk/tools/jmod/resources/jmod.properties
jdk/test/com/sun/management/HotSpotDiagnosticMXBean/CheckOrigin.java
jdk/test/com/sun/tools/attach/AttachSelf.java
jdk/test/com/sun/tools/attach/RunnerUtil.java
jdk/test/com/sun/tools/attach/java.policy.allow
jdk/test/com/sun/tools/attach/modules/Agent.java
jdk/test/com/sun/tools/attach/modules/Driver.java
jdk/test/com/sun/tools/attach/modules/m/module-info.java
jdk/test/com/sun/tools/attach/modules/m/p/Main.java
jdk/test/java/lang/instrument/executableJAR/Agent.java
jdk/test/java/lang/instrument/executableJAR/AgentHelper.java
jdk/test/java/lang/instrument/executableJAR/ExecJarWithAgent.java
jdk/test/java/lang/instrument/executableJAR/Main.java
jdk/test/java/lang/invoke/DefineClassTest.java
jdk/test/java/lang/module/AutomaticModulesTest.java
jdk/test/java/lang/module/ConfigurationTest.java
jdk/test/java/lang/module/ModuleDescriptorTest.java
jdk/test/java/lang/module/customfs/ModulesInCustomFileSystem.java
jdk/test/java/lang/module/customfs/m1/module-info.java
jdk/test/java/lang/module/customfs/m1/p/Main.java
jdk/test/java/lang/module/customfs/m2/module-info.java
jdk/test/java/lang/module/customfs/m2/q/Hello.java
jdk/test/java/nio/file/spi/SetDefaultProvider.java
jdk/test/java/nio/file/spi/TestProvider.java
jdk/test/java/nio/file/spi/m/module-info.java
jdk/test/java/nio/file/spi/m/p/Main.java
jdk/test/java/util/logging/TestLoggerWeakRefLeak.java
jdk/test/lib/testlibrary/ModuleTargetHelper.java
jdk/test/sun/management/jmxremote/bootstrap/JvmstatCountersTest.java
jdk/test/tools/jar/mmrjar/Basic.java
jdk/test/tools/jar/modularJar/Basic.java
jdk/test/tools/jar/multiRelease/whitebox/Driver.java
jdk/test/tools/jar/multiRelease/whitebox/jdk.jartool/sun/tools/jar/ValidatorComparatorTest.java
jdk/test/tools/jlink/IntegrationTest.java
jdk/test/tools/jlink/JLinkNegativeTest.java
jdk/test/tools/jlink/plugins/SystemModuleDescriptors/SystemModulesTest.java
jdk/test/tools/jlink/plugins/SystemModuleDescriptors/UserModuleTest.java
jdk/test/tools/jlink/plugins/SystemModuleDescriptors/src/m1/p1/Main.java
jdk/test/tools/jlink/plugins/SystemModuleDescriptors/src/m4/p4/Main.java
jdk/test/tools/jmod/JmodTest.java
jdk/test/tools/launcher/modules/describe/DescribeModuleTest.java
jdk/test/tools/launcher/modules/listmods/ListModsTest.java
jdk/test/tools/launcher/modules/listmods/src/java.transaction/javax/transaction/atomic/Atomic.java
jdk/test/tools/launcher/modules/listmods/src/java.transaction/module-info.java
jdk/test/tools/launcher/modules/patch/systemmodules/PatchSystemModules.java
jdk/test/tools/launcher/modules/showmoduleresolution/ShowModuleResolutionTest.java
jdk/test/tools/launcher/modules/validate/ValidateModulesTest.java
jdk/test/tools/pack200/pack200-verifier/src/xmlkit/ClassReader.java
--- a/jdk/make/mapfiles/libinstrument/mapfile-vers	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/make/mapfiles/libinstrument/mapfile-vers	Thu May 04 07:26:55 2017 +0000
@@ -39,6 +39,7 @@
             Java_sun_instrument_InstrumentationImpl_getObjectSize0;
 	    Java_sun_instrument_InstrumentationImpl_appendToClassLoaderSearch0;
 	    Java_sun_instrument_InstrumentationImpl_setNativeMethodPrefixes;
+            Java_sun_instrument_InstrumentationImpl_loadAgent0;
 	local:
 		*;
 };
--- a/jdk/src/java.base/share/classes/java/lang/ClassLoader.java	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/src/java.base/share/classes/java/lang/ClassLoader.java	Thu May 04 07:26:55 2017 +0000
@@ -119,18 +119,24 @@
  * The Java run-time has the following built-in class loaders:
  *
  * <ul>
- * <li>Bootstrap class loader.
+ * <li><p>Bootstrap class loader.
  *     It is the virtual machine's built-in class loader, typically represented
  *     as {@code null}, and does not have a parent.</li>
- * <li>{@linkplain #getPlatformClassLoader() Platform class loader}.
+ * <li><p>{@linkplain #getPlatformClassLoader() Platform class loader}.
  *     All <em>platform classes</em> are visible to the platform class loader
  *     that can be used as the parent of a {@code ClassLoader} instance.
  *     Platform classes include Java SE platform APIs, their implementation
  *     classes and JDK-specific run-time classes that are defined by the
- *     platform class loader or its ancestors.</li>
- * <li>{@linkplain #getSystemClassLoader() System class loader}.
- *     It is also known as <em>application class
- *     loader</em> and is distinct from the platform class loader.
+ *     platform class loader or its ancestors.
+ *     <p> To allow for upgrading/overriding of modules defined to the platform
+ *     class loader, and where classes in the upgraded version link to
+ *     classes in modules defined to the application class loader, the
+ *     platform class loader may delegate to the application class loader.
+ *     In other words, classes in named modules defined to the application
+ *     class loader may be visible to the platform class loader. </li>
+ * <li><p>{@linkplain #getSystemClassLoader() System class loader}.
+ *     It is also known as <em>application class loader</em> and is distinct
+ *     from the platform class loader.
  *     The system class loader is typically used to define classes on the
  *     application class path, module path, and JDK-specific tools.
  *     The platform class loader is a parent or an ancestor of the system class
@@ -368,6 +374,10 @@
      * Creates a new class loader of the specified name and using the
      * specified parent class loader for delegation.
      *
+     * @apiNote If the parent is specified as {@code null} (for the
+     * bootstrap class loader) then there is no guarantee that all platform
+     * classes are visible.
+     *
      * @param  name   class loader name; or {@code null} if not named
      * @param  parent the parent class loader
      *
@@ -390,9 +400,12 @@
      * delegation.
      *
      * <p> If there is a security manager, its {@link
-     * SecurityManager#checkCreateClassLoader()
-     * checkCreateClassLoader} method is invoked.  This may result in
-     * a security exception.  </p>
+     * SecurityManager#checkCreateClassLoader() checkCreateClassLoader} method
+     * is invoked.  This may result in a security exception.  </p>
+     *
+     * @apiNote If the parent is specified as {@code null} (for the
+     * bootstrap class loader) then there is no guarantee that all platform
+     * classes are visible.
      *
      * @param  parent
      *         The parent class loader
@@ -2206,6 +2219,12 @@
      * this class loader are searched recursively (parent by parent)
      * for a {@code Package} of the given name.
      *
+     * @apiNote The {@link #getPlatformClassLoader() platform class loader}
+     * may delegate to the application class loader but the application class
+     * loader is not its ancestor.  When invoked on the platform class loader,
+     * this method  will not find packages defined to the application
+     * class loader.
+     *
      * @param  name
      *         The <a href="#name">package name</a>
      *
@@ -2251,6 +2270,14 @@
      * {@code Package} object of the same package name, each defined by
      * a different class loader in the class loader hierarchy.
      *
+     * @apiNote The {@link #getPlatformClassLoader() platform class loader}
+     * may delegate to the application class loader. In other words,
+     * packages in modules defined to the application class loader may be
+     * visible to the platform class loader.  On the other hand,
+     * the application class loader is not its ancestor and hence
+     * when invoked on the platform class loader, this method will not
+     * return any packages defined to the application class loader.
+     *
      * @return  The array of {@code Package} objects defined by this
      *          class loader and its ancestors
      *
--- a/jdk/src/java.base/share/classes/java/lang/Module.java	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/src/java.base/share/classes/java/lang/Module.java	Thu May 04 07:26:55 2017 +0000
@@ -57,6 +57,7 @@
 import jdk.internal.loader.BootLoader;
 import jdk.internal.misc.JavaLangAccess;
 import jdk.internal.misc.SharedSecrets;
+import jdk.internal.module.ModuleLoaderMap;
 import jdk.internal.module.ServicesCatalog;
 import jdk.internal.module.Resources;
 import jdk.internal.org.objectweb.asm.AnnotationVisitor;
@@ -215,8 +216,8 @@
     }
 
     /**
-     * Returns the layer that contains this module or {@code null} if this
-     * module is not in a layer.
+     * Returns the module layer that contains this module or {@code null} if
+     * this module is not in a module layer.
      *
      * A module layer contains named modules and therefore this method always
      * returns {@code null} when invoked on an unnamed module.
@@ -691,6 +692,13 @@
      * <p> This method has no effect if the package is already <em>open</em>
      * to the given module. </p>
      *
+     * @apiNote This method can be used for cases where a <em>consumer
+     * module</em> uses a qualified opens to open a package to an <em>API
+     * module</em> but where the reflective access to the members of classes in
+     * the consumer module is delegated to code in another module. Code in the
+     * API module can use this method to open the package in the consumer module
+     * to the other module.
+     *
      * @param  pn
      *         The package name
      * @param  other
@@ -1077,7 +1085,7 @@
             if (loader != null) {
                 moduleToLoader.put(name, loader);
                 loaders.add(loader);
-            } else if (!isBootLayer) {
+            } else if (!(clf instanceof ModuleLoaderMap.Mapper)) {
                 throw new IllegalArgumentException("loader can't be 'null'");
             }
         }
@@ -1458,11 +1466,11 @@
      *     encapsulated. </li>
      *
      *     <li> A <em>package name</em> is derived from the resource name. If
-     *     the package name is a {@link #getPackages() package} in the module
-     *     then the resource can only be located by the caller of this method
-     *     when the package is {@link #isOpen(String,Module) open} to at least
-     *     the caller's module. If the resource is not in a package in the module
-     *     then the resource is not encapsulated. </li>
+     *     the package name is a {@linkplain #getPackages() package} in the
+     *     module then the resource can only be located by the caller of this
+     *     method when the package is {@linkplain #isOpen(String,Module) open}
+     *     to at least the caller's module. If the resource is not in a
+     *     package in the module then the resource is not encapsulated. </li>
      * </ul>
      *
      * <p> In the above, the <em>package name</em> for a resource is derived
@@ -1521,8 +1529,7 @@
         }
 
         // locate resource in module
-        JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
-        URL url = jla.findResource(loader, mn, name);
+        URL url = loader.findResource(mn, name);
         if (url != null) {
             try {
                 return url.openStream();
--- a/jdk/src/java.base/share/classes/java/lang/SecurityManager.java	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/src/java.base/share/classes/java/lang/SecurityManager.java	Thu May 04 07:26:55 2017 +0000
@@ -25,10 +25,10 @@
 
 package java.lang;
 
-import java.lang.RuntimePermission;
 import java.lang.module.ModuleDescriptor;
 import java.lang.module.ModuleDescriptor.Exports;
 import java.lang.module.ModuleDescriptor.Opens;
+import java.lang.module.ModuleReference;
 import java.lang.reflect.Member;
 import java.io.FileDescriptor;
 import java.io.File;
@@ -42,12 +42,15 @@
 import java.security.Security;
 import java.security.SecurityPermission;
 import java.util.HashSet;
+import java.util.Map;
 import java.util.Objects;
 import java.util.PropertyPermission;
 import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.stream.Collectors;
-import java.util.stream.Stream;
 
+import jdk.internal.module.ModuleBootstrap;
+import jdk.internal.module.ModuleLoaderMap;
 import jdk.internal.reflect.CallerSensitive;
 import sun.security.util.SecurityConstants;
 
@@ -1431,29 +1434,29 @@
         return packages;
     }
 
-    // The non-exported packages of the modules in the boot layer that are
-    // loaded by the platform class loader or its ancestors. A non-exported
-    // package is a package that either is not exported at all by its containing
-    // module or is exported in a qualified fashion by its containing module.
-    private static final Set<String> nonExportedPkgs;
-
+    // The non-exported packages in modules defined to the boot or platform
+    // class loaders. A non-exported package is a package that is not exported
+    // or is only exported to specific modules.
+    private static final Map<String, Boolean> nonExportedPkgs = new ConcurrentHashMap<>();
     static {
-        // Get the modules in the boot layer
-        Stream<Module> bootLayerModules = ModuleLayer.boot().modules().stream();
+        addNonExportedPackages(ModuleLayer.boot());
+    }
 
-        // Filter out the modules loaded by the boot or platform loader
-        PrivilegedAction<Set<Module>> pa = () ->
-            bootLayerModules.filter(SecurityManager::isBootOrPlatformModule)
-                            .collect(Collectors.toSet());
-        Set<Module> modules = AccessController.doPrivileged(pa);
+    /**
+     * Record the non-exported packages of the modules in the given layer
+     */
+    static void addNonExportedPackages(ModuleLayer layer) {
+        Set<String> bootModules = ModuleLoaderMap.bootModules();
+        Set<String> platformModules = ModuleLoaderMap.platformModules();
+        layer.modules().stream()
+                .map(Module::getDescriptor)
+                .filter(md -> bootModules.contains(md.name())
+                        || platformModules.contains(md.name()))
+                .map(SecurityManager::nonExportedPkgs)
+                .flatMap(Set::stream)
+                .forEach(pn -> nonExportedPkgs.put(pn, Boolean.TRUE));
+    }
 
-        // Filter out the non-exported packages
-        nonExportedPkgs = modules.stream()
-                                 .map(Module::getDescriptor)
-                                 .map(SecurityManager::nonExportedPkgs)
-                                 .flatMap(Set::stream)
-                                 .collect(Collectors.toSet());
-    }
 
     /**
      * Called by java.security.Security
@@ -1468,14 +1471,6 @@
     }
 
     /**
-     * Returns true if the module's loader is the boot or platform loader.
-     */
-    private static boolean isBootOrPlatformModule(Module m) {
-        return m.getClassLoader() == null ||
-               m.getClassLoader() == ClassLoader.getPlatformClassLoader();
-    }
-
-    /**
      * Returns the non-exported packages of the specified module.
      */
     private static Set<String> nonExportedPkgs(ModuleDescriptor md) {
@@ -1535,7 +1530,7 @@
         Objects.requireNonNull(pkg, "package name can't be null");
 
         // check if pkg is not exported to all modules
-        if (nonExportedPkgs.contains(pkg)) {
+        if (nonExportedPkgs.containsKey(pkg)) {
             checkPermission(
                 new RuntimePermission("accessClassInPackage." + pkg));
             return;
@@ -1634,7 +1629,7 @@
         Objects.requireNonNull(pkg, "package name can't be null");
 
         // check if pkg is not exported to all modules
-        if (nonExportedPkgs.contains(pkg)) {
+        if (nonExportedPkgs.containsKey(pkg)) {
             checkPermission(
                 new RuntimePermission("defineClassInPackage." + pkg));
             return;
--- a/jdk/src/java.base/share/classes/java/lang/System.java	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/src/java.base/share/classes/java/lang/System.java	Thu May 04 07:26:55 2017 +0000
@@ -41,7 +41,6 @@
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
 import java.net.URI;
-import java.net.URL;
 import java.security.AccessControlContext;
 import java.security.ProtectionDomain;
 import java.security.AccessController;
@@ -2111,9 +2110,6 @@
             public Class<?> findBootstrapClassOrNull(ClassLoader cl, String name) {
                 return cl.findBootstrapClassOrNull(name);
             }
-            public URL findResource(ClassLoader cl, String mn, String name) throws IOException {
-                return cl.findResource(mn, name);
-            }
             public Stream<Package> packages(ClassLoader cl) {
                 return cl.packages();
             }
@@ -2123,6 +2119,9 @@
             public String fastUUID(long lsb, long msb) {
                 return Long.fastUUID(lsb, msb);
             }
+            public void addNonExportedPackages(ModuleLayer layer) {
+                SecurityManager.addNonExportedPackages(layer);
+            }
             public void invalidatePackageAccessCache() {
                 SecurityManager.invalidatePackageAccessCache();
             }
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java	Thu May 04 07:26:55 2017 +0000
@@ -876,9 +876,7 @@
          * accessible to the class. The {@code PACKAGE} lookup mode serves to authenticate
          * that the lookup object was created by a caller in the runtime package (or derived
          * from a lookup originally created by suitably privileged code to a target class in
-         * the runtime package). The lookup modes cannot include {@link #PRIVATE PRIVATE}
-         * access. A lookup with {@code PRIVATE} access can be downgraded to drop this lookup
-         * mode with the {@linkplain #dropLookupMode(int) dropLookupMode} method. </p>
+         * the runtime package). </p>
          *
          * <p> The {@code bytes} parameter is the class bytes of a valid class file (as defined
          * by the <em>The Java Virtual Machine Specification</em>) with a class name in the
@@ -896,7 +894,6 @@
          * @throws IllegalArgumentException the bytes are for a class in a different package
          * to the lookup class
          * @throws IllegalAccessException if this lookup does not have {@code PACKAGE} access
-         * @throws UnsupportedOperationException if the lookup class has {@code PRIVATE} access
          * @throws LinkageError if the class is malformed ({@code ClassFormatError}), cannot be
          * verified ({@code VerifyError}), is already defined, or another linkage error occurs
          * @throws SecurityException if denied by the security manager
@@ -911,8 +908,6 @@
             SecurityManager sm = System.getSecurityManager();
             if (sm != null)
                 sm.checkPermission(new RuntimePermission("defineClass"));
-            if (hasPrivateAccess())
-                throw new UnsupportedOperationException("PRIVATE access not supported");
             if ((lookupModes() & PACKAGE) == 0)
                 throw new IllegalAccessException("Lookup does not have PACKAGE access");
             assert (lookupModes() & (MODULE|PUBLIC)) != 0;
--- a/jdk/src/java.base/share/classes/java/lang/module/Configuration.java	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/src/java.base/share/classes/java/lang/module/Configuration.java	Thu May 04 07:26:55 2017 +0000
@@ -109,20 +109,17 @@
     private final Set<ResolvedModule> modules;
     private final Map<String, ResolvedModule> nameToModule;
 
-    // module constraints on target
-    private final String osName;
-    private final String osArch;
+    // constraint on target platform
+    private final String targetPlatform;
 
-    String osName() { return osName; }
-    String osArch() { return osArch; }
+    String targetPlatform() { return targetPlatform; }
 
     private Configuration() {
         this.parents = Collections.emptyList();
         this.graph = Collections.emptyMap();
         this.modules = Collections.emptySet();
         this.nameToModule = Collections.emptyMap();
-        this.osName = null;
-        this.osArch = null;
+        this.targetPlatform = null;
     }
 
     private Configuration(List<Configuration> parents,
@@ -147,8 +144,7 @@
         this.modules = Set.of(moduleArray);
         this.nameToModule = Map.ofEntries(nameEntries);
 
-        this.osName = resolver.osName();
-        this.osArch = resolver.osArch();
+        this.targetPlatform = resolver.targetPlatform();
     }
 
     /**
--- a/jdk/src/java.base/share/classes/java/lang/module/ModuleDescriptor.java	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/src/java.base/share/classes/java/lang/module/ModuleDescriptor.java	Thu May 04 07:26:55 2017 +0000
@@ -99,6 +99,7 @@
      *
      * @see ModuleDescriptor#modifiers()
      * @since 9
+     * @spec JPMS
      */
     public static enum Modifier {
         /**
--- a/jdk/src/java.base/share/classes/java/lang/module/ModuleFinder.java	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/src/java.base/share/classes/java/lang/module/ModuleFinder.java	Thu May 04 07:26:55 2017 +0000
@@ -286,8 +286,9 @@
      *     class names of provider classes. </p></li>
      *
      *     <li><p> If the JAR file has a {@code Main-Class} attribute in its
-     *     main manifest then its value is the module {@link
-     *     ModuleDescriptor#mainClass() main class}. </p></li>
+     *     main manifest, its value is a legal class name, and its package is
+     *     in the set of packages derived for the module, then the value is the
+     *     module {@linkplain ModuleDescriptor#mainClass() main class}. </p></li>
      *
      * </ul>
      *
@@ -298,8 +299,7 @@
      * file, where the JAR file contains a {@code .class} in the top-level
      * directory of the JAR file, where an entry in a service configuration
      * file is not a legal class name or its package name is not in the set of
-     * packages derived for the module, or where the module main class is not
-     * a legal class name or its package is not in the module. </p>
+     * packages derived for the module. </p>
      *
      * <p> In addition to JAR files, an implementation may also support modules
      * that are packaged in other implementation specific module formats. If
--- a/jdk/src/java.base/share/classes/java/lang/module/Resolver.java	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/src/java.base/share/classes/java/lang/module/Resolver.java	Thu May 04 07:26:55 2017 +0000
@@ -28,6 +28,7 @@
 import java.io.PrintStream;
 import java.lang.module.ModuleDescriptor.Provides;
 import java.lang.module.ModuleDescriptor.Requires.Modifier;
+import java.net.URI;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -38,10 +39,8 @@
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
-import java.util.Objects;
 import java.util.Optional;
 import java.util.Set;
-import java.util.StringJoiner;
 import java.util.stream.Collectors;
 
 import jdk.internal.module.ModuleHashes;
@@ -69,12 +68,10 @@
     // true if all automatic modules have been found
     private boolean haveAllAutomaticModules;
 
-    // module constraints on target platform
-    private String osName;
-    private String osArch;
+    // constraint on target platform
+    private String targetPlatform;
 
-    String osName() { return osName; }
-    String osArch() { return osArch; }
+    String targetPlatform() { return targetPlatform; }
 
     /**
      * @throws IllegalArgumentException if there are more than one parent and
@@ -89,37 +86,23 @@
         this.afterFinder = afterFinder;
         this.traceOutput = traceOutput;
 
-        // record constraints on target platform, checking that they don't conflict
+        // record constraint on target platform, checking for conflicts
         for (Configuration parent : parents) {
-            String value = parent.osName();
+            String value = parent.targetPlatform();
             if (value != null) {
-                if (osName == null) {
-                    osName = value;
+                if (targetPlatform == null) {
+                    targetPlatform = value;
                 } else {
-                    if (!value.equals(osName)) {
-                        failParentConflict("Operating System", osName, value);
-                    }
-                }
-            }
-            value = parent.osArch();
-            if (value != null) {
-                if (osArch == null) {
-                    osArch = value;
-                } else {
-                    if (!value.equals(osArch)) {
-                        failParentConflict("OS architecture", osArch, value);
+                    if (!value.equals(targetPlatform)) {
+                        String msg = "Parents have conflicting constraints on target" +
+                                     "  platform: " + targetPlatform + ", " + value;
+                        throw new IllegalArgumentException(msg);
                     }
                 }
             }
         }
     }
 
-    private void failParentConflict(String constraint, String s1, String s2) {
-        String msg = "Parents have conflicting constraints on target "
-                     + constraint + ": " + s1 + ", " + s2;
-        throw new IllegalArgumentException(msg);
-    }
-
     /**
      * Resolves the given named modules.
      *
@@ -147,8 +130,7 @@
             }
 
             if (isTracing()) {
-                trace("Root module %s located", root);
-                mref.location().ifPresent(uri -> trace("  (%s)", uri));
+                trace("root %s", nameAndInfo(mref));
             }
 
             addFoundModule(mref);
@@ -180,9 +162,7 @@
                     ModuleDescriptor other = mref.descriptor();
                     q.offer(other);
                     if (isTracing()) {
-                        trace("Automatic module %s located, required by %s",
-                              other.name(), descriptor.name());
-                        mref.location().ifPresent(uri -> trace("  (%s)", uri));
+                        trace("%s requires %s", descriptor.name(), nameAndInfo(mref));
                     }
                 });
                 haveAllAutomaticModules = true;
@@ -213,21 +193,13 @@
                     }
                 }
 
+                if (isTracing() && !dn.equals("java.base")) {
+                    trace("%s requires %s", descriptor.name(), nameAndInfo(mref));
+                }
+
                 if (!nameToReference.containsKey(dn)) {
                     addFoundModule(mref);
                     q.offer(mref.descriptor());
-
-                    if (isTracing()) {
-                        String prefix;
-                        if (mref.descriptor().isAutomatic()) {
-                            prefix = "Automatic module";
-                        } else {
-                            prefix = "Module";
-                        }
-                        trace(prefix + " %s located, required by %s",
-                              dn, descriptor.name());
-                        mref.location().ifPresent(uri -> trace("  (%s)", uri));
-                    }
                 }
 
             }
@@ -291,6 +263,13 @@
         do {
             for (ModuleDescriptor descriptor : candidateConsumers) {
                 if (!descriptor.uses().isEmpty()) {
+
+                    // the modules that provide at least one service
+                    Set<ModuleDescriptor> modulesToBind = null;
+                    if (isTracing()) {
+                        modulesToBind = new HashSet<>();
+                    }
+
                     for (String service : descriptor.uses()) {
                         Set<ModuleReference> mrefs = availableProviders.get(service);
                         if (mrefs != null) {
@@ -298,15 +277,13 @@
                                 ModuleDescriptor provider = mref.descriptor();
                                 if (!provider.equals(descriptor)) {
 
-                                    trace("Module %s provides %s, used by %s",
-                                            provider.name(), service, descriptor.name());
+                                    if (isTracing() && modulesToBind.add(provider)) {
+                                        trace("%s binds %s", descriptor.name(),
+                                                nameAndInfo(mref));
+                                    }
 
                                     String pn = provider.name();
                                     if (!nameToReference.containsKey(pn)) {
-                                        if (isTracing()) {
-                                            mref.location()
-                                                .ifPresent(uri -> trace("  (%s)", uri));
-                                        }
                                         addFoundModule(mref);
                                         q.push(provider);
                                     }
@@ -349,59 +326,31 @@
         if (mref instanceof ModuleReferenceImpl) {
             ModuleTarget target = ((ModuleReferenceImpl)mref).moduleTarget();
             if (target != null)
-                checkTargetConstraints(mn, target);
+                checkTargetPlatform(mn, target);
         }
 
         nameToReference.put(mn, mref);
     }
 
     /**
-     * Check that the module's constraints on the target platform do not
-     * conflict with the constraints of other modules resolved so far or
-     * modules in parent configurations.
+     * Check that the module's constraints on the target platform does
+     * conflict with the constraint of other modules resolved so far.
      */
-    private void checkTargetConstraints(String mn, ModuleTarget target) {
-        String value = target.osName();
+    private void checkTargetPlatform(String mn, ModuleTarget target) {
+        String value = target.targetPlatform();
         if (value != null) {
-            if (osName == null) {
-                osName = value;
+            if (targetPlatform == null) {
+                targetPlatform = value;
             } else {
-                if (!value.equals(osName)) {
-                    failTargetConstraint(mn, target);
-                }
-            }
-        }
-        value = target.osArch();
-        if (value != null) {
-            if (osArch == null) {
-                osArch = value;
-            } else {
-                if (!value.equals(osArch)) {
-                    failTargetConstraint(mn, target);
+                if (!value.equals(targetPlatform)) {
+                    findFail("Module %s has constraints on target platform (%s)"
+                             + " that conflict with other modules: %s", mn,
+                             value, targetPlatform);
                 }
             }
         }
     }
 
-    private void failTargetConstraint(String mn, ModuleTarget target) {
-        String s1 = targetAsString(osName, osArch);
-        String s2 = targetAsString(target.osName(), target.osArch());
-        findFail("Module %s has constraints on target platform (%s) that"
-                 + " conflict with other modules: %s", mn, s1, s2);
-    }
-
-    private String targetAsString(ModuleTarget target) {
-        return targetAsString(target.osName(), target.osArch());
-    }
-
-    private String targetAsString(String osName, String osArch) {
-        return new StringJoiner("-")
-                .add(Objects.toString(osName, "*"))
-                .add(Objects.toString(osArch, "*"))
-                .toString();
-    }
-
-
     /**
      * Execute post-resolution checks and returns the module graph of resolved
      * modules as {@code Map}. The resolved modules will be in the given
@@ -412,12 +361,6 @@
     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));
-        }
-
         if (check) {
             detectCycles();
             checkHashes();
@@ -520,9 +463,8 @@
                     findFail("Unable to compute the hash of module %s", dn);
                 }
 
-                // skip checking the hash if the module has been patched
                 ModuleReferenceImpl other = (ModuleReferenceImpl)mref2;
-                if (other != null && !other.isPatched()) {
+                if (other != null) {
                     byte[] recordedHash = hashes.hashFor(dn);
                     byte[] actualHash = other.computeHash(algorithm);
                     if (actualHash == null)
@@ -965,9 +907,17 @@
 
     private void trace(String fmt, Object ... args) {
         if (traceOutput != null) {
-            traceOutput.format("[Resolver] " + fmt, args);
+            traceOutput.format(fmt, args);
             traceOutput.println();
         }
     }
 
+    private String nameAndInfo(ModuleReference mref) {
+        ModuleDescriptor descriptor = mref.descriptor();
+        StringBuilder sb = new StringBuilder(descriptor.name());
+        mref.location().ifPresent(uri -> sb.append(" " + uri));
+        if (descriptor.isAutomatic())
+            sb.append(" automatic");
+        return sb.toString();
+    }
 }
--- a/jdk/src/java.base/share/classes/java/nio/file/FileSystems.java	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/src/java.base/share/classes/java/nio/file/FileSystems.java	Thu May 04 07:26:55 2017 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -36,6 +36,8 @@
 import java.util.ServiceConfigurationError;
 import java.util.ServiceLoader;
 
+import jdk.internal.misc.VM;
+
 /**
  * Factory methods for file systems. This class defines the {@link #getDefault
  * getDefault} method to get the default file system and factory methods to
@@ -120,8 +122,8 @@
 
             // if the property java.nio.file.spi.DefaultFileSystemProvider is
             // set then its value is the name of the default provider (or a list)
-            String propValue = System
-                .getProperty("java.nio.file.spi.DefaultFileSystemProvider");
+            String prop = "java.nio.file.spi.DefaultFileSystemProvider";
+            String propValue = System.getProperty(prop);
             if (propValue != null) {
                 for (String cn: propValue.split(",")) {
                     try {
@@ -184,7 +186,7 @@
      * @return  the default file system
      */
     public static FileSystem getDefault() {
-        if (jdk.internal.misc.VM.isBooted()) {
+        if (VM.isModuleSystemInited()) {
             return DefaultFileSystemHolder.defaultFileSystem;
         } else {
             return BuiltinFileSystemHolder.builtinFileSystem;
--- a/jdk/src/java.base/share/classes/jdk/internal/loader/BuiltinClassLoader.java	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/src/java.base/share/classes/jdk/internal/loader/BuiltinClassLoader.java	Thu May 04 07:26:55 2017 +0000
@@ -172,12 +172,10 @@
     }
 
     /**
-     * Register a module this this class loader. This has the effect of making
-     * the types in the module visible.
+     * Register a module this class loader. This has the effect of making the
+     * types in the module visible.
      */
     public void loadModule(ModuleReference mref) {
-        assert !VM.isModuleSystemInited();
-
         String mn = mref.descriptor().name();
         if (nameToModule.putIfAbsent(mn, mref) != null) {
             throw new InternalError(mn + " already defined to this loader");
@@ -191,6 +189,11 @@
                                         + other.mref().descriptor().name());
             }
         }
+
+        // clear resources cache if VM is already initialized
+        if (VM.isModuleSystemInited() && resourceCache != null) {
+            resourceCache = null;
+        }
     }
 
     /**
@@ -355,7 +358,10 @@
     private List<URL> findMiscResource(String name) throws IOException {
         SoftReference<Map<String, List<URL>>> ref = this.resourceCache;
         Map<String, List<URL>> map = (ref != null) ? ref.get() : null;
-        if (map != null) {
+        if (map == null) {
+            map = new ConcurrentHashMap<>();
+            this.resourceCache = new SoftReference<>(map);
+        } else {
             List<URL> urls = map.get(name);
             if (urls != null)
                 return urls;
@@ -381,23 +387,18 @@
                                 }
                             }
                         }
-                        return result;
+                        return (result != null) ? result : Collections.emptyList();
                     }
                 });
         } catch (PrivilegedActionException pae) {
             throw (IOException) pae.getCause();
         }
 
-        // only cache resources after all modules have been defined
+        // only cache resources after VM is fully initialized
         if (VM.isModuleSystemInited()) {
-            if (map == null) {
-                map = new ConcurrentHashMap<>();
-                this.resourceCache = new SoftReference<>(map);
-            }
-            if (urls == null)
-                urls = Collections.emptyList();
             map.putIfAbsent(name, urls);
         }
+
         return urls;
     }
 
--- a/jdk/src/java.base/share/classes/jdk/internal/misc/JavaLangAccess.java	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/src/java.base/share/classes/jdk/internal/misc/JavaLangAccess.java	Thu May 04 07:26:55 2017 +0000
@@ -25,13 +25,11 @@
 
 package jdk.internal.misc;
 
-import java.io.IOException;
 import java.lang.annotation.Annotation;
 import java.lang.module.ModuleDescriptor;
 import java.lang.reflect.Executable;
 import java.lang.reflect.Method;
 import java.net.URI;
-import java.net.URL;
 import java.security.AccessControlContext;
 import java.security.ProtectionDomain;
 import java.util.Map;
@@ -157,12 +155,6 @@
     Class<?> findBootstrapClassOrNull(ClassLoader cl, String name);
 
     /**
-     * Returns a URL to a resource with the given name in a module that is
-     * defined to the given class loader.
-     */
-    URL findResource(ClassLoader cl, String moduleName, String name) throws IOException;
-
-    /**
      * Returns the Packages for the given class loader.
      */
     Stream<Package> packages(ClassLoader cl);
@@ -178,6 +170,11 @@
     String fastUUID(long lsb, long msb);
 
     /**
+     * Record the non-exported packages of the modules in the given layer
+     */
+    void addNonExportedPackages(ModuleLayer layer);
+
+    /**
      * Invalidate package access cache
      */
     void invalidatePackageAccessCache();
--- a/jdk/src/java.base/share/classes/jdk/internal/module/Checks.java	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/src/java.base/share/classes/jdk/internal/module/Checks.java	Thu May 04 07:26:55 2017 +0000
@@ -25,6 +25,8 @@
 
 package jdk.internal.module;
 
+import java.util.Set;
+
 /**
  * Utility class for checking module, package, and class names.
  */
@@ -45,18 +47,17 @@
         int next;
         int off = 0;
         while ((next = name.indexOf('.', off)) != -1) {
-            if (isJavaIdentifier(name, off, (next - off)) == -1) {
-                String id = name.substring(off, next);
+            String id = name.substring(off, next);
+            if (!isJavaIdentifier(id)) {
                 throw new IllegalArgumentException(name + ": Invalid module name"
                         + ": '" + id + "' is not a Java identifier");
             }
             off = next+1;
         }
-        int last = isJavaIdentifier(name, off, name.length() - off);
-        if (last == -1) {
-            String id = name.substring(off);
+        String last = name.substring(off);
+        if (!isJavaIdentifier(last)) {
             throw new IllegalArgumentException(name + ": Invalid module name"
-                    + ": '" + id + "' is not a Java identifier");
+                    + ": '" + last + "' is not a Java identifier");
         }
         return name;
     }
@@ -68,14 +69,13 @@
         int next;
         int off = 0;
         while ((next = name.indexOf('.', off)) != -1) {
-            if (isJavaIdentifier(name, off, (next - off)) == -1)
+            String id = name.substring(off, next);
+            if (!isJavaIdentifier(id))
                 return false;
             off = next+1;
         }
-        int last = isJavaIdentifier(name, off, name.length() - off);
-        if (last == -1)
-            return false;
-        return true;
+        String last = name.substring(off);
+        return isJavaIdentifier(last);
     }
 
     /**
@@ -144,12 +144,13 @@
         int next;
         int off = 0;
         while ((next = name.indexOf('.', off)) != -1) {
-            if (isJavaIdentifier(name, off, (next - off)) == -1)
+            String id = name.substring(off, next);
+            if (!isJavaIdentifier(id))
                 return false;
             off = next+1;
         }
-        int count = name.length() - off;
-        return (isJavaIdentifier(name, off, count) != -1);
+        String last = name.substring(off);
+        return isJavaIdentifier(last);
     }
 
     /**
@@ -164,76 +165,99 @@
         int next;
         int off = 0;
         while ((next = name.indexOf('.', off)) != -1) {
-            if (isJavaIdentifier(name, off, (next - off)) == -1) {
-                String id = name.substring(off, next);
+            String id = name.substring(off, next);
+            if (!isJavaIdentifier(id)) {
                 throw new IllegalArgumentException(name + ": Invalid " + what
                         + ": '" + id + "' is not a Java identifier");
             }
             off = next + 1;
         }
-        if (isJavaIdentifier(name, off, name.length() - off) == -1) {
-            String id = name.substring(off, name.length());
+        String last = name.substring(off);
+        if (!isJavaIdentifier(last)) {
             throw new IllegalArgumentException(name + ": Invalid " + what
-                    + ": '" + id + "' is not a Java identifier");
+                    + ": '" + last + "' is not a Java identifier");
         }
         return name;
     }
 
     /**
-     * Returns {@code true} if a given legal module name contains an identifier
-     * that doesn't end with a Java letter.
+     * Returns true if the given char sequence is a legal Java identifier,
+     * otherwise false.
      */
-    public static boolean hasJavaIdentifierWithTrailingDigit(String name) {
-        // quick scan to allow names that are just ASCII without digits
-        boolean needToParse = false;
-        int i = 0;
-        while (i < name.length()) {
-            int c = name.charAt(i);
-            if (c > 0x7F || (c >= '0' && c <= '9')) {
-                needToParse = true;
-                break;
-            }
-            i++;
-        }
-        if (!needToParse)
+    private static boolean isJavaIdentifier(CharSequence cs) {
+        if (cs.length() == 0 || RESERVED.contains(cs))
             return false;
 
-        // slow path
-        int next;
-        int off = 0;
-        while ((next = name.indexOf('.', off)) != -1) {
-            int last = isJavaIdentifier(name, off, (next - off));
-            if (!Character.isJavaIdentifierStart(last))
-                return true;
-            off = next+1;
-        }
-        int last = isJavaIdentifier(name, off, name.length() - off);
-        if (!Character.isJavaIdentifierStart(last))
-            return true;
-        return false;
-
-    }
+        int first = Character.codePointAt(cs, 0);
+        if (!Character.isJavaIdentifierStart(first))
+            return false;
 
-    /**
-     * Checks if a char sequence is a legal Java identifier, returning the code
-     * point of the last character if legal or {@code -1} if not legal.
-     */
-    private static int isJavaIdentifier(CharSequence cs, int offset, int count) {
-        if (count == 0)
-            return -1;
-        int first = Character.codePointAt(cs, offset);
-        if (!Character.isJavaIdentifierStart(first))
-            return -1;
-
-        int cp = first;
         int i = Character.charCount(first);
-        while (i < count) {
-            cp = Character.codePointAt(cs, offset+i);
+        while (i < cs.length()) {
+            int cp = Character.codePointAt(cs, i);
             if (!Character.isJavaIdentifierPart(cp))
-                return -1;
+                return false;
             i += Character.charCount(cp);
         }
 
-        return cp;
+        return true;
     }
+
+    // keywords, boolean and null literals, not allowed in identifiers
+    private static final Set<String> RESERVED = Set.of(
+            "abstract",
+            "assert",
+            "boolean",
+            "break",
+            "byte",
+            "case",
+            "catch",
+            "char",
+            "class",
+            "const",
+            "continue",
+            "default",
+            "do",
+            "double",
+            "else",
+            "enum",
+            "extends",
+            "final",
+            "finally",
+            "float",
+            "for",
+            "goto",
+            "if",
+            "implements",
+            "import",
+            "instanceof",
+            "int",
+            "interface",
+            "long",
+            "native",
+            "new",
+            "package",
+            "private",
+            "protected",
+            "public",
+            "return",
+            "short",
+            "static",
+            "strictfp",
+            "super",
+            "switch",
+            "synchronized",
+            "this",
+            "throw",
+            "throws",
+            "transient",
+            "try",
+            "void",
+            "volatile",
+            "while",
+            "true",
+            "false",
+            "null",
+            "_"
+    );
 }
--- a/jdk/src/java.base/share/classes/jdk/internal/module/ClassFileAttributes.java	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/src/java.base/share/classes/jdk/internal/module/ClassFileAttributes.java	Thu May 04 07:26:55 2017 +0000
@@ -549,34 +549,26 @@
      *   u2 attribute_name_index;
      *   u4 attribute_length;
      *
-     *   // index to CONSTANT_utf8_info structure with the OS name
-     *   u2 os_name_index;
-     *   // index to CONSTANT_utf8_info structure with the OS arch
-     *   u2 os_arch_index
+     *   // index to CONSTANT_utf8_info structure with the target platform
+     *   u2 target_platform_index;
      * }
      *
      * } </pre>
      */
     public static class ModuleTargetAttribute extends Attribute {
-        private final String osName;
-        private final String osArch;
+        private final String targetPlatform;
 
-        public ModuleTargetAttribute(String osName, String osArch) {
+        public ModuleTargetAttribute(String targetPlatform) {
             super(MODULE_TARGET);
-            this.osName = osName;
-            this.osArch = osArch;
+            this.targetPlatform = targetPlatform;
         }
 
         public ModuleTargetAttribute() {
-            this(null, null);
+            this(null);
         }
 
-        public String osName() {
-            return osName;
-        }
-
-        public String osArch() {
-            return osArch;
+        public String targetPlatform() {
+            return targetPlatform;
         }
 
         @Override
@@ -588,20 +580,14 @@
                                  Label[] labels)
         {
 
-            String osName = null;
-            String osArch = null;
+            String targetPlatform = null;
 
-            int name_index = cr.readUnsignedShort(off);
-            if (name_index != 0)
-                osName = cr.readUTF8(off, buf);
+            int target_platform_index = cr.readUnsignedShort(off);
+            if (target_platform_index != 0)
+                targetPlatform = cr.readUTF8(off, buf);
             off += 2;
 
-            int arch_index = cr.readUnsignedShort(off);
-            if (arch_index != 0)
-                osArch = cr.readUTF8(off, buf);
-            off += 2;
-
-            return new ModuleTargetAttribute(osName, osArch);
+            return new ModuleTargetAttribute(targetPlatform);
         }
 
         @Override
@@ -613,15 +599,10 @@
         {
             ByteVector attr = new ByteVector();
 
-            int name_index = 0;
-            if (osName != null && osName.length() > 0)
-                name_index = cw.newUTF8(osName);
-            attr.putShort(name_index);
-
-            int arch_index = 0;
-            if (osArch != null && osArch.length() > 0)
-                arch_index = cw.newUTF8(osArch);
-            attr.putShort(arch_index);
+            int target_platform_index = 0;
+            if (targetPlatform != null && targetPlatform.length() > 0)
+                target_platform_index = cw.newUTF8(targetPlatform);
+            attr.putShort(target_platform_index);
 
             return attr;
         }
--- a/jdk/src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java	Thu May 04 07:26:55 2017 +0000
@@ -84,8 +84,9 @@
     // The ModulePatcher for the initial configuration
     private static final ModulePatcher patcher = initModulePatcher();
 
-    // ModuleFinder for the initial configuration
-    private static ModuleFinder initialFinder;
+    // ModuleFinders for the initial configuration
+    private static ModuleFinder unlimitedFinder;
+    private static ModuleFinder limitedFinder;
 
     /**
      * Returns the ModulePatcher for the initial configuration.
@@ -95,11 +96,20 @@
     }
 
     /**
-     * Returns the ModuleFinder for the initial configuration
+     * Returns the ModuleFinder for the initial configuration before observability
+     * is limited by the --limit-modules command line option.
      */
-    public static ModuleFinder finder() {
-        assert initialFinder != null;
-        return initialFinder;
+    public static ModuleFinder unlimitedFinder() {
+        assert unlimitedFinder != null;
+        return unlimitedFinder;
+    }
+
+    /**
+     * Returns the ModuleFinder for the initial configuration.
+     */
+    public static ModuleFinder limitedFinder() {
+        assert limitedFinder != null;
+        return limitedFinder;
     }
 
     /**
@@ -134,6 +144,11 @@
 
         PerfCounters.defineBaseTime.addElapsedTimeFrom(t1);
 
+        // special mode to boot with only java.base, ignores other options
+        String propValue = getAndRemoveProperty("jdk.module.minimumBoot");
+        if (propValue != null) {
+            return createMinimalBootLayer();
+        }
 
         long t2 = System.nanoTime();
 
@@ -180,7 +195,8 @@
         }
 
         // --limit-modules
-        String propValue = getAndRemoveProperty("jdk.module.limitmods");
+        unlimitedFinder = finder;
+        propValue = getAndRemoveProperty("jdk.module.limitmods");
         if (propValue != null) {
             Set<String> mods = new HashSet<>();
             for (String mod: propValue.split(",")) {
@@ -188,6 +204,7 @@
             }
             finder = limitFinder(finder, mods, roots);
         }
+        limitedFinder = finder;
 
         // If there is no initial module specified then assume that the initial
         // module is the unnamed module of the application class loader. This
@@ -267,7 +284,8 @@
         }
 
         PrintStream traceOutput = null;
-        if (Boolean.getBoolean("jdk.launcher.traceResolver"))
+        propValue = getAndRemoveProperty("jdk.module.showModuleResolution");
+        if (propValue != null && Boolean.parseBoolean(propValue))
             traceOutput = System.out;
 
         // run the resolver to create the configuration
@@ -362,10 +380,21 @@
         // total time to initialize
         PerfCounters.bootstrapTime.addElapsedTimeFrom(t0);
 
-        // remember the ModuleFinder
-        initialFinder = finder;
+        return bootLayer;
+    }
 
-        return bootLayer;
+    /**
+     * Create a "minimal" boot module layer that only contains java.base.
+     */
+    private static ModuleLayer createMinimalBootLayer() {
+        Configuration cf = SharedSecrets.getJavaLangModuleAccess()
+            .resolveAndBind(ModuleFinder.ofSystem(),
+                            Set.of(JAVA_BASE),
+                            false,
+                            null);
+
+        Function<String, ClassLoader> clf = ModuleLoaderMap.mappingFunction(cf);
+        return ModuleLayer.empty().defineModules(cf, clf);
     }
 
     /**
--- a/jdk/src/java.base/share/classes/jdk/internal/module/ModuleHashesBuilder.java	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/src/java.base/share/classes/jdk/internal/module/ModuleHashesBuilder.java	Thu May 04 07:26:55 2017 +0000
@@ -138,7 +138,7 @@
     }
 
     /*
-     * Utilty class
+     * Utility class
      */
     static class Graph<T> {
         private final Set<T> nodes;
--- a/jdk/src/java.base/share/classes/jdk/internal/module/ModuleInfo.java	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/src/java.base/share/classes/jdk/internal/module/ModuleInfo.java	Thu May 04 07:26:55 2017 +0000
@@ -546,21 +546,15 @@
     private ModuleTarget readModuleTargetAttribute(DataInput in, ConstantPool cpool)
         throws IOException
     {
-        String osName = null;
-        String osArch = null;
-
-        int name_index = in.readUnsignedShort();
-        if (name_index != 0)
-            osName = cpool.getUtf8(name_index);
+        String targetPlatform = null;
 
-        int arch_index = in.readUnsignedShort();
-        if (arch_index != 0)
-            osArch = cpool.getUtf8(arch_index);
+        int index = in.readUnsignedShort();
+        if (index != 0)
+            targetPlatform = cpool.getUtf8(index);
 
-        return new ModuleTarget(osName, osArch);
+        return new ModuleTarget(targetPlatform);
     }
 
-
     /**
      * Reads the ModuleHashes attribute
      */
@@ -612,7 +606,6 @@
         return new ModuleResolution(flags);
     }
 
-
     /**
      * Returns true if the given attribute can be present at most once
      * in the class file. Returns false otherwise.
--- a/jdk/src/java.base/share/classes/jdk/internal/module/ModuleInfoExtender.java	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/src/java.base/share/classes/jdk/internal/module/ModuleInfoExtender.java	Thu May 04 07:26:55 2017 +0000
@@ -62,9 +62,8 @@
     // the value of the ModuleMainClass attribute
     private String mainClass;
 
-    // the values for the ModuleTarget attribute
-    private String osName;
-    private String osArch;
+    // the value for the ModuleTarget attribute
+    private String targetPlatform;
 
     // the hashes for the ModuleHashes attribute
     private ModuleHashes hashes;
@@ -108,11 +107,10 @@
     }
 
     /**
-     * Sets the values for the ModuleTarget attribute.
+     * Sets the value for the ModuleTarget attribute.
      */
-    public ModuleInfoExtender targetPlatform(String osName, String osArch) {
-        this.osName = osName;
-        this.osArch = osArch;
+    public ModuleInfoExtender targetPlatform(String targetPlatform) {
+        this.targetPlatform = targetPlatform;
         return this;
     }
 
@@ -199,8 +197,8 @@
             cv.addAttribute(new ModulePackagesAttribute(packages));
         if (mainClass != null)
             cv.addAttribute(new ModuleMainClassAttribute(mainClass));
-        if (osName != null || osArch != null)
-            cv.addAttribute(new ModuleTargetAttribute(osName, osArch));
+        if (targetPlatform != null)
+            cv.addAttribute(new ModuleTargetAttribute(targetPlatform));
         if (hashes != null)
             cv.addAttribute(new ModuleHashesAttribute(hashes));
         if (moduleResolution != null)
--- a/jdk/src/java.base/share/classes/jdk/internal/module/ModuleInfoWriter.java	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/src/java.base/share/classes/jdk/internal/module/ModuleInfoWriter.java	Thu May 04 07:26:55 2017 +0000
@@ -66,10 +66,9 @@
         // write ModuleMainClass if the module has a main class
         md.mainClass().ifPresent(mc -> cw.visitAttribute(new ModuleMainClassAttribute(mc)));
 
-        // write ModuleTarget if there is a platform OS/arch
+        // write ModuleTarget if there is a target platform
         if (target != null) {
-            cw.visitAttribute(new ModuleTargetAttribute(target.osName(),
-                                                        target.osArch()));
+            cw.visitAttribute(new ModuleTargetAttribute(target.targetPlatform()));
         }
 
         cw.visitEnd();
--- a/jdk/src/java.base/share/classes/jdk/internal/module/ModuleLoaderMap.java	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/src/java.base/share/classes/jdk/internal/module/ModuleLoaderMap.java	Thu May 04 07:26:55 2017 +0000
@@ -37,36 +37,66 @@
 
 
 /**
- * The module to class loader map.  The list of boot modules and platform modules
- * are generated at build time.
+ * Supports the mapping of modules to class loaders. The set of modules mapped
+ * to the boot and platform class loaders is generated at build time from
+ * this source file.
  */
-final class ModuleLoaderMap {
+public final class ModuleLoaderMap {
+
+    /**
+     * Maps the system modules to the built-in class loaders.
+     */
+    public static final class Mapper implements Function<String, ClassLoader> {
+        private final Map<String, ClassLoader> map;
+
+        Mapper(Map<String, ClassLoader> map) {
+            this.map = map; // defensive copy not needed
+        }
+
+        @Override
+        public ClassLoader apply(String name) {
+            return map.get(name);
+        }
+    }
+
+    /**
+     * Returns the names of the modules defined to the boot loader.
+     */
+    public static Set<String> bootModules() {
+        // The list of boot modules generated at build time.
+        String[] BOOT_MODULES = new String[] { "@@BOOT_MODULE_NAMES@@" };
+        Set<String> bootModules = new HashSet<>(BOOT_MODULES.length);
+        for (String mn : BOOT_MODULES) {
+            bootModules.add(mn);
+        }
+        return bootModules;
+    }
+
+    /**
+     * Returns the names of the modules defined to the platform loader.
+     */
+    public static Set<String> platformModules() {
+        // The list of platform modules generated at build time.
+        String[] PLATFORM_MODULES = new String[] { "@@PLATFORM_MODULE_NAMES@@" };
+        Set<String> platformModules = new HashSet<>(PLATFORM_MODULES.length);
+        for (String mn : PLATFORM_MODULES) {
+            platformModules.add(mn);
+        }
+        return platformModules;
+    }
 
     /**
      * Returns the function to map modules in the given configuration to the
      * built-in class loaders.
      */
     static Function<String, ClassLoader> mappingFunction(Configuration cf) {
-
-        // The list of boot modules and platform modules are generated at build time.
-        final String[] BOOT_MODULES = new String[] { "@@BOOT_MODULE_NAMES@@" };
-        final String[] PLATFORM_MODULES = new String[] { "@@PLATFORM_MODULE_NAMES@@" };
-
-        Set<String> bootModules = new HashSet<>(BOOT_MODULES.length);
-        for (String mn : BOOT_MODULES) {
-            bootModules.add(mn);
-        }
-
-        Set<String> platformModules = new HashSet<>(PLATFORM_MODULES.length);
-        for (String mn : PLATFORM_MODULES) {
-            platformModules.add(mn);
-        }
+        Set<String> bootModules = bootModules();
+        Set<String> platformModules = platformModules();
 
         ClassLoader platformClassLoader = ClassLoaders.platformClassLoader();
         ClassLoader appClassLoader = ClassLoaders.appClassLoader();
 
         Map<String, ClassLoader> map = new HashMap<>();
-
         for (ResolvedModule resolvedModule : cf.modules()) {
             String mn = resolvedModule.name();
             if (!bootModules.contains(mn)) {
@@ -77,12 +107,6 @@
                 }
             }
         }
-
-        return new Function<String, ClassLoader> () {
-            @Override
-            public ClassLoader apply(String mn) {
-                return map.get(mn);
-            }
-        };
+        return new Mapper(map);
     }
 }
--- a/jdk/src/java.base/share/classes/jdk/internal/module/ModulePatcher.java	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/src/java.base/share/classes/jdk/internal/module/ModulePatcher.java	Thu May 04 07:26:55 2017 +0000
@@ -120,7 +120,7 @@
 
                     // 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())) {
+                    try (JarFile jf = new JarFile(file.toString())) {
                         jf.stream()
                           .filter(e -> !e.isDirectory()
                                   && (!isAutomatic || e.getName().endsWith(".class")))
@@ -431,7 +431,7 @@
         private final URL csURL;
 
         JarResourceFinder(Path path) throws IOException {
-            this.jf = new JarFile(path.toFile());
+            this.jf = new JarFile(path.toString());
             this.csURL = path.toUri().toURL();
         }
 
@@ -505,7 +505,7 @@
         public Resource find(String name) throws IOException {
             Path file = Resources.toFilePath(dir, name);
             if (file != null) {
-                return  newResource(name, dir, file);
+                return newResource(name, dir, file);
             } else {
                 return null;
             }
--- a/jdk/src/java.base/share/classes/jdk/internal/module/ModulePath.java	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/src/java.base/share/classes/jdk/internal/module/ModulePath.java	Thu May 04 07:26:55 2017 +0000
@@ -59,6 +59,7 @@
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import java.util.stream.Collectors;
+import java.util.zip.ZipException;
 import java.util.zip.ZipFile;
 
 import jdk.internal.jmod.JmodFile;
@@ -315,26 +316,42 @@
     {
         try {
 
+            // exploded module
             if (attrs.isDirectory()) {
                 return readExplodedModule(entry); // may return null
-            } else {
+            }
+
+            // JAR or JMOD file
+            if (attrs.isRegularFile()) {
                 String fn = entry.getFileName().toString();
-                if (attrs.isRegularFile()) {
-                    if (fn.endsWith(".jar")) {
+                boolean isDefaultFileSystem = isDefaultFileSystem(entry);
+
+                // JAR file
+                if (fn.endsWith(".jar")) {
+                    if (isDefaultFileSystem) {
                         return readJar(entry);
-                    } else if (isLinkPhase && fn.endsWith(".jmod")) {
-                        return readJMod(entry);
+                    } else {
+                        // the JAR file is in a custom file system so
+                        // need to copy it to the local file system
+                        Path tmpdir = Files.createTempDirectory("mlib");
+                        Path target = Files.copy(entry, tmpdir.resolve(fn));
+                        return readJar(target);
                     }
                 }
-                return null;
+
+                // JMOD file
+                if (isDefaultFileSystem && isLinkPhase && fn.endsWith(".jmod")) {
+                    return readJMod(entry);
+                }
             }
 
+            return null;
+
         } catch (InvalidModuleDescriptorException e) {
             throw new FindException("Error reading module: " + entry, e);
         }
     }
 
-
     /**
      * Returns a string with the file name of the module if possible.
      * If the module location is not a file URI then return the URI
@@ -434,7 +451,7 @@
      * 3. The contents of any META-INF/services configuration files are mapped
      *    to "provides" declarations
      * 4. The Main-Class attribute in the main attributes of the JAR manifest
-     *    is mapped to the module descriptor mainClass
+     *    is mapped to the module descriptor mainClass if possible
      */
     private ModuleDescriptor deriveModuleDescriptor(JarFile jf)
         throws IOException
@@ -530,12 +547,12 @@
             String mainClass = attrs.getValue(Attributes.Name.MAIN_CLASS);
             if (mainClass != null) {
                 mainClass = mainClass.replace("/", ".");
-                String pn = packageName(mainClass);
-                if (!packages.contains(pn)) {
-                    String msg = "Main-Class " + mainClass + " not in module";
-                    throw new InvalidModuleDescriptorException(msg);
+                if (Checks.isClassName(mainClass)) {
+                    String pn = packageName(mainClass);
+                    if (packages.contains(pn)) {
+                        builder.mainClass(mainClass);
+                    }
                 }
-                builder.mainClass(mainClass);
             }
         }
 
@@ -617,6 +634,8 @@
             }
 
             return ModuleReferences.newJarModule(attrs, patcher, file);
+        } catch (ZipException e) {
+            throw new FindException("Error reading " + file, e);
         }
     }
 
@@ -733,6 +752,16 @@
         }
     }
 
+
+    /**
+     * Return true if a path locates a path in the default file system
+     */
+    private boolean isDefaultFileSystem(Path path) {
+        return path.getFileSystem().provider()
+                .getScheme().equalsIgnoreCase("file");
+    }
+
+
     private static final PerfCounter scanTime
         = PerfCounter.newPerfCounter("jdk.module.finder.modulepath.scanTime");
     private static final PerfCounter moduleCount
--- a/jdk/src/java.base/share/classes/jdk/internal/module/ModuleReferences.java	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/src/java.base/share/classes/jdk/internal/module/ModuleReferences.java	Thu May 04 07:26:55 2017 +0000
@@ -25,6 +25,7 @@
 
 package jdk.internal.module;
 
+import java.io.File;
 import java.io.IOError;
 import java.io.IOException;
 import java.io.InputStream;
@@ -226,8 +227,8 @@
 
         static JarFile newJarFile(Path path) {
             try {
-                return new JarFile(path.toFile(),
-                                   true,               // verify
+                return new JarFile(new File(path.toString()),
+                                   true,                       // verify
                                    ZipFile.OPEN_READ,
                                    JarFile.runtimeVersion());
             } catch (IOException ioe) {
--- a/jdk/src/java.base/share/classes/jdk/internal/module/ModuleResolution.java	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/src/java.base/share/classes/jdk/internal/module/ModuleResolution.java	Thu May 04 07:26:55 2017 +0000
@@ -39,6 +39,10 @@
         this.value = value;
     }
 
+    public int value() {
+        return value;
+    }
+
     public static ModuleResolution empty() {
         return new ModuleResolution(0);
     }
@@ -74,35 +78,30 @@
             throw new InternalError("cannot add deprecated for removal to " + value);
         return new ModuleResolution(value | WARN_DEPRECATED_FOR_REMOVAL);
     }
+
     public ModuleResolution withIncubating() {
         if ((value & (WARN_DEPRECATED | WARN_DEPRECATED_FOR_REMOVAL)) != 0)
             throw new InternalError("cannot add incubating to " + value);
         return new ModuleResolution(value | WARN_INCUBATING);
     }
 
-    public int value() {
-        return value;
-    }
-
     public static boolean doNotResolveByDefault(ModuleReference mref) {
         // get the DO_NOT_RESOLVE_BY_DEFAULT flag, if any
-        if (!(mref instanceof ModuleReferenceImpl))
-            return false;
-
-        ModuleResolution mres = ((ModuleReferenceImpl)mref).moduleResolution();
-        if (mres != null)
-            return mres.doNotResolveByDefault();
+        if (mref instanceof ModuleReferenceImpl) {
+            ModuleResolution mres = ((ModuleReferenceImpl) mref).moduleResolution();
+            if (mres != null)
+                return mres.doNotResolveByDefault();
+        }
 
         return false;
     }
 
     public static boolean hasIncubatingWarning(ModuleReference mref) {
-        if (!(mref instanceof ModuleReferenceImpl))
-            return false;
-
-        ModuleResolution mres = ((ModuleReferenceImpl)mref).moduleResolution();
-        if (mres != null)
-            return mres.hasIncubatingWarning();
+        if (mref instanceof ModuleReferenceImpl) {
+            ModuleResolution mres = ((ModuleReferenceImpl) mref).moduleResolution();
+            if (mres != null)
+                return mres.hasIncubatingWarning();
+        }
 
         return false;
     }
--- a/jdk/src/java.base/share/classes/jdk/internal/module/ModuleTarget.java	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/src/java.base/share/classes/jdk/internal/module/ModuleTarget.java	Thu May 04 07:26:55 2017 +0000
@@ -25,22 +25,21 @@
 
 package jdk.internal.module;
 
+/**
+ * Represents the module target.
+ *
+ * For now, this is a single value for the target platform, e.g. "linux-x64".
+ */
 public final class ModuleTarget {
 
-    private final String osName;
-    private final String osArch;
+    private final String targetPlatform;
 
-    public ModuleTarget(String osName, String osArch) {
-        this.osName = osName;
-        this.osArch = osArch;
+    public ModuleTarget(String targetPlatform) {
+        this.targetPlatform = targetPlatform;
     }
 
-    public String osName() {
-        return osName;
-    }
-
-    public String osArch() {
-        return osArch;
+    public String targetPlatform() {
+        return targetPlatform;
     }
 
 }
--- a/jdk/src/java.base/share/classes/jdk/internal/module/Modules.java	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/src/java.base/share/classes/jdk/internal/module/Modules.java	Thu May 04 07:26:55 2017 +0000
@@ -25,12 +25,22 @@
 
 package jdk.internal.module;
 
+import java.lang.module.Configuration;
 import java.lang.module.ModuleDescriptor;
+import java.lang.module.ModuleFinder;
+import java.lang.module.ModuleReference;
+import java.lang.module.ResolvedModule;
 import java.net.URI;
 import java.security.AccessController;
 import java.security.PrivilegedAction;
+import java.util.List;
+import java.util.Map;
+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.loader.ClassLoaders;
 import jdk.internal.misc.JavaLangAccess;
 import jdk.internal.misc.SharedSecrets;
@@ -38,8 +48,8 @@
 /**
  * A helper class for creating and updating modules. This class is intended to
  * support command-line options, tests, and the instrumentation API. It is also
- * used by the VM to add read edges when agents are instrumenting code that
- * need to link to supporting classes.
+ * used by the VM to load modules or add read edges when agents are instrumenting
+ * code that need to link to supporting classes.
  *
  * The parameters that are package names in this API are the fully-qualified
  * names of the packages as defined in section 6.5.3 of <cite>The Java&trade;
@@ -154,4 +164,90 @@
         addReads(m, BootLoader.getUnnamedModule());
         addReads(m, ClassLoaders.appClassLoader().getUnnamedModule());
     }
+
+    /**
+     * Called by the VM to load a system module, typically "java.instrument" or
+     * "jdk.management.agent". If the module is not loaded then it is resolved
+     * and loaded (along with any dependences that weren't previously loaded)
+     * into a child layer.
+     */
+    public static synchronized Module loadModule(String name) {
+        ModuleLayer top = topLayer;
+        if (top == null)
+            top = ModuleLayer.boot();
+
+        Module module = top.findModule(name).orElse(null);
+        if (module != null) {
+            // module already loaded
+            return module;
+        }
+
+        // resolve the module with the top-most layer as the parent
+        ModuleFinder empty = ModuleFinder.of();
+        ModuleFinder finder = ModuleBootstrap.unlimitedFinder();
+        Set<String> roots = Set.of(name);
+        Configuration cf = top.configuration().resolveAndBind(empty, finder, roots);
+
+        // create the child layer
+        Function<String, ClassLoader> clf = ModuleLoaderMap.mappingFunction(cf);
+        ModuleLayer newLayer = top.defineModules(cf, clf);
+
+        // add qualified exports/opens to give access to modules in child layer
+        Map<String, Module> map = newLayer.modules().stream()
+                                          .collect(Collectors.toMap(Module::getName,
+                                                  Function.identity()));
+        ModuleLayer layer = top;
+        while (layer != null) {
+            for (Module m : layer.modules()) {
+                // qualified exports
+                m.getDescriptor().exports().stream()
+                    .filter(ModuleDescriptor.Exports::isQualified)
+                    .forEach(e -> e.targets().forEach(target -> {
+                        Module other = map.get(target);
+                        if (other != null) {
+                            addExports(m, e.source(), other);
+                        }}));
+
+                // qualified opens
+                m.getDescriptor().opens().stream()
+                    .filter(ModuleDescriptor.Opens::isQualified)
+                    .forEach(o -> o.targets().forEach(target -> {
+                        Module other = map.get(target);
+                        if (other != null) {
+                            addOpens(m, o.source(), other);
+                        }}));
+            }
+
+            List<ModuleLayer> parents = layer.parents();
+            assert parents.size() <= 1;
+            layer = parents.isEmpty() ? null : parents.get(0);
+        }
+
+        // update security manager before making types visible
+        JLA.addNonExportedPackages(newLayer);
+
+        // update the built-in class loaders to make the types visible
+        for (ResolvedModule resolvedModule : cf.modules()) {
+            ModuleReference mref = resolvedModule.reference();
+            String mn = mref.descriptor().name();
+            ClassLoader cl = clf.apply(mn);
+            if (cl == null) {
+                BootLoader.loadModule(mref);
+            } else {
+                ((BuiltinClassLoader) cl).loadModule(mref);
+            }
+        }
+
+        // new top layer
+        topLayer = newLayer;
+
+        // return module
+        return newLayer.findModule(name)
+                       .orElseThrow(() -> new InternalError("module not loaded"));
+
+    }
+
+    // the top-most system layer
+    private static ModuleLayer topLayer;
+
 }
--- a/jdk/src/java.base/share/classes/jdk/internal/module/Resources.java	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/src/java.base/share/classes/jdk/internal/module/Resources.java	Thu May 04 07:26:55 2017 +0000
@@ -26,10 +26,10 @@
 
 import java.io.File;
 import java.io.IOException;
+import java.nio.file.FileSystem;
 import java.nio.file.Files;
 import java.nio.file.NoSuchFileException;
 import java.nio.file.Path;
-import java.nio.file.Paths;
 import java.nio.file.attribute.BasicFileAttributes;
 
 /**
@@ -94,7 +94,7 @@
         if (expectDirectory) {
             name = name.substring(0, name.length() - 1);  // drop trailing "/"
         }
-        Path path = toSafeFilePath(name);
+        Path path = toSafeFilePath(dir.getFileSystem(), name);
         if (path != null) {
             Path file = dir.resolve(path);
             try {
@@ -116,7 +116,7 @@
      * are rejected, as are resource names that translates to a file path
      * with a root component.
      */
-    private static Path toSafeFilePath(String name) {
+    private static Path toSafeFilePath(FileSystem fs, String name) {
         // scan elements of resource name
         int next;
         int off = 0;
@@ -135,12 +135,12 @@
         // convert to file path
         Path path;
         if (File.separatorChar == '/') {
-            path = Paths.get(name);
+            path = fs.getPath(name);
         } else {
             // not allowed to embed file separators
             if (name.contains(File.separator))
                 return null;
-            path = Paths.get(name.replace('/', File.separatorChar));
+            path = fs.getPath(name.replace('/', File.separatorChar));
         }
 
         // file path not allowed to have root component
--- a/jdk/src/java.base/share/classes/module-info.java	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/src/java.base/share/classes/module-info.java	Thu May 04 07:26:55 2017 +0000
@@ -161,6 +161,7 @@
         java.security.jgss,
         java.sql,
         java.xml,
+        jdk.attach,
         jdk.charsets,
         jdk.compiler,   // reflective dependency
         jdk.incubator.httpclient,
--- a/jdk/src/java.base/share/classes/sun/launcher/LauncherHelper.java	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/src/java.base/share/classes/sun/launcher/LauncherHelper.java	Thu May 04 07:26:55 2017 +0000
@@ -43,13 +43,17 @@
 import java.io.IOException;
 import java.io.PrintStream;
 import java.io.UnsupportedEncodingException;
-import java.lang.module.ModuleFinder;
-import java.lang.module.ModuleReference;
+import java.lang.module.Configuration;
+import java.lang.module.FindException;
 import java.lang.module.ModuleDescriptor;
 import java.lang.module.ModuleDescriptor.Requires;
 import java.lang.module.ModuleDescriptor.Exports;
 import java.lang.module.ModuleDescriptor.Opens;
 import java.lang.module.ModuleDescriptor.Provides;
+import java.lang.module.ModuleFinder;
+import java.lang.module.ModuleReference;
+import java.lang.module.ResolvedModule;
+import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
 import java.math.BigDecimal;
@@ -58,14 +62,16 @@
 import java.nio.charset.Charset;
 import java.nio.file.DirectoryStream;
 import java.nio.file.Files;
+import java.nio.file.NoSuchFileException;
 import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.attribute.BasicFileAttributes;
 import java.text.Normalizer;
 import java.text.MessageFormat;
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
-import java.util.HashSet;
+import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Locale;
@@ -83,6 +89,7 @@
 import java.util.stream.Stream;
 
 import jdk.internal.misc.VM;
+import jdk.internal.module.ModuleBootstrap;
 import jdk.internal.module.Modules;
 
 
@@ -98,6 +105,7 @@
             "javafx.application.Application";
     private static final String JAVAFX_FXHELPER_CLASS_NAME_SUFFIX =
             "sun.launcher.LauncherHelper$FXHelper";
+    private static final String LAUNCHER_AGENT_CLASS = "Launcher-Agent-Class";
     private static final String MAIN_CLASS = "Main-Class";
     private static final String ADD_EXPORTS = "Add-Exports";
     private static final String ADD_OPENS = "Add-Opens";
@@ -408,8 +416,12 @@
         ostream =  (printToStderr) ? System.err : System.out;
     }
 
+    static void initOutput(PrintStream ps) {
+        ostream = ps;
+    }
+
     static String getMainClassFromJar(String jarname) {
-        String mainValue = null;
+        String mainValue;
         try (JarFile jarFile = new JarFile(jarname)) {
             Manifest manifest = jarFile.getManifest();
             if (manifest == null) {
@@ -426,6 +438,22 @@
                 abort(null, "java.launcher.jar.error3", jarname);
             }
 
+            // Launcher-Agent-Class (only check for this when Main-Class present)
+            String agentClass = mainAttrs.getValue(LAUNCHER_AGENT_CLASS);
+            if (agentClass != null) {
+                ModuleLayer.boot().findModule("java.instrument").ifPresent(m -> {
+                    try {
+                        String cn = "sun.instrument.InstrumentationImpl";
+                        Class<?> clazz = Class.forName(cn, false, null);
+                        Method loadAgent = clazz.getMethod("loadAgent", String.class);
+                        loadAgent.invoke(null, jarname);
+                    } catch (Throwable e) {
+                        if (e instanceof InvocationTargetException) e = e.getCause();
+                        abort(e, "java.launcher.jar.error4", jarname);
+                    }
+                });
+            }
+
             // Add-Exports and Add-Opens
             String exports = mainAttrs.getValue(ADD_EXPORTS);
             if (exports != null) {
@@ -913,141 +941,350 @@
         }
     }
 
-    private static void formatCommaList(PrintStream out,
-                                        String prefix,
-                                        Collection<?> list)
-    {
-        if (list.isEmpty())
-            return;
-        out.format("%s", prefix);
-        boolean first = true;
-        for (Object ob : list) {
-            if (first) {
-                out.format(" %s", ob);
-                first = false;
-            } else {
-                out.format(", %s", ob);
-            }
-        }
-        out.format("%n");
+    /**
+     * Called by the launcher to list the observable modules.
+     */
+    static void listModules() {
+        initOutput(System.out);
+
+        ModuleBootstrap.limitedFinder().findAll().stream()
+            .sorted(new JrtFirstComparator())
+            .forEach(LauncherHelper::showModule);
+    }
+
+    /**
+     * Called by the launcher to show the resolved modules
+     */
+    static void showResolvedModules() {
+        initOutput(System.out);
+
+        ModuleLayer bootLayer = ModuleLayer.boot();
+        Configuration cf = bootLayer.configuration();
+
+        cf.modules().stream()
+            .map(ResolvedModule::reference)
+            .sorted(new JrtFirstComparator())
+            .forEach(LauncherHelper::showModule);
     }
 
     /**
-     * Called by the launcher to list the observable modules.
-     * If called without any sub-options then the output is a simple list of
-     * the modules. If called with sub-options then the sub-options are the
-     * names of the modules to list (e.g. --list-modules java.base,java.desktop)
+     * Called by the launcher to describe a module
      */
-    static void listModules(boolean printToStderr, String optionFlag)
-        throws IOException, ClassNotFoundException
-    {
-        initOutput(printToStderr);
+    static void describeModule(String moduleName) {
+        initOutput(System.out);
 
-        ModuleFinder finder = jdk.internal.module.ModuleBootstrap.finder();
-        int colon = optionFlag.indexOf('=');
-        if (colon == -1) {
-            finder.findAll().stream()
-                  .sorted(Comparator.comparing(ModuleReference::descriptor))
-                  .forEach(mref -> describeModule(finder, mref, false));
-        } else {
-            String[] names = optionFlag.substring(colon+1).split(",");
-            for (String name: names) {
-                ModuleReference mref = finder.find(name).orElse(null);
-                if (mref == null) {
-                    System.err.format("%s not found%n", name);
-                    continue;
-                }
-                describeModule(finder, mref, true);
-            }
+        ModuleFinder finder = ModuleBootstrap.limitedFinder();
+        ModuleReference mref = finder.find(moduleName).orElse(null);
+        if (mref == null) {
+            abort(null, "java.launcher.module.error4", moduleName);
         }
-    }
+        ModuleDescriptor md = mref.descriptor();
 
-    /**
-     * Describes the given module.
-     */
-    static void describeModule(ModuleFinder finder,
-                               ModuleReference mref,
-                               boolean verbose)
-    {
-        ModuleDescriptor md = mref.descriptor();
-        ostream.print("module " + midAndLocation(md, mref.location()));
-        if (md.isAutomatic())
-            ostream.print(" automatic");
-        ostream.println();
-
-        if (!verbose)
-            return;
+        // one-line summary
+        showModule(mref);
 
         // unqualified exports (sorted by package)
-        Set<Exports> exports = new TreeSet<>(Comparator.comparing(Exports::source));
-        md.exports().stream().filter(e -> !e.isQualified()).forEach(exports::add);
-        for (Exports e : exports) {
-            String modsAndSource = Stream.concat(toStringStream(e.modifiers()),
-                    Stream.of(e.source()))
+        md.exports().stream()
+            .filter(e -> !e.isQualified())
+            .sorted(Comparator.comparing(Exports::source))
+            .map(e -> Stream.concat(Stream.of(e.source()),
+                                    toStringStream(e.modifiers()))
+                    .collect(Collectors.joining(" ")))
+            .forEach(sourceAndMods -> ostream.format("exports %s%n", sourceAndMods));
+
+        // dependences
+        for (Requires r : md.requires()) {
+            String nameAndMods = Stream.concat(Stream.of(r.name()),
+                                               toStringStream(r.modifiers()))
                     .collect(Collectors.joining(" "));
-            ostream.format("  exports %s%n", modsAndSource);
+            ostream.format("requires %s", nameAndMods);
+            finder.find(r.name())
+                .map(ModuleReference::descriptor)
+                .filter(ModuleDescriptor::isAutomatic)
+                .ifPresent(any -> ostream.print(" automatic"));
+            ostream.println();
         }
 
-        for (Requires d : md.requires()) {
-            ostream.format("  requires %s", d);
-            String suffix = finder.find(d.name())
-                    .map(ModuleReference::descriptor)
-                    .map(any -> any.isAutomatic() ? " automatic" : "")
-                    .orElse(" not found");
-            ostream.println(suffix);
+        // service use and provides
+        for (String s : md.uses()) {
+            ostream.format("uses %s%n", s);
         }
-        for (String s : md.uses()) {
-            ostream.format("  uses %s%n", s);
-        }
+        for (Provides ps : md.provides()) {
+            String names = ps.providers().stream().collect(Collectors.joining(" "));
+            ostream.format("provides %s with %s%n", ps.service(), names);
 
-        for (Provides ps : md.provides()) {
-            ostream.format("  provides %s with %s%n", ps.service(),
-                    ps.providers().stream().collect(Collectors.joining(", ")));
         }
 
         // qualified exports
         for (Exports e : md.exports()) {
             if (e.isQualified()) {
-                String modsAndSource = Stream.concat(toStringStream(e.modifiers()),
-                        Stream.of(e.source()))
-                        .collect(Collectors.joining(" "));
-                ostream.format("  exports %s", modsAndSource);
-                formatCommaList(ostream, " to", e.targets());
+                String who = e.targets().stream().collect(Collectors.joining(" "));
+                ostream.format("qualified exports %s to %s%n", e.source(), who);
             }
         }
 
         // open packages
-        for (Opens obj: md.opens()) {
-            String modsAndSource = Stream.concat(toStringStream(obj.modifiers()),
-                    Stream.of(obj.source()))
+        for (Opens opens: md.opens()) {
+            if (opens.isQualified())
+                ostream.print("qualified ");
+            String sourceAndMods = Stream.concat(Stream.of(opens.source()),
+                                                 toStringStream(opens.modifiers()))
                     .collect(Collectors.joining(" "));
-            ostream.format("  opens %s", modsAndSource);
-            if (obj.isQualified())
-                formatCommaList(ostream, " to", obj.targets());
-            else
-                ostream.println();
+            ostream.format("opens %s", sourceAndMods);
+            if (opens.isQualified()) {
+                String who = opens.targets().stream().collect(Collectors.joining(" "));
+                ostream.format(" to %s", who);
+            }
+            ostream.println();
         }
 
         // non-exported/non-open packages
         Set<String> concealed = new TreeSet<>(md.packages());
         md.exports().stream().map(Exports::source).forEach(concealed::remove);
         md.opens().stream().map(Opens::source).forEach(concealed::remove);
-        concealed.forEach(p -> ostream.format("  contains %s%n", p));
+        concealed.forEach(p -> ostream.format("contains %s%n", p));
+    }
+
+    /**
+     * Prints a single line with the module name, version and modifiers
+     */
+    private static void showModule(ModuleReference mref) {
+        ModuleDescriptor md = mref.descriptor();
+        ostream.print(md.toNameAndVersion());
+        mref.location()
+                .filter(uri -> !isJrt(uri))
+                .ifPresent(uri -> ostream.format(" %s", uri));
+        if (md.isOpen())
+            ostream.print(" open");
+        if (md.isAutomatic())
+            ostream.print(" automatic");
+        ostream.println();
     }
 
-    static <T> String toString(Set<T> s) {
-        return toStringStream(s).collect(Collectors.joining(" "));
+    /**
+     * A ModuleReference comparator that considers modules in the run-time
+     * image to be less than modules than not in the run-time image.
+     */
+    private static class JrtFirstComparator implements Comparator<ModuleReference> {
+        private final Comparator<ModuleReference> real;
+
+        JrtFirstComparator() {
+            this.real = Comparator.comparing(ModuleReference::descriptor);
+        }
+
+        @Override
+        public int compare(ModuleReference a, ModuleReference b) {
+            if (isJrt(a)) {
+                return isJrt(b) ? real.compare(a, b) : -1;
+            } else {
+                return isJrt(b) ? 1 : real.compare(a, b);
+            }
+        }
     }
 
-    static <T> Stream<String> toStringStream(Set<T> s) {
+    private static <T> Stream<String> toStringStream(Set<T> s) {
         return s.stream().map(e -> e.toString().toLowerCase());
     }
 
-    static String midAndLocation(ModuleDescriptor md, Optional<URI> location ) {
-        URI loc = location.orElse(null);
-        if (loc == null || loc.getScheme().equalsIgnoreCase("jrt"))
-            return md.toNameAndVersion();
-        else
-            return md.toNameAndVersion() + " (" + loc + ")";
+    private static boolean isJrt(ModuleReference mref) {
+        return isJrt(mref.location().orElse(null));
+    }
+
+    private static boolean isJrt(URI uri) {
+        return (uri != null && uri.getScheme().equalsIgnoreCase("jrt"));
+    }
+
+    /**
+     * Called by the launcher to validate the modules on the upgrade and
+     * application module paths.
+     *
+     * @return {@code true} if no errors are found
+     */
+    private static boolean validateModules() {
+        initOutput(System.out);
+
+        ModuleValidator validator = new ModuleValidator();
+
+        // upgrade module path
+        String value = System.getProperty("jdk.module.upgrade.path");
+        if (value != null) {
+            Stream.of(value.split(File.pathSeparator))
+                    .map(Paths::get)
+                    .forEach(validator::scan);
+        }
+
+        // system modules
+        ModuleFinder.ofSystem().findAll().stream()
+                .sorted(Comparator.comparing(ModuleReference::descriptor))
+                .forEach(validator::process);
+
+        // application module path
+        value = System.getProperty("jdk.module.path");
+        if (value != null) {
+            Stream.of(value.split(File.pathSeparator))
+                    .map(Paths::get)
+                    .forEach(validator::scan);
+        }
+
+        return !validator.foundErrors();
+    }
+
+    /**
+     * A simple validator to check for errors and conflicts between modules.
+     */
+    static class ModuleValidator {
+        private static final String MODULE_INFO = "module-info.class";
+
+        private Map<String, ModuleReference> nameToModule = new HashMap<>();
+        private Map<String, ModuleReference> packageToModule = new HashMap<>();
+        private boolean errorFound;
+
+        /**
+         * Returns true if at least one error was found
+         */
+        boolean foundErrors() {
+            return errorFound;
+        }
+
+        /**
+         * Prints the module location and name.
+         */
+        private void printModule(ModuleReference mref) {
+            mref.location()
+                .filter(uri -> !isJrt(uri))
+                .ifPresent(uri -> ostream.print(uri + " "));
+            ModuleDescriptor descriptor = mref.descriptor();
+            ostream.print(descriptor.name());
+            if (descriptor.isAutomatic())
+                ostream.print(" automatic");
+            ostream.println();
+        }
+
+        /**
+         * Prints the module location and name, checks if the module is
+         * shadowed by a previously seen module, and finally checks for
+         * package conflicts with previously seen modules.
+         */
+        void process(ModuleReference mref) {
+            printModule(mref);
+
+            String name = mref.descriptor().name();
+            ModuleReference previous = nameToModule.putIfAbsent(name, mref);
+            if (previous != null) {
+                ostream.print(INDENT + "shadowed by ");
+                printModule(previous);
+            } else {
+                // check for package conflicts when not shadowed
+                for (String pkg :  mref.descriptor().packages()) {
+                    previous = packageToModule.putIfAbsent(pkg, mref);
+                    if (previous != null) {
+                        String mn = previous.descriptor().name();
+                        ostream.println(INDENT + "contains " + pkg
+                                        + " conflicts with module " + mn);
+                        errorFound = true;
+                    }
+                }
+            }
+        }
+
+        /**
+         * Scan an element on a module path. The element is a directory
+         * of modules, an exploded module, or a JAR file.
+         */
+        void scan(Path entry) {
+            BasicFileAttributes attrs;
+            try {
+                attrs = Files.readAttributes(entry, BasicFileAttributes.class);
+            } catch (NoSuchFileException ignore) {
+                return;
+            } catch (IOException ioe) {
+                ostream.println(entry + " " + ioe);
+                errorFound = true;
+                return;
+            }
+
+            String fn = entry.getFileName().toString();
+            if (attrs.isRegularFile() && fn.endsWith(".jar")) {
+                // JAR file, explicit or automatic module
+                scanModule(entry).ifPresent(this::process);
+            } else if (attrs.isDirectory()) {
+                Path mi = entry.resolve(MODULE_INFO);
+                if (Files.exists(mi)) {
+                    // exploded module
+                    scanModule(entry).ifPresent(this::process);
+                } else {
+                    // directory of modules
+                    scanDirectory(entry);
+                }
+            }
+        }
+
+        /**
+         * Scan the JAR files and exploded modules in a directory.
+         */
+        private void scanDirectory(Path dir) {
+            try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir)) {
+                Map<String, Path> moduleToEntry = new HashMap<>();
+
+                for (Path entry : stream) {
+                    BasicFileAttributes attrs;
+                    try {
+                        attrs = Files.readAttributes(entry, BasicFileAttributes.class);
+                    } catch (IOException ioe) {
+                        ostream.println(entry + " " + ioe);
+                        errorFound = true;
+                        continue;
+                    }
+
+                    ModuleReference mref = null;
+
+                    String fn = entry.getFileName().toString();
+                    if (attrs.isRegularFile() && fn.endsWith(".jar")) {
+                        mref = scanModule(entry).orElse(null);
+                    } else if (attrs.isDirectory()) {
+                        Path mi = entry.resolve(MODULE_INFO);
+                        if (Files.exists(mi)) {
+                            mref = scanModule(entry).orElse(null);
+                        }
+                    }
+
+                    if (mref != null) {
+                        String name = mref.descriptor().name();
+                        Path previous = moduleToEntry.putIfAbsent(name, entry);
+                        if (previous != null) {
+                            // same name as other module in the directory
+                            printModule(mref);
+                            ostream.println(INDENT + "contains same module as "
+                                            + previous.getFileName());
+                            errorFound = true;
+                        } else {
+                            process(mref);
+                        }
+                    }
+                }
+            } catch (IOException ioe) {
+                ostream.println(dir + " " + ioe);
+                errorFound = true;
+            }
+        }
+
+        /**
+         * Scan a JAR file or exploded module.
+         */
+        private Optional<ModuleReference> scanModule(Path entry) {
+            ModuleFinder finder = ModuleFinder.of(entry);
+            try {
+                return finder.findAll().stream().findFirst();
+            } catch (FindException e) {
+                ostream.println(entry);
+                ostream.println(INDENT + e.getMessage());
+                Throwable cause = e.getCause();
+                if (cause != null) {
+                    ostream.println(INDENT + cause);
+                }
+                errorFound = true;
+                return Optional.empty();
+            }
+        }
     }
 }
--- a/jdk/src/java.base/share/classes/sun/launcher/resources/launcher.properties	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/src/java.base/share/classes/sun/launcher/resources/launcher.properties	Thu May 04 07:26:55 2017 +0000
@@ -53,26 +53,33 @@
 \                  A {0} separated list of directories, each directory\n\
 \                  is a directory of modules that replace upgradeable\n\
 \                  modules in the runtime image\n\
-\    --add-modules <modulename>[,<modulename>...]\n\
+\    --add-modules <module name>[,<module name>...]\n\
 \                  root modules to resolve in addition to the initial module.\n\
-\                  <modulename> can also be ALL-DEFAULT, ALL-SYSTEM,\n\
+\                  <module name> can also be ALL-DEFAULT, ALL-SYSTEM,\n\
 \                  ALL-MODULE-PATH.\n\
-\    --limit-modules <modulename>[,<modulename>...]\n\
-\                  limit the universe of observable modules\n\
-\    --list-modules [<modulename>[,<modulename>...]]\n\
-\                  list the observable modules and exit\n\
-\    --dry-run     create VM but do not execute main method.\n\
-\                  This --dry-run option may be useful for validating the\n\
+\    --list-modules\n\
+\                  list observable modules and exit\n\
+\    --d <module name>\n\
+\    --describe-module <module name>\n\
+\                  describe a module and exit\n\
+\    --dry-run     create VM and load main class but do not execute main method.\n\
+\                  The --dry-run option may be useful for validating the\n\
 \                  command-line options such as the module system configuration.\n\
+\    --validate-modules\n\
+\                  validate all modules and exit\n\
+\                  The --validate-modules option may be useful for finding\n\
+\                  conflicts and other errors with modules on the module path.\n\
 \    -D<name>=<value>\n\
 \                  set a system property\n\
-\    -verbose:[class|gc|jni]\n\
+\    -verbose:[class|module|gc|jni]\n\
 \                  enable verbose output\n\
 \    -version      print product version to the error stream and exit\n\
 \    --version     print product version to the output stream and exit\n\
 \    -showversion  print product version to the error stream and continue\n\
 \    --show-version\n\
 \                  print product version to the output stream and continue\n\
+\    --show-module-resolution\n\
+\                  show module resolution output during startup\n\
 \    -? -h -help\n\
 \                  print this help message to the error stream\n\
 \    --help        print this help message to the output stream\n\
@@ -119,7 +126,6 @@
 \    -Xcomp            forces compilation of methods on first invocation\n\
 \    -Xdebug           provided for backward compatibility\n\
 \    -Xdiag            show additional diagnostic messages\n\
-\    -Xdiag:resolver   show resolver diagnostic messages\n\
 \    -Xfuture          enable strictest checks, anticipating future default\n\
 \    -Xint             interpreted mode execution only\n\
 \    -Xinternalversion\n\
@@ -164,10 +170,12 @@
 \                      permit illegal access to members of types in named modules\n\
 \                      by code in unnamed modules. This compatibility option will\n\
 \                      be removed in the next release.\n\
-\    --disable-@files  disable further argument file expansion\n\
+\    --limit-modules <module name>[,<module name>...]\n\
+\                      limit the universe of observable modules\n\
 \    --patch-module <module>=<file>({0}<file>)*\n\
-\                      Override or augment a module with classes and resources\n\
-\                      in JAR files or directories.\n\n\
+\                      override or augment a module with classes and resources\n\
+\                      in JAR files or directories.\n\
+\    --disable-@files  disable further argument file expansion\n\n\
 These extra options are subject to change without notice.\n
 
 # Translators please note do not translate the options themselves
@@ -204,6 +212,7 @@
     Error: An unexpected error occurred while trying to open file {0}
 java.launcher.jar.error2=manifest not found in {0}
 java.launcher.jar.error3=no main manifest attribute, in {0}
+java.launcher.jar.error4=error loading java agent in {0}
 java.launcher.init.error=initialization error
 java.launcher.javafx.error1=\
     Error: The JavaFX launchApplication method has the wrong signature, it\n\
@@ -215,4 +224,5 @@
 java.launcher.module.error3=\
     Error: Unable to load main class {0} from module {1}\n\
     \t{2}
-
+java.launcher.module.error4=\
+    {0} not found
--- a/jdk/src/java.base/share/native/libjli/emessages.h	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/src/java.base/share/native/libjli/emessages.h	Thu May 04 07:26:55 2017 +0000
@@ -43,13 +43,14 @@
 #define ARG_ERROR2      "Error: %s requires jar file specification"
 #define ARG_ERROR3      "Error: The -J option should not be followed by a space."
 #define ARG_ERROR4      "Error: %s requires module path specification"
-#define ARG_ERROR5      "Error: %s requires module id"
+#define ARG_ERROR5      "Error: %s requires module name"
 #define ARG_ERROR6      "Error: %s requires modules to be specified"
 #define ARG_ERROR7      "Error: %s can only be specified once"
 #define ARG_ERROR8      "Error: Unmatched quote in environment variable %s"
 #define ARG_ERROR9      "Error: Option %s is not allowed in environment variable %s"
 #define ARG_ERROR10     "Error: Option %s in %s is not allowed in environment variable %s"
 #define ARG_ERROR11     "Error: Cannot specify main class in environment variable %s"
+#define ARG_ERROR12     "Error: %s requires module name"
 
 #define JVM_ERROR1      "Error: Could not create the Java Virtual Machine.\n" GEN_ERROR
 #define JVM_ERROR2      "Error: Could not detach main thread.\n" JNI_ERROR
--- a/jdk/src/java.base/share/native/libjli/java.c	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/src/java.base/share/native/libjli/java.c	Thu May 04 07:26:55 2017 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1995, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1995, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -71,7 +71,10 @@
 static jboolean printXUsage = JNI_FALSE;  /* print and exit*/
 static jboolean dryRun = JNI_FALSE;       /* initialize VM and exit */
 static char     *showSettings = NULL;     /* print but continue */
-static char     *listModules = NULL;
+static jboolean showResolvedModules = JNI_FALSE;
+static jboolean listModules = JNI_FALSE;
+static char     *describeModule = NULL;
+static jboolean validateModules = JNI_FALSE;
 
 static const char *_program_name;
 static const char *_launcher_name;
@@ -118,7 +121,10 @@
 static void PrintJavaVersion(JNIEnv *env, jboolean extraLF);
 static void PrintUsage(JNIEnv* env, jboolean doXUsage);
 static void ShowSettings(JNIEnv* env, char *optString);
-static void ListModules(JNIEnv* env, char *optString);
+static void ShowResolvedModules(JNIEnv* env);
+static void ListModules(JNIEnv* env);
+static void DescribeModule(JNIEnv* env, char* optString);
+static jboolean ValidateModules(JNIEnv* env);
 
 static void SetPaths(int argc, char **argv);
 
@@ -409,12 +415,34 @@
         CHECK_EXCEPTION_LEAVE(1);
     }
 
-    if (listModules != NULL) {
-        ListModules(env, listModules);
+    // show resolved modules and continue
+    if (showResolvedModules) {
+        ShowResolvedModules(env);
+        CHECK_EXCEPTION_LEAVE(1);
+    }
+
+    // list observable modules, then exit
+    if (listModules) {
+        ListModules(env);
         CHECK_EXCEPTION_LEAVE(1);
         LEAVE();
     }
 
+    // describe a module, then exit
+    if (describeModule != NULL) {
+        DescribeModule(env, describeModule);
+        CHECK_EXCEPTION_LEAVE(1);
+        LEAVE();
+    }
+
+    // validate modules on the module path, then exit
+    if (validateModules) {
+        jboolean okay = ValidateModules(env);
+        CHECK_EXCEPTION_LEAVE(1);
+        if (!okay) ret = 1;
+        LEAVE();
+    }
+
     if (printVersion || showVersion) {
         PrintJavaVersion(env, showVersion);
         CHECK_EXCEPTION_LEAVE(0);
@@ -552,7 +580,8 @@
 IsLauncherOption(const char* name) {
     return IsClassPathOption(name) ||
            IsLauncherMainOption(name) ||
-           JLI_StrCmp(name, "--list-modules") == 0;
+           JLI_StrCmp(name, "--describe-module") == 0 ||
+           JLI_StrCmp(name, "-d") == 0;
 }
 
 /*
@@ -1199,7 +1228,7 @@
 
     } else if (JLI_StrCCmp(arg, "--") == 0 && (equals = JLI_StrChr(arg, '=')) != NULL) {
         value = equals+1;
-        if (JLI_StrCCmp(arg, "--list-modules=") == 0 ||
+        if (JLI_StrCCmp(arg, "--describe-module=") == 0 ||
             JLI_StrCCmp(arg, "--module=") == 0 ||
             JLI_StrCCmp(arg, "--class-path=") == 0) {
             kind = LAUNCHER_OPTION_WITH_ARGUMENT;
@@ -1263,18 +1292,18 @@
             REPORT_ERROR (has_arg_any_len, ARG_ERROR1, arg);
             SetClassPath(value);
             mode = LM_CLASS;
-        } else if (JLI_StrCmp(arg, "--list-modules") == 0 ||
-                   JLI_StrCCmp(arg, "--list-modules=") == 0) {
-            listModules = arg;
-
-            // set listModules to --list-modules=<module-names> if argument is specified
-            if (JLI_StrCmp(arg, "--list-modules") == 0 && has_arg) {
-                static const char format[] = "%s=%s";
-                size_t buflen = JLI_StrLen(option) + 2 + JLI_StrLen(value);
-                listModules = JLI_MemAlloc(buflen);
-                JLI_Snprintf(listModules, buflen, format, option, value);
-            }
-            return JNI_TRUE;
+        } else if (JLI_StrCmp(arg, "--list-modules") == 0) {
+            listModules = JNI_TRUE;
+        } else if (JLI_StrCmp(arg, "--show-resolved-modules") == 0) {
+            showResolvedModules = JNI_TRUE;
+        } else if (JLI_StrCmp(arg, "--validate-modules") == 0) {
+            AddOption("-Djdk.module.minimumBoot=true", NULL);
+            validateModules = JNI_TRUE;
+        } else if (JLI_StrCmp(arg, "--describe-module") == 0 ||
+                   JLI_StrCCmp(arg, "--describe-module=") == 0 ||
+                   JLI_StrCmp(arg, "-d") == 0) {
+            REPORT_ERROR (has_arg_any_len, ARG_ERROR12, arg);
+            describeModule = value;
 /*
  * Parse white-space options
  */
@@ -1336,9 +1365,8 @@
             showSettings = arg;
         } else if (JLI_StrCmp(arg, "-Xdiag") == 0) {
             AddOption("-Dsun.java.launcher.diag=true", NULL);
-            AddOption("-Djdk.launcher.traceResolver=true", NULL);
-        } else if (JLI_StrCmp(arg, "-Xdiag:resolver") == 0) {
-            AddOption("-Djdk.launcher.traceResolver=true", NULL);
+        } else if (JLI_StrCmp(arg, "--show-module-resolution") == 0) {
+            AddOption("-Djdk.module.showModuleResolution=true", NULL);
 /*
  * The following case provide backward compatibility with old-style
  * command line options.
@@ -1399,7 +1427,10 @@
     }
 
     if (*pwhat == NULL) {
-        *pret = 1;
+        /* LM_UNKNOWN okay for options that exit */
+        if (!listModules && !describeModule && !validateModules) {
+            *pret = 1;
+        }
     } else if (mode == LM_UNKNOWN) {
         /* default to LM_CLASS if -m, -jar and -cp options are
          * not specified */
@@ -1828,21 +1859,61 @@
 }
 
 /**
- * List modules supported by the runtime
+ * Show resolved modules
+ */
+static void
+ShowResolvedModules(JNIEnv *env)
+{
+    jmethodID showResolvedModulesID;
+    jclass cls = GetLauncherHelperClass(env);
+    NULL_CHECK(cls);
+    NULL_CHECK(showResolvedModulesID = (*env)->GetStaticMethodID(env, cls,
+            "showResolvedModules", "()V"));
+    (*env)->CallStaticVoidMethod(env, cls, showResolvedModulesID);
+}
+
+/**
+ * List observable modules
  */
 static void
-ListModules(JNIEnv *env, char *optString)
+ListModules(JNIEnv *env)
 {
     jmethodID listModulesID;
+    jclass cls = GetLauncherHelperClass(env);
+    NULL_CHECK(cls);
+    NULL_CHECK(listModulesID = (*env)->GetStaticMethodID(env, cls,
+            "listModules", "()V"));
+    (*env)->CallStaticVoidMethod(env, cls, listModulesID);
+}
+
+/**
+ * Describe a module
+ */
+static void
+DescribeModule(JNIEnv *env, char *optString)
+{
+    jmethodID describeModuleID;
     jstring joptString = NULL;
     jclass cls = GetLauncherHelperClass(env);
     NULL_CHECK(cls);
-    NULL_CHECK(listModulesID = (*env)->GetStaticMethodID(env, cls,
-            "listModules", "(ZLjava/lang/String;)V"));
+    NULL_CHECK(describeModuleID = (*env)->GetStaticMethodID(env, cls,
+            "describeModule", "(Ljava/lang/String;)V"));
     NULL_CHECK(joptString = (*env)->NewStringUTF(env, optString));
-    (*env)->CallStaticVoidMethod(env, cls, listModulesID,
-                                 USE_STDOUT,
-                                 joptString);
+    (*env)->CallStaticVoidMethod(env, cls, describeModuleID, joptString);
+}
+
+/**
+ * Validate modules
+ */
+static jboolean
+ValidateModules(JNIEnv *env)
+{
+    jmethodID validateModulesID;
+    jclass cls = GetLauncherHelperClass(env);
+    NULL_CHECK_RETURN_VALUE(cls, JNI_FALSE);
+    validateModulesID = (*env)->GetStaticMethodID(env, cls, "validateModules", "()Z");
+    NULL_CHECK_RETURN_VALUE(cls, JNI_FALSE);
+    return (*env)->CallStaticBooleanMethod(env, cls, validateModulesID);
 }
 
 /*
--- a/jdk/src/java.instrument/share/classes/java/lang/instrument/Instrumentation.java	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/src/java.instrument/share/classes/java/lang/instrument/Instrumentation.java	Thu May 04 07:26:55 2017 +0000
@@ -729,7 +729,8 @@
      * Tests whether a module can be modified with {@link #redefineModule
      * redefineModule}. If a module is modifiable then this method returns
      * {@code true}. If a module is not modifiable then this method returns
-     * {@code false}.
+     * {@code false}. This method always returns {@code true} when the module
+     * is an unnamed module (as redefining an unnamed module is a no-op).
      *
      * @param module the module to test if it can be modified
      * @return {@code true} if the module is modifiable, otherwise {@code false}
--- a/jdk/src/java.instrument/share/classes/java/lang/instrument/package.html	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/src/java.instrument/share/classes/java/lang/instrument/package.html	Thu May 04 07:26:55 2017 +0000
@@ -1,5 +1,5 @@
 <!--
- Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved.
+ Copyright (c) 2003, 2017, Oracle and/or its affiliates. All rights reserved.
  DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 
  This code is free software; you can redistribute it and/or modify it
@@ -112,7 +112,9 @@
 The <code>premain</code> methods will be run under the same security and classloader 
 rules as the application <code>main</code> method.
 There are no modeling restrictions on what the agent <code>premain</code> method may do.
-Anything application <code>main</code> can do, including creating threads, is legal from <code>premain</code>.
+Anything application <code>main</code> can do, including creating threads, is legal from
+<code>premain</code>.
+
 
 <P>
 Each agent is passed its agent options via the <code>agentArgs</code> parameter.
@@ -126,7 +128,6 @@
 If a <code>premain</code> method throws an uncaught exception, the JVM will abort.
 
 
-
 <h3>Starting Agents After VM Startup</h3>
 
 <p>
@@ -191,76 +192,134 @@
 not abort. If the <code>agentmain</code> method throws an uncaught exception it will be ignored.
 
 
+<h3>Deploying Agents in Executable JAR file</h3>
+
+The JAR File Specification defines manifest attributes for standalone applications that are
+bundled as <em>executable JAR files</em>. If an implementation supports a mechanism to start
+an application as an executable JAR then the main manifest may include the
+<code>Launcher-Agent-Class</code> attribute to specify the class name
+of an agent to start before the application <code>main</code> method is invoked. The Java
+virtual machine attempts to invoke the following method on the agent class:
+
+<blockquote>
+    <code>public static void
+        agentmain(String agentArgs, Instrumentation inst);
+    </code>
+</blockquote>
+
+<P>
+If the agent class does not implement this method then the JVM will attempt to invoke:
+
+<blockquote>
+    <code>public static void
+        agentmain(String agentArgs);
+    </code>
+</blockquote>
+
+<p>
+The value of the <code>agentArgs</code> parameter is always the empty string.
+
+<P>
+The <code>agentmain</code> method should do any necessary initialization
+required to start the agent and return. If the agent cannot be started, for
+example the agent class cannot be loaded, the agent class does not define a
+conformant <code>agentmain</code> method, or the <code>agentmain</code> method
+throws an uncaught exception or error, the JVM will abort.
+
+
+<h3>Visibility</h3>
+
+The types visible to the agent class are the types visible to the system class
+loader. They minimally include the types in packages exported by
+<a href="{@docRoot}/java.base-summary.html">java.base</a> and
+<a href="{@docRoot}/java.instrument-summary.html">java.instrument</a>.
+Whether all {@linkplain ClassLoader#getPlatformClassLoader() platform classes}
+are visible or not will depend on the initial module or application.
+
+<p>
+Supporting classes that the agent makes visible to the bootstrap class loader
+(by means of {@link Instrumentation#appendToBootstrapClassLoaderSearch
+appendToBootstrapClassLoaderSearch} or the <code>Boot-Class-Path</code> attribute
+specified below) can only link to types defined to the bootstrap class loader.
+There is no guarantee that all platform classes are visible to the boot class
+loader.
+
 
 <h3>Manifest Attributes</h3>
+
 The following manifest attributes are defined for an agent JAR file:
 <blockquote>
 <dl>
 <dt><code>Premain-Class</code></dt>
 <dd>
-                        When an agent is specified at JVM launch time this attribute
-			specifies the agent class.
-			That is, the class containing the <code>premain</code> method.
-                        When an agent is specified at JVM launch time this attribute
-			is required. If the attribute is not present the JVM will abort.
-                        Note: this is a class name, not a file name or path.							
+            When an agent is specified at JVM launch time this attribute
+            specifies the agent class.
+            That is, the class containing the <code>premain</code> method.
+            When an agent is specified at JVM launch time this attribute
+            is required. If the attribute is not present the JVM will abort.
+            Note: this is a class name, not a file name or path.
 </dd>
-
 <dt><code>Agent-Class</code></dt>
 <dd>
-                        If an implementation supports a mechanism to start agents 
-                        sometime after the VM has started then this attribute specifies
-                        the agent class.
-                        That is, the class containing the <code>agentmain</code> method.
-                        This attribute is required, if it is not present the agent
-                        will not be started.
-                        Note: this is a class name, not a file name or path.
-</dd>			
-
+            If an implementation supports a mechanism to start agents
+            sometime after the VM has started then this attribute specifies
+            the agent class.
+            That is, the class containing the <code>agentmain</code> method.
+            This attribute is required, if it is not present the agent
+            will not be started.
+            Note: this is a class name, not a file name or path.
+</dd>
+<dt><code>Launcher-Agent-Class</code></dt>
+<dd>
+            If an implementation supports a mechanism to start an application
+            as an executable JAR then the main manifest may include this
+            attribute to specify the class name of an agent to start before the
+            application <code>main</code> method is invoked.
+</dd>
 <dt><code>Boot-Class-Path</code></dt>
 <dd>
-                        A list of paths to be searched by the bootstrap class
-                        loader. Paths represent directories or libraries
-                        (commonly referred to as JAR or zip libraries on
-                        many platforms). 			
-                        These paths are searched by the
-                        bootstrap class loader after the platform specific
-                        mechanisms of locating a class have failed.
-                        Paths are searched in the order listed.
-                        Paths in the list are separated by one or more spaces.
-                        A path takes the syntax of the path component of a
-                        hierarchical URI. The path is
-                        absolute if it begins with a slash character ('/'),
-                        otherwise it is relative. A relative path is resolved
-                        against the absolute path of the agent JAR file.
-                        Malformed and non-existent paths are ignored.	
-			When an agent is started sometime after the VM has
-			started then paths that do not represent a JAR file
-			are ignored.
-                        This attribute is optional.
+            A list of paths to be searched by the bootstrap class
+            loader. Paths represent directories or libraries
+            (commonly referred to as JAR or zip libraries on
+            many platforms).
+            These paths are searched by the
+            bootstrap class loader after the platform specific
+            mechanisms of locating a class have failed.
+            Paths are searched in the order listed.
+            Paths in the list are separated by one or more spaces.
+            A path takes the syntax of the path component of a
+            hierarchical URI. The path is
+            absolute if it begins with a slash character ('/'),
+            otherwise it is relative. A relative path is resolved
+            against the absolute path of the agent JAR file.
+            Malformed and non-existent paths are ignored.
+            When an agent is started sometime after the VM has
+            started then paths that do not represent a JAR file
+            are ignored.
+            This attribute is optional.
 </dd>
 <dt><code>Can-Redefine-Classes</code></dt>
 <dd>
-                        Boolean (<code>true</code> or <code>false</code>, case irrelevant).
-                        Is the ability to redefine classes
-                        needed by this agent.
-                        Values other than <code>true</code> are considered <code>false</code>.
-                        This attribute is optional, the default is <code>false</code>.
+            Boolean (<code>true</code> or <code>false</code>, case irrelevant).
+            Is the ability to redefine classes
+            needed by this agent.
+            Values other than <code>true</code> are considered <code>false</code>.
+            This attribute is optional, the default is <code>false</code>.
 </dd>
 <dt><code>Can-Retransform-Classes</code></dt>
 <dd>
-                        Boolean (<code>true</code> or <code>false</code>, case irrelevant).
-                        Is the ability to retransform classes
-                        needed by this agent.
-                        Values other than <code>true</code> are considered <code>false</code>.
-                        This attribute is optional, the default is <code>false</code>.
+            Boolean (<code>true</code> or <code>false</code>, case irrelevant).
+            Is the ability to retransform classes
+            needed by this agent.
+            Values other than <code>true</code> are considered <code>false</code>.
+            This attribute is optional, the default is <code>false</code>.
 </dd>
 <dt><code>Can-Set-Native-Method-Prefix</code></dt>
 <dd>
-                        Boolean (<code>true</code> or <code>false</code>, case irrelevant).
-                        Is the ability to set native method prefix needed by this agent.
-                        Values other than <code>true</code> are considered <code>false</code>.
-                        This attribute is optional, the default is <code>false</code>.
+            Boolean (<code>true</code> or <code>false</code>, case irrelevant).
+            Is the ability to set native method prefix needed by this agent.
+            Values other than <code>true</code> are considered <code>false</code>.
+            This attribute is optional, the default is <code>false</code>.
 </dd>
 </dl>
 </blockquote>
--- a/jdk/src/java.instrument/share/classes/module-info.java	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/src/java.instrument/share/classes/module-info.java	Thu May 04 07:26:55 2017 +0000
@@ -32,5 +32,8 @@
  */
 module java.instrument {
     exports java.lang.instrument;
+
+    // allow java launcher to load agents in executable JAR files
+    exports sun.instrument to java.base;
 }
 
--- a/jdk/src/java.instrument/share/classes/sun/instrument/InstrumentationImpl.java	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/src/java.instrument/share/classes/sun/instrument/InstrumentationImpl.java	Thu May 04 07:26:55 2017 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -555,4 +555,15 @@
                                     classfileBuffer);
         }
     }
+
+
+    /**
+     * Invoked by the java launcher to load a java agent that is packaged with
+     * the main application in an executable JAR file.
+     */
+    public static void loadAgent(String path) {
+        loadAgent0(path);
+    }
+
+    private static native void loadAgent0(String path);
 }
--- a/jdk/src/java.instrument/share/native/libinstrument/InstrumentationImplNativeMethods.c	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/src/java.instrument/share/native/libinstrument/InstrumentationImplNativeMethods.c	Thu May 04 07:26:55 2017 +0000
@@ -159,3 +159,20 @@
   (JNIEnv * jnienv, jobject implThis, jlong agent, jobjectArray prefixArray, jboolean isRetransformable) {
     setNativeMethodPrefixes(jnienv, (JPLISAgent*)(intptr_t)agent, prefixArray, isRetransformable);
 }
+
+
+/*
+ * Class:     sun_instrument_InstrumentationImpl
+ * Method:    loadAgent0
+ */
+JNIEXPORT void JNICALL Java_sun_instrument_InstrumentationImpl_loadAgent0
+   (JNIEnv* env, jclass clazz, jstring jarfile)
+{
+    extern jint loadAgent(JNIEnv* env, jstring path);
+    if (loadAgent(env, jarfile) != JNI_OK) {
+        if (!(*env)->ExceptionCheck(env)) {
+            createAndThrowInternalError(env);
+        }
+    }
+}
+
--- a/jdk/src/java.instrument/share/native/libinstrument/InvocationAdapter.c	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/src/java.instrument/share/native/libinstrument/InvocationAdapter.c	Thu May 04 07:26:55 2017 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -106,7 +106,7 @@
  * convert them to JVM TI capabilities.
  */
 void
-convertCapabilityAtrributes(const jarAttribute* attributes, JPLISAgent* agent) {
+convertCapabilityAttributes(const jarAttribute* attributes, JPLISAgent* agent) {
     /* set redefineClasses capability */
     if (getBooleanAttribute(attributes, "Can-Redefine-Classes")) {
         addRedefineClassesCapability(agent);
@@ -229,7 +229,7 @@
         /*
          * Convert JAR attributes into agent capabilities
          */
-        convertCapabilityAtrributes(attributes, agent);
+        convertCapabilityAttributes(attributes, agent);
 
         /*
          * Track (record) the agent class name and options data
@@ -386,7 +386,7 @@
         /*
          * Convert JAR attributes into agent capabilities
          */
-        convertCapabilityAtrributes(attributes, agent);
+        convertCapabilityAttributes(attributes, agent);
 
         /*
          * Create the java.lang.instrument.Instrumentation instance
@@ -435,6 +435,109 @@
 DEF_Agent_OnUnload(JavaVM *vm) {
 }
 
+/**
+ * Invoked by the java launcher to load an agent in the main executable JAR.
+ * The Launcher-Agent-Class attribute in the main manifest of the JAR file
+ * is the agent class.
+ *
+ * Returns JNI_OK if the agent is loaded and initialized; JNI_ERR if this
+ * function fails, possibly with a pending exception.
+ */
+jint loadAgent(JNIEnv* env, jstring path) {
+    JavaVM* vm;
+    JPLISAgent* agent;
+    const char* jarfile = NULL;
+    jarAttribute* attributes = NULL;
+    char* agentClass = NULL;
+    char* bootClassPath;
+    int oldLen, newLen;
+    jint result = JNI_ERR;
+
+    if ((*env)->GetJavaVM(env, &vm) < 0) {
+        return JNI_ERR;
+    }
+
+    // create JPLISAgent with JVMTI environment
+    if (createNewJPLISAgent(vm, &agent) != JPLIS_INIT_ERROR_NONE) {
+        return JNI_ERR;
+    }
+
+    // get path to JAR file as UTF-8 string
+    jarfile = (*env)->GetStringUTFChars(env, path, NULL);
+    if (jarfile == NULL) {
+        return JNI_ERR;
+    }
+
+    // read the attributes in the main section of JAR manifest
+    attributes = readAttributes(jarfile);
+    if (attributes == NULL) {
+        goto releaseAndReturn;
+    }
+
+    // Launcher-Agent-Class is required
+    agentClass = getAttribute(attributes, "Launcher-Agent-Class");
+    if (agentClass == NULL) {
+        goto releaseAndReturn;
+    }
+
+    // The value of Launcher-Agent-Class is in UTF-8, convert it to modified UTF-8
+    oldLen = (int) strlen(agentClass);
+    newLen = modifiedUtf8LengthOfUtf8(agentClass, oldLen);
+    if (newLen == oldLen) {
+        agentClass = strdup(agentClass);
+    } else {
+        char* str = (char*) malloc(newLen + 1);
+        if (str != NULL) {
+            convertUtf8ToModifiedUtf8(agentClass, oldLen, str, newLen);
+        }
+        agentClass = str;
+    }
+    if (agentClass == NULL) {
+         jthrowable oome = createThrowable(env, "java/lang/OutOfMemoryError", NULL);
+         if (oome != NULL) (*env)->Throw(env, oome);
+         goto releaseAndReturn;
+    }
+
+    // Boot-Class-Path
+    bootClassPath = getAttribute(attributes, "Boot-Class-Path");
+    if (bootClassPath != NULL) {
+        appendBootClassPath(agent, jarfile, bootClassPath);
+    }
+
+    // Can-XXXX capabilities
+    convertCapabilityAttributes(attributes, agent);
+
+    // Create the java.lang.instrument.Instrumentation object
+    if (!createInstrumentationImpl(env, agent)) {
+        goto releaseAndReturn;
+    }
+
+    // Enable the ClassFileLoadHook
+    if (!setLivePhaseEventHandlers(agent)) {
+        goto releaseAndReturn;
+    }
+
+    // invoke the agentmain method
+    if (!startJavaAgent(agent, env, agentClass, "", agent->mAgentmainCaller)) {
+        goto releaseAndReturn;
+    }
+
+    // initialization complete
+    result = JNI_OK;
+
+    releaseAndReturn:
+        if (agentClass != NULL) {
+            free(agentClass);
+        }
+        if (attributes != NULL) {
+            freeAttributes(attributes);
+        }
+        if (jarfile != NULL) {
+            (*env)->ReleaseStringUTFChars(env, path, jarfile);
+        }
+
+    return result;
+}
 
 /*
  *  JVMTI callback support
--- a/jdk/src/java.management/share/classes/java/lang/management/ThreadInfo.java	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/src/java.management/share/classes/java/lang/management/ThreadInfo.java	Thu May 04 07:26:55 2017 +0000
@@ -841,6 +841,9 @@
      * @return a {@code ThreadInfo} object represented
      *         by {@code cd} if {@code cd} is not {@code null};
      *         {@code null} otherwise.
+     *
+     * @revised 9
+     * @spec JPMS
      */
     public static ThreadInfo from(CompositeData cd) {
         if (cd == null) {
--- a/jdk/src/jdk.attach/aix/classes/sun/tools/attach/VirtualMachineImpl.java	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/src/jdk.attach/aix/classes/sun/tools/attach/VirtualMachineImpl.java	Thu May 04 07:26:55 2017 +0000
@@ -214,13 +214,14 @@
             // Special-case the "load" command so that the right exception is
             // thrown.
             if (cmd.equals("load")) {
-                throw new AgentLoadException("Failed to load agent library");
+                String msg = "Failed to load agent library";
+                if (!message.isEmpty())
+                    msg += ": " + message;
+                throw new AgentLoadException(msg);
             } else {
-                if (message == null) {
-                    throw new AttachOperationFailedException("Command failed in target VM");
-                } else {
-                    throw new AttachOperationFailedException(message);
-                }
+                if (message.isEmpty())
+                    message = "Command failed in target VM";
+                throw new AttachOperationFailedException(message);
             }
         }
 
--- a/jdk/src/jdk.attach/linux/classes/sun/tools/attach/VirtualMachineImpl.java	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/src/jdk.attach/linux/classes/sun/tools/attach/VirtualMachineImpl.java	Thu May 04 07:26:55 2017 +0000
@@ -211,13 +211,14 @@
             // Special-case the "load" command so that the right exception is
             // thrown.
             if (cmd.equals("load")) {
-                throw new AgentLoadException("Failed to load agent library");
+                String msg = "Failed to load agent library";
+                if (!message.isEmpty())
+                    msg += ": " + message;
+                throw new AgentLoadException(msg);
             } else {
-                if (message == null) {
-                    throw new AttachOperationFailedException("Command failed in target VM");
-                } else {
-                    throw new AttachOperationFailedException(message);
-                }
+                if (message.isEmpty())
+                    message = "Command failed in target VM";
+                throw new AttachOperationFailedException(message);
             }
         }
 
--- a/jdk/src/jdk.attach/macosx/classes/sun/tools/attach/VirtualMachineImpl.java	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/src/jdk.attach/macosx/classes/sun/tools/attach/VirtualMachineImpl.java	Thu May 04 07:26:55 2017 +0000
@@ -213,13 +213,14 @@
             // Special-case the "load" command so that the right exception is
             // thrown.
             if (cmd.equals("load")) {
-                throw new AgentLoadException("Failed to load agent library");
+                String msg = "Failed to load agent library";
+                if (!message.isEmpty())
+                    msg += ": " + message;
+                throw new AgentLoadException(msg);
             } else {
-                if (message == null) {
-                    throw new AttachOperationFailedException("Command failed in target VM");
-                } else {
-                    throw new AttachOperationFailedException(message);
-                }
+                if (message.isEmpty())
+                    message = "Command failed in target VM";
+                throw new AttachOperationFailedException(message);
             }
         }
 
--- a/jdk/src/jdk.attach/share/classes/sun/tools/attach/HotSpotVirtualMachine.java	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/src/jdk.attach/share/classes/sun/tools/attach/HotSpotVirtualMachine.java	Thu May 04 07:26:55 2017 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -25,15 +25,19 @@
 
 package sun.tools.attach;
 
+import com.sun.tools.attach.AttachNotSupportedException;
 import com.sun.tools.attach.VirtualMachine;
 import com.sun.tools.attach.AgentLoadException;
 import com.sun.tools.attach.AgentInitializationException;
 import com.sun.tools.attach.spi.AttachProvider;
+import jdk.internal.misc.VM;
 
 import java.io.BufferedReader;
 import java.io.InputStream;
 import java.io.IOException;
 import java.io.InputStreamReader;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
 import java.util.Properties;
 import java.util.stream.Collectors;
 
@@ -43,8 +47,33 @@
 
 public abstract class HotSpotVirtualMachine extends VirtualMachine {
 
-    HotSpotVirtualMachine(AttachProvider provider, String id) {
+    private static final long CURRENT_PID;
+    private static final boolean ALLOW_ATTACH_SELF;
+    static {
+        PrivilegedAction<ProcessHandle> pa = ProcessHandle::current;
+        CURRENT_PID = AccessController.doPrivileged(pa).pid();
+
+        String s = VM.getSavedProperty("jdk.attach.allowAttachSelf");
+        ALLOW_ATTACH_SELF = "".equals(s) || Boolean.parseBoolean(s);
+    }
+
+    HotSpotVirtualMachine(AttachProvider provider, String id)
+        throws AttachNotSupportedException, IOException
+    {
         super(provider, id);
+
+        int pid;
+        try {
+            pid = Integer.parseInt(id);
+        } catch (NumberFormatException e) {
+            throw new AttachNotSupportedException("Invalid process identifier");
+        }
+
+        // The tool should be a different VM to the target. This check will
+        // eventually be enforced by the target VM.
+        if (!ALLOW_ATTACH_SELF && (pid == 0 || pid == CURRENT_PID)) {
+            throw new IOException("Can not attach to current VM");
+        }
     }
 
     /*
@@ -103,8 +132,6 @@
         }
         try {
             loadAgentLibrary("instrument", args);
-        } catch (AgentLoadException x) {
-            throw new InternalError("instrument library is missing in target VM", x);
         } catch (AgentInitializationException x) {
             /*
              * Translate interesting errors into the right exception and
@@ -116,13 +143,17 @@
                 case JNI_ENOMEM:
                     throw new AgentLoadException("Insuffient memory");
                 case ATTACH_ERROR_BADJAR:
-                    throw new AgentLoadException("Agent JAR not found or no Agent-Class attribute");
+                    throw new AgentLoadException(
+                        "Agent JAR not found or no Agent-Class attribute");
                 case ATTACH_ERROR_NOTONCP:
-                    throw new AgentLoadException("Unable to add JAR file to system class path");
+                    throw new AgentLoadException(
+                        "Unable to add JAR file to system class path");
                 case ATTACH_ERROR_STARTFAIL:
-                    throw new AgentInitializationException("Agent JAR loaded but agent failed to initialize");
+                    throw new AgentInitializationException(
+                        "Agent JAR loaded but agent failed to initialize");
                 default :
-                    throw new AgentLoadException("Failed to load agent - unknown reason: " + rc);
+                    throw new AgentLoadException("" +
+                        "Failed to load agent - unknown reason: " + rc);
             }
         }
     }
@@ -163,20 +194,20 @@
         return props;
     }
 
-    private static final String MANAGMENT_PREFIX = "com.sun.management.";
+    private static final String MANAGEMENT_PREFIX = "com.sun.management.";
 
     private static boolean checkedKeyName(Object key) {
         if (!(key instanceof String)) {
             throw new IllegalArgumentException("Invalid option (not a String): "+key);
         }
-        if (!((String)key).startsWith(MANAGMENT_PREFIX)) {
+        if (!((String)key).startsWith(MANAGEMENT_PREFIX)) {
             throw new IllegalArgumentException("Invalid option: "+key);
         }
         return true;
     }
 
     private static String stripKeyName(Object key) {
-        return ((String)key).substring(MANAGMENT_PREFIX.length());
+        return ((String)key).substring(MANAGEMENT_PREFIX.length());
     }
 
     @Override
@@ -204,9 +235,11 @@
     @Override
     public String startLocalManagementAgent() throws IOException {
         executeJCmd("ManagementAgent.start_local").close();
-        return getAgentProperties().getProperty("com.sun.management.jmxremote.localConnectorAddress");
+        String prop = MANAGEMENT_PREFIX + "jmxremote.localConnectorAddress";
+        return getAgentProperties().getProperty(prop);
     }
 
+
     // --- HotSpot specific methods ---
 
     // same as SIGQUIT
@@ -245,9 +278,9 @@
         return executeCommand("jcmd", command);
     }
 
+
     // -- Supporting methods
 
-
     /*
      * Execute the given command in the target VM - specific platform
      * implementation must implement this.
@@ -306,10 +339,10 @@
     /*
      * Utility method to read data into a String.
      */
-    String readErrorMessage(InputStream sis) throws IOException {
+    String readErrorMessage(InputStream in) throws IOException {
         String s;
         StringBuilder message = new StringBuilder();
-        BufferedReader br = new BufferedReader(new InputStreamReader(sis));
+        BufferedReader br = new BufferedReader(new InputStreamReader(in));
         while ((s = br.readLine()) != null) {
             message.append(s);
         }
--- a/jdk/src/jdk.attach/solaris/classes/sun/tools/attach/VirtualMachineImpl.java	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/src/jdk.attach/solaris/classes/sun/tools/attach/VirtualMachineImpl.java	Thu May 04 07:26:55 2017 +0000
@@ -160,13 +160,14 @@
             String message = readErrorMessage(sis);
             sis.close();
             if (cmd.equals("load")) {
-                throw new AgentLoadException("Failed to load agent library");
+                String msg = "Failed to load agent library";
+                if (!message.isEmpty())
+                    msg += ": " + message;
+                throw new AgentLoadException(msg);
             } else {
-                if (message == null) {
-                    throw new AttachOperationFailedException("Command failed in target VM");
-                } else {
-                    throw new AttachOperationFailedException(message);
-                }
+                if (message.isEmpty())
+                    message = "Command failed in target VM";
+                throw new AttachOperationFailedException(message);
             }
         }
 
--- a/jdk/src/jdk.attach/windows/classes/sun/tools/attach/VirtualMachineImpl.java	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/src/jdk.attach/windows/classes/sun/tools/attach/VirtualMachineImpl.java	Thu May 04 07:26:55 2017 +0000
@@ -100,28 +100,29 @@
             connectPipe(hPipe);
 
             // create an input stream for the pipe
-            PipedInputStream is = new PipedInputStream(hPipe);
+            PipedInputStream in = new PipedInputStream(hPipe);
 
             // read completion status
-            int status = readInt(is);
+            int status = readInt(in);
             if (status != 0) {
                 // read from the stream and use that as the error message
-                String message = readErrorMessage(is);
-                is.close();
+                String message = readErrorMessage(in);
+                in.close();
                 // special case the load command so that the right exception is thrown
                 if (cmd.equals("load")) {
-                    throw new AgentLoadException("Failed to load agent library");
+                    String msg = "Failed to load agent library";
+                    if (!message.isEmpty())
+                        msg += ": " + message;
+                    throw new AgentLoadException(msg);
                 } else {
-                    if (message == null) {
-                        throw new AttachOperationFailedException("Command failed in target VM");
-                    } else {
-                        throw new AttachOperationFailedException(message);
-                    }
+                    if (message.isEmpty())
+                        message = "Command failed in target VM";
+                    throw new AttachOperationFailedException(message);
                 }
             }
 
             // return the input stream
-            return is;
+            return in;
 
         } catch (IOException ioe) {
             closePipe(hPipe);
--- a/jdk/src/jdk.jartool/share/classes/sun/tools/jar/Main.java	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/src/jdk.jartool/share/classes/sun/tools/jar/Main.java	Thu May 04 07:26:55 2017 +0000
@@ -71,6 +71,7 @@
 import static java.util.jar.JarFile.MANIFEST_NAME;
 import static java.util.stream.Collectors.joining;
 import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
+import static sun.tools.jar.Validator.ENTRYNAME_COMPARATOR;
 
 /**
  * This class implements a simple utility for creating files in the JAR
@@ -132,6 +133,10 @@
     // if --release option found followed by at least file
     boolean isMultiRelease;
 
+    // The last parsed --release value, if any. Used in conjunction with
+    // "-d,--describe-module" to select the operative module descriptor.
+    int releaseValue = -1;
+
     /*
      * cflag: create
      * uflag: update
@@ -413,7 +418,7 @@
                     }
                 } else {
                     try (FileInputStream fin = new FileInputStream(FileDescriptor.in)) {
-                        found = describeModule(fin);
+                        found = describeModuleFromStream(fin);
                     }
                 }
                 if (!found)
@@ -604,11 +609,6 @@
         /* parse file arguments */
         int n = args.length - count;
         if (n > 0) {
-            if (dflag) {
-                // "--describe-module/-d" does not require file argument(s)
-                usageError(formatMsg("error.bad.dflag", args[count]));
-                return false;
-            }
             int version = BASE_VERSION;
             int k = 0;
             String[] nameBuf = new String[n];
@@ -616,6 +616,12 @@
             try {
                 for (int i = count; i < args.length; i++) {
                     if (args[i].equals("-C")) {
+                        if (dflag) {
+                            // "--describe-module/-d" does not require file argument(s),
+                            // but does accept --release
+                            usageError(getMsg("error.bad.dflag"));
+                            return false;
+                        }
                         /* change the directory */
                         String dir = args[++i];
                         dir = (dir.endsWith(File.separator) ?
@@ -649,8 +655,15 @@
                         k = 0;
                         nameBuf = new String[n];
                         version = v;
+                        releaseValue = version;
                         pathsMap.put(version, new HashSet<>());
                     } else {
+                        if (dflag) {
+                            // "--describe-module/-d" does not require file argument(s),
+                            // but does accept --release
+                            usageError(getMsg("error.bad.dflag"));
+                            return false;
+                        }
                         nameBuf[k++] = args[i];
                     }
                 }
@@ -756,7 +769,7 @@
      * can be found by recursively descending directories.
      *
      * @param dir    parent directory
-     * @param file s list of files to expand
+     * @param files  list of files to expand
      * @param cpaths set of directories specified by -C option for the files
      * @throws IOException if an I/O error occurs
      */
@@ -1721,23 +1734,62 @@
 
     // Modular jar support
 
-    static <T> String toString(Collection<T> c,
-                               CharSequence prefix,
-                               CharSequence suffix ) {
-        if (c.isEmpty())
-            return "";
-        return c.stream().map(e -> e.toString())
-                           .collect(joining(", ", prefix, suffix));
+    /**
+     * Associates a module descriptor's zip entry name along with its
+     * bytes and an optional URI. Used when describing modules.
+     */
+    interface ModuleInfoEntry {
+       String name();
+       Optional<String> uriString();
+       InputStream bytes() throws IOException;
     }
 
+    static class ZipFileModuleInfoEntry implements ModuleInfoEntry {
+        private final ZipFile zipFile;
+        private final ZipEntry entry;
+        ZipFileModuleInfoEntry(ZipFile zipFile, ZipEntry entry) {
+            this.zipFile = zipFile;
+            this.entry = entry;
+        }
+        @Override public String name() { return entry.getName(); }
+        @Override public InputStream bytes() throws IOException {
+            return zipFile.getInputStream(entry);
+        }
+        /** Returns an optional containing the effective URI. */
+        @Override public Optional<String> uriString() {
+            String uri = (Paths.get(zipFile.getName())).toUri().toString();
+            uri = "jar:" + uri + "/!" + entry.getName();
+            return Optional.of(uri);
+        }
+    }
+
+    static class StreamedModuleInfoEntry implements ModuleInfoEntry {
+        private final String name;
+        private final byte[] bytes;
+        StreamedModuleInfoEntry(String name, byte[] bytes) {
+            this.name = name;
+            this.bytes = bytes;
+        }
+        @Override public String name() { return name; }
+        @Override public InputStream bytes() throws IOException {
+            return new ByteArrayInputStream(bytes);
+        }
+        /** Returns an empty optional. */
+        @Override public Optional<String> uriString() {
+            return Optional.empty();  // no URI can be derived
+        }
+    }
+
+    /** Describes a module from a given zip file. */
     private boolean describeModule(ZipFile zipFile) throws IOException {
-        ZipEntry[] zes = zipFile.stream()
-            .filter(e -> isModuleInfoEntry(e.getName()))
-            .sorted(Validator.ENTRY_COMPARATOR)
-            .toArray(ZipEntry[]::new);
+        ZipFileModuleInfoEntry[] infos = zipFile.stream()
+                .filter(e -> isModuleInfoEntry(e.getName()))
+                .sorted(Validator.ENTRY_COMPARATOR)
+                .map(e -> new ZipFileModuleInfoEntry(zipFile, e))
+                .toArray(ZipFileModuleInfoEntry[]::new);
 
-        if (zes.length == 0) {
-            // No module descriptor found, derive the automatic module name
+        if (infos.length == 0) {
+            // No module descriptor found, derive and describe the automatic module
             String fn = zipFile.getName();
             ModuleFinder mf = ModuleFinder.of(Paths.get(fn));
             try {
@@ -1747,8 +1799,8 @@
                     return true;
                 }
                 ModuleDescriptor md = mref.iterator().next().descriptor();
-                output(getMsg("out.automodule"));
-                describeModule(md, null, null, "automatic");
+                output(getMsg("out.automodule") + "\n");
+                describeModule(md, null, null, "");
             } catch (FindException e) {
                 String msg = formatMsg("error.unable.derive.automodule", fn);
                 Throwable t = e.getCause();
@@ -1757,46 +1809,117 @@
                 output(msg);
             }
         } else {
-            for (ZipEntry ze : zes) {
-                try (InputStream is = zipFile.getInputStream(ze)) {
-                    describeModule(is, ze.getName());
-                }
-            }
+            return describeModuleFromEntries(infos);
         }
         return true;
     }
 
-    private boolean describeModule(FileInputStream fis)
+    private boolean describeModuleFromStream(FileInputStream fis)
         throws IOException
     {
+        List<ModuleInfoEntry> infos = new LinkedList<>();
+
         try (BufferedInputStream bis = new BufferedInputStream(fis);
              ZipInputStream zis = new ZipInputStream(bis)) {
             ZipEntry e;
             while ((e = zis.getNextEntry()) != null) {
                 String ename = e.getName();
-                if (isModuleInfoEntry(ename)){
-                    moduleInfos.put(ename, zis.readAllBytes());
+                if (isModuleInfoEntry(ename)) {
+                    infos.add(new StreamedModuleInfoEntry(ename, zis.readAllBytes()));
                 }
             }
         }
-        if (moduleInfos.size() == 0)
+
+        if (infos.size() == 0)
             return false;
-        String[] names = moduleInfos.keySet().stream()
-            .sorted(Validator.ENTRYNAME_COMPARATOR)
-            .toArray(String[]::new);
-        for (String name : names) {
-            describeModule(new ByteArrayInputStream(moduleInfos.get(name)), name);
+
+        ModuleInfoEntry[] sorted = infos.stream()
+                .sorted(Comparator.comparing(ModuleInfoEntry::name, ENTRYNAME_COMPARATOR))
+                .toArray(ModuleInfoEntry[]::new);
+
+        return describeModuleFromEntries(sorted);
+    }
+
+    private boolean lessThanEqualReleaseValue(ModuleInfoEntry entry) {
+        return intVersionFromEntry(entry) <= releaseValue ? true : false;
+    }
+
+    private static String versionFromEntryName(String name) {
+        String s = name.substring(VERSIONS_DIR_LENGTH);
+        return s.substring(0, s.indexOf("/"));
+    }
+
+    private static int intVersionFromEntry(ModuleInfoEntry entry) {
+        String name = entry.name();
+        if (!name.startsWith(VERSIONS_DIR))
+            return BASE_VERSION;
+
+        String s = name.substring(VERSIONS_DIR_LENGTH);
+        s = s.substring(0, s.indexOf('/'));
+        return Integer.valueOf(s);
+    }
+
+    /**
+     * Describes a single module descriptor, determined by the specified
+     * --release, if any, from the given ordered entries.
+     * The given infos must be ordered as per ENTRY_COMPARATOR.
+     */
+    private boolean describeModuleFromEntries(ModuleInfoEntry[] infos)
+        throws IOException
+    {
+        assert infos.length > 0;
+
+        // Informative: output all non-root descriptors, if any
+        String releases = Arrays.stream(infos)
+                .filter(e -> !e.name().equals(MODULE_INFO))
+                .map(ModuleInfoEntry::name)
+                .map(Main::versionFromEntryName)
+                .collect(joining(" "));
+        if (!releases.equals(""))
+            output("releases: " + releases + "\n");
+
+        // Describe the operative descriptor for the specified --release, if any
+        if (releaseValue != -1) {
+            ModuleInfoEntry entry = null;
+            int i = 0;
+            while (i < infos.length && lessThanEqualReleaseValue(infos[i])) {
+                entry = infos[i];
+                i++;
+            }
+
+            if (entry == null) {
+                output(formatMsg("error.no.operative.descriptor",
+                                 String.valueOf(releaseValue)));
+                return false;
+            }
+
+            String uriString = entry.uriString().orElse("");
+            try (InputStream is = entry.bytes()) {
+                describeModule(is, uriString);
+            }
+        } else {
+            // no specific --release specified, output the root, if any
+            if (infos[0].name().equals(MODULE_INFO)) {
+                String uriString = infos[0].uriString().orElse("");
+                try (InputStream is = infos[0].bytes()) {
+                    describeModule(is, uriString);
+                }
+            } else {
+                // no root, output message to specify --release
+                output(getMsg("error.no.root.descriptor"));
+            }
         }
         return true;
     }
 
     static <T> String toString(Collection<T> set) {
         if (set.isEmpty()) { return ""; }
-        return set.stream().map(e -> e.toString().toLowerCase(Locale.ROOT))
-                  .collect(joining(" "));
+        return " " + set.stream().map(e -> e.toString().toLowerCase(Locale.ROOT))
+                  .sorted().collect(joining(" "));
     }
 
-    private void describeModule(InputStream entryInputStream, String ename)
+
+    private void describeModule(InputStream entryInputStream, String uriString)
         throws IOException
     {
         ModuleInfo.Attributes attrs = ModuleInfo.read(entryInputStream, null);
@@ -1804,71 +1927,94 @@
         ModuleTarget target = attrs.target();
         ModuleHashes hashes = attrs.recordedHashes();
 
-        describeModule(md, target, hashes, ename);
+        describeModule(md, target, hashes, uriString);
     }
 
     private void describeModule(ModuleDescriptor md,
                                 ModuleTarget target,
                                 ModuleHashes hashes,
-                                String ename)
+                                String uriString)
         throws IOException
     {
         StringBuilder sb = new StringBuilder();
-        sb.append("\nmodule ")
-          .append(md.toNameAndVersion())
-          .append(" (").append(ename).append(")");
+
+        sb.append(md.toNameAndVersion());
+
+        if (!uriString.equals(""))
+            sb.append(" ").append(uriString);
+        if (md.isOpen())
+            sb.append(" open");
+        if (md.isAutomatic())
+            sb.append(" automatic");
+        sb.append("\n");
 
-        if (md.isOpen())
-            sb.append("\n  open ");
+        // unqualified exports (sorted by package)
+        md.exports().stream()
+                .sorted(Comparator.comparing(Exports::source))
+                .filter(e -> !e.isQualified())
+                .forEach(e -> sb.append("exports ").append(e.source())
+                                .append(toString(e.modifiers())).append("\n"));
+
+        // dependences
+        md.requires().stream().sorted()
+                .forEach(r -> sb.append("requires ").append(r.name())
+                                .append(toString(r.modifiers())).append("\n"));
 
-        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());
-            });
+        // service use and provides
+        md.uses().stream().sorted()
+                .forEach(s -> sb.append("uses ").append(s).append("\n"));
+
+        md.provides().stream()
+                .sorted(Comparator.comparing(Provides::service))
+                .forEach(p -> sb.append("provides ").append(p.service())
+                                .append(" with")
+                                .append(toString(p.providers()))
+                                .append("\n"));
 
-        md.uses().stream().sorted()
-            .forEach(p -> sb.append("\n  uses ").append(p));
+        // qualified exports
+        md.exports().stream()
+                .sorted(Comparator.comparing(Exports::source))
+                .filter(Exports::isQualified)
+                .forEach(e -> sb.append("qualified exports ").append(e.source())
+                                .append(" to").append(toString(e.targets()))
+                                .append("\n"));
 
-        md.exports().stream()
-            .sorted(Comparator.comparing(Exports::source))
-            .forEach(p -> sb.append("\n  exports ").append(p));
+        // open packages
+        md.opens().stream()
+                .sorted(Comparator.comparing(Opens::source))
+                .filter(o -> !o.isQualified())
+                .forEach(o -> sb.append("opens ").append(o.source())
+                                 .append(toString(o.modifiers()))
+                                 .append("\n"));
 
         md.opens().stream()
-            .sorted(Comparator.comparing(Opens::source))
-            .forEach(p -> sb.append("\n  opens ").append(p));
+                .sorted(Comparator.comparing(Opens::source))
+                .filter(Opens::isQualified)
+                .forEach(o -> sb.append("qualified opens ").append(o.source())
+                                 .append(toString(o.modifiers()))
+                                 .append(" to").append(toString(o.targets()))
+                                 .append("\n"));
 
-        Set<String> concealed = new HashSet<>(md.packages());
+        // non-exported/non-open packages
+        Set<String> concealed = new TreeSet<>(md.packages());
         md.exports().stream().map(Exports::source).forEach(concealed::remove);
         md.opens().stream().map(Opens::source).forEach(concealed::remove);
-        concealed.stream().sorted()
-            .forEach(p -> sb.append("\n  contains ").append(p));
+        concealed.forEach(p -> sb.append("contains ").append(p).append("\n"));
 
-        md.provides().stream()
-            .sorted(Comparator.comparing(Provides::service))
-            .forEach(p -> sb.append("\n  provides ").append(p.service())
-                            .append(" with ")
-                            .append(toString(p.providers())));
-
-        md.mainClass().ifPresent(v -> sb.append("\n  main-class " + v));
+        md.mainClass().ifPresent(v -> sb.append("main-class ").append(v).append("\n"));
 
         if (target != null) {
-            String osName = target.osName();
-            if (osName != null)
-                sb.append("\n  operating-system-name " + osName);
-            String osArch = target.osArch();
-            if (osArch != null)
-                sb.append("\n  operating-system-architecture " + osArch);
+            String targetPlatform = target.targetPlatform();
+            if (!targetPlatform.isEmpty())
+                sb.append("platform ").append(targetPlatform).append("\n");
        }
 
        if (hashes != null) {
            hashes.names().stream().sorted().forEach(
-                   mod -> sb.append("\n  hashes ").append(mod).append(" ")
+                   mod -> sb.append("hashes ").append(mod).append(" ")
                             .append(hashes.algorithm()).append(" ")
-                            .append(toHex(hashes.hashFor(mod))));
+                            .append(toHex(hashes.hashFor(mod)))
+                            .append("\n"));
         }
 
         output(sb.toString());
--- a/jdk/src/jdk.jartool/share/classes/sun/tools/jar/Validator.java	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/src/jdk.jartool/share/classes/sun/tools/jar/Validator.java	Thu May 04 07:26:55 2017 +0000
@@ -116,7 +116,7 @@
             // version number strings need to be sorted numerically
             n = VERSIONS_DIR.length();   // skip the common prefix
             int i1 = s1.indexOf('/', n);
-            int i2 = s1.indexOf('/', n);
+            int i2 = s2.indexOf('/', n);
             if (i1 == -1) throw new InvalidJarException(s1);
             if (i2 == -1) throw new InvalidJarException(s2);
             // shorter version numbers go first
--- a/jdk/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar.properties	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/src/jdk.jartool/share/classes/sun/tools/jar/resources/jar.properties	Thu May 04 07:26:55 2017 +0000
@@ -45,7 +45,7 @@
 	'e' flag and manifest with the 'Main-Class' attribute cannot be specified \n\
 	 together!
 error.bad.dflag=\
-        '-d, --describe-module' option requires no input file(s) to be specified: {0}
+        '-d, --describe-module' option requires no input file(s) to be specified
 error.bad.reason=\
         bad reason: {0}, must be one of deprecated, deprecated-for-removal, or incubating
 error.nosuch.fileordir=\
@@ -62,6 +62,10 @@
         Hashing module {0} dependences, unable to find module {1} on module path
 error.module.options.without.info=\
         One of --module-version or --hash-modules without module-info.class
+error.no.operative.descriptor=\
+        No operative descriptor for release: {0}
+error.no.root.descriptor=\
+        No root module descriptor, specify --release
 error.unable.derive.automodule=\
         Unable to derive module descriptor for: {0}
 error.unexpected.module-info=\
--- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/builder/DefaultImageBuilder.java	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/builder/DefaultImageBuilder.java	Thu May 04 07:26:55 2017 +0000
@@ -152,14 +152,14 @@
     @Override
     public void storeFiles(ResourcePool files) {
         try {
-            String targetOsName = files.moduleView()
-                                       .findModule("java.base")
-                                       .map(ResourcePoolModule::osName)
-                                       .orElse(null);
-            if (targetOsName == null) {
+            String value = files.moduleView()
+                                .findModule("java.base")
+                                .map(ResourcePoolModule::targetPlatform)
+                                .orElse(null);
+            if (value == null) {
                 throw new PluginException("ModuleTarget attribute is missing for java.base module");
             }
-            this.targetPlatform = Platform.toPlatform(targetOsName);
+            this.targetPlatform = Platform.toPlatform(value);
 
             checkResourcePool(files);
 
--- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImagePluginStack.java	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ImagePluginStack.java	Thu May 04 07:26:55 2017 +0000
@@ -324,15 +324,9 @@
             }
 
             @Override
-            public String osName() {
+            public String targetPlatform() {
                 initModuleAttributes();
-                return target != null? target.osName() : null;
-            }
-
-            @Override
-            public String osArch() {
-                initModuleAttributes();
-                return target != null? target.osArch() : null;
+                return target != null? target.targetPlatform() : null;
             }
 
             private void initModuleAttributes() {
--- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/Platform.java	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/Platform.java	Thu May 04 07:26:55 2017 +0000
@@ -40,12 +40,17 @@
     UNKNOWN;
 
     /**
-     * Returns the {@code Platform} of the given OS name specified
+     * Returns the {@code Platform} derived from the target platform
      * in the {@code ModuleTarget} attribute.
-     *
-     * @param osName OS name in ModuleTarget attribute
      */
-    public static Platform toPlatform(String osName) {
+    public static Platform toPlatform(String targetPlatform) {
+        String osName;
+        int index = targetPlatform.indexOf("-");
+        if (index < 0) {
+            osName = targetPlatform;
+        } else {
+            osName = targetPlatform.substring(0, index);
+        }
         try {
             return Platform.valueOf(osName.toUpperCase(Locale.ENGLISH));
         } catch (IllegalArgumentException e) {
@@ -57,9 +62,9 @@
      * Returns the {@code Platform} to which the given module is target to.
      */
     public static Platform getTargetPlatform(ResourcePoolModule module) {
-        String osName = module.osName();
-        if (osName != null) {
-            return toPlatform(osName);
+        String targetPlatform = module.targetPlatform();
+        if (targetPlatform != null) {
+            return toPlatform(targetPlatform);
         } else {
             return Platform.UNKNOWN;
         }
--- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ResourcePoolManager.java	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/ResourcePoolManager.java	Thu May 04 07:26:55 2017 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -110,15 +110,9 @@
         }
 
         @Override
-        public String osName() {
+        public String targetPlatform() {
             initModuleAttributes();
-            return target != null? target.osName() : null;
-        }
-
-        @Override
-        public String osArch() {
-            initModuleAttributes();
-            return target != null? target.osArch() : null;
+            return target != null? target.targetPlatform() : null;
         }
 
         private void initModuleAttributes() {
--- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/ReleaseInfoPlugin.java	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/ReleaseInfoPlugin.java	Thu May 04 07:26:55 2017 +0000
@@ -33,13 +33,13 @@
 import java.util.EnumSet;
 import java.util.HashMap;
 import java.util.Map;
-import java.util.Optional;
 import java.util.Properties;
 import java.util.Set;
 import java.util.function.Function;
 import java.util.stream.Collectors;
 import jdk.tools.jlink.internal.ModuleSorter;
 import jdk.tools.jlink.internal.Utils;
+import jdk.tools.jlink.plugin.PluginException;
 import jdk.tools.jlink.plugin.ResourcePool;
 import jdk.tools.jlink.plugin.ResourcePoolBuilder;
 import jdk.tools.jlink.plugin.ResourcePoolEntry;
@@ -132,18 +132,16 @@
     public ResourcePool transform(ResourcePool in, ResourcePoolBuilder out) {
         in.transformAndCopy(Function.identity(), out);
 
-        Optional<ResourcePoolModule> javaBase = in.moduleView().findModule("java.base");
-        javaBase.ifPresent(mod -> {
-            // fill release information available from transformed "java.base" module!
-            ModuleDescriptor desc = mod.descriptor();
-            desc.version().ifPresent(s -> release.put("JAVA_VERSION",
-                    quote(parseVersion(s.toString()))));
-            desc.version().ifPresent(s -> release.put("JAVA_FULL_VERSION",
-                    quote(s.toString())));
+        ResourcePoolModule javaBase = in.moduleView().findModule("java.base")
+                                                     .orElse(null);
+        if (javaBase == null || javaBase.targetPlatform() == null) {
+            throw new PluginException("ModuleTarget attribute is missing for java.base module");
+        }
 
-            release.put("OS_NAME", quote(mod.osName()));
-            release.put("OS_ARCH", quote(mod.osArch()));
-        });
+        // fill release information available from transformed "java.base" module!
+        ModuleDescriptor desc = javaBase.descriptor();
+        desc.version().ifPresent(v -> release.put("JAVA_VERSION",
+                                                  quote(parseVersion(v))));
 
         // put topological sorted module names separated by space
         release.put("MODULES",  new ModuleSorter(in.moduleView())
@@ -152,14 +150,15 @@
 
         // create a TOP level ResourcePoolEntry for "release" file.
         out.add(ResourcePoolEntry.create("/java.base/release",
-            ResourcePoolEntry.Type.TOP, releaseFileContent()));
+                                         ResourcePoolEntry.Type.TOP,
+                                         releaseFileContent()));
         return out.build();
     }
 
     // Parse version string and return a string that includes only version part
     // leaving "pre", "build" information. See also: java.lang.Runtime.Version.
-    private static String parseVersion(String str) {
-        return Runtime.Version.parse(str)
+    private static String parseVersion(ModuleDescriptor.Version v) {
+        return Runtime.Version.parse(v.toString())
                       .version()
                       .stream()
                       .map(Object::toString)
--- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/SystemModulesPlugin.java	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/SystemModulesPlugin.java	Thu May 04 07:26:55 2017 +0000
@@ -211,8 +211,7 @@
             // drop target attribute only if any OS property is present
             ModuleTarget target = attrs.target();
             if (dropModuleTarget && target != null) {
-                this.dropModuleTarget = (target.osName() != null)
-                                        || (target.osArch() != null);
+                this.dropModuleTarget = (target.targetPlatform() != null);
             } else {
                 this.dropModuleTarget = false;
             }
@@ -377,7 +376,7 @@
             }
 
             void dropModuleTarget() {
-                extender.targetPlatform("", "");
+                extender.targetPlatform("");
             }
 
             byte[] getBytes() throws IOException {
@@ -527,8 +526,7 @@
                 ModuleDescriptor md = moduleInfo.descriptor;
                 // drop ModuleTarget attribute if java.base has all OS properties
                 ModuleTarget target = moduleInfo.target();
-                if (dropModuleTarget
-                    && (target.osName() != null) && (target.osArch() != null)) {
+                if (dropModuleTarget && target.targetPlatform() != null) {
                     dropModuleTarget = true;
                 } else {
                     dropModuleTarget = false;
@@ -543,7 +541,7 @@
             moduleInfo.validatePackages();
 
             // module-info.class may be overridden for optimization
-            // 1. update ModuleTarget attribute to drop osName, osArch, osVersion
+            // 1. update ModuleTarget attribute to drop targetPlartform
             // 2. add/update ModulePackages attribute
             if (moduleInfo.shouldRewrite()) {
                 entry = entry.copyWithContent(moduleInfo.getBytes());
@@ -655,10 +653,9 @@
                     // new ModuleTarget(String, String)
                     mv.visitTypeInsn(NEW, MODULE_TARGET_CLASSNAME);
                     mv.visitInsn(DUP);
-                    mv.visitLdcInsn(minfo.target().osName());
-                    mv.visitLdcInsn(minfo.target().osArch());
+                    mv.visitLdcInsn(minfo.target().targetPlatform());
                     mv.visitMethodInsn(INVOKESPECIAL, MODULE_TARGET_CLASSNAME,
-                        "<init>", "(Ljava/lang/String;Ljava/lang/String;)V", false);
+                        "<init>", "(Ljava/lang/String;)V", false);
 
                     mv.visitInsn(AASTORE);
                 }
--- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/plugin/ResourcePoolModule.java	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/plugin/ResourcePoolModule.java	Thu May 04 07:26:55 2017 +0000
@@ -58,18 +58,11 @@
     public ModuleDescriptor descriptor();
 
     /**
-     * The module target OS name for this module.
+     * The target platform
      *
-     * @return The module target OS name
+     * @return The target platform
      */
-    public String osName();
-
-    /**
-     * The module target OS arch for this module.
-     *
-     * @return The module target OS arch
-     */
-    public String osArch();
+    public String targetPlatform();
 
     /**
      * Retrieves all the packages located in this module.
--- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jmod/JmodTask.java	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jmod/JmodTask.java	Thu May 04 07:26:55 2017 +0000
@@ -177,8 +177,7 @@
         ModuleFinder moduleFinder;
         Version moduleVersion;
         String mainClass;
-        String osName;
-        String osArch;
+        String targetPlatform;
         Pattern modulesToHash;
         ModuleResolution moduleResolution;
         boolean dryrun;
@@ -311,9 +310,9 @@
         try (JmodFile jf = new JmodFile(options.jmodFile)) {
             try (InputStream in = jf.getInputStream(Section.CLASSES, MODULE_INFO)) {
                 ModuleInfo.Attributes attrs = ModuleInfo.read(in, null);
-                printModuleDescriptor(attrs.descriptor(),
-                                      attrs.target(),
-                                      attrs.recordedHashes());
+                describeModule(attrs.descriptor(),
+                               attrs.target(),
+                               attrs.recordedHashes());
                 return true;
             } catch (IOException e) {
                 throw new CommandException("err.module.descriptor.not.found");
@@ -323,66 +322,92 @@
 
     static <T> String toString(Collection<T> c) {
         if (c.isEmpty()) { return ""; }
-        return c.stream().map(e -> e.toString().toLowerCase(Locale.ROOT))
-                  .collect(joining(" "));
+        return " " + c.stream().map(e -> e.toString().toLowerCase(Locale.ROOT))
+                .sorted().collect(joining(" "));
     }
 
-    private void printModuleDescriptor(ModuleDescriptor md,
-                                       ModuleTarget target,
-                                       ModuleHashes hashes)
+    private void describeModule(ModuleDescriptor md,
+                                ModuleTarget target,
+                                ModuleHashes hashes)
         throws IOException
     {
         StringBuilder sb = new StringBuilder();
-        sb.append("\n").append(md.toNameAndVersion());
+
+        sb.append(md.toNameAndVersion());
+
+        if (md.isOpen())
+            sb.append(" open");
+        if (md.isAutomatic())
+            sb.append(" automatic");
+        sb.append("\n");
+
+        // unqualified exports (sorted by package)
+        md.exports().stream()
+                .sorted(Comparator.comparing(Exports::source))
+                .filter(e -> !e.isQualified())
+                .forEach(e -> sb.append("exports ").append(e.source())
+                                .append(toString(e.modifiers())).append("\n"));
+
+        // dependences
+        md.requires().stream().sorted()
+                .forEach(r -> sb.append("requires ").append(r.name())
+                                .append(toString(r.modifiers())).append("\n"));
 
-        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());
-            });
+        // service use and provides
+        md.uses().stream().sorted()
+                .forEach(s -> sb.append("uses ").append(s).append("\n"));
+
+        md.provides().stream()
+                .sorted(Comparator.comparing(Provides::service))
+                .forEach(p -> sb.append("provides ").append(p.service())
+                                .append(" with")
+                                .append(toString(p.providers()))
+                                .append("\n"));
 
-        md.uses().stream().sorted()
-            .forEach(s -> sb.append("\n  uses ").append(s));
+        // qualified exports
+        md.exports().stream()
+                .sorted(Comparator.comparing(Exports::source))
+                .filter(Exports::isQualified)
+                .forEach(e -> sb.append("qualified exports ").append(e.source())
+                                .append(" to").append(toString(e.targets()))
+                                .append("\n"));
 
-        md.exports().stream()
-            .sorted(Comparator.comparing(Exports::source))
-            .forEach(p -> sb.append("\n  exports ").append(p));
+        // open packages
+        md.opens().stream()
+                .sorted(Comparator.comparing(Opens::source))
+                .filter(o -> !o.isQualified())
+                .forEach(o -> sb.append("opens ").append(o.source())
+                                 .append(toString(o.modifiers()))
+                                 .append("\n"));
 
         md.opens().stream()
-            .sorted(Comparator.comparing(Opens::source))
-            .forEach(p -> sb.append("\n  opens ").append(p));
+                .sorted(Comparator.comparing(Opens::source))
+                .filter(Opens::isQualified)
+                .forEach(o -> sb.append("qualified opens ").append(o.source())
+                                 .append(toString(o.modifiers()))
+                                 .append(" to").append(toString(o.targets()))
+                                 .append("\n"));
 
-        Set<String> concealed = new HashSet<>(md.packages());
+        // non-exported/non-open packages
+        Set<String> concealed = new TreeSet<>(md.packages());
         md.exports().stream().map(Exports::source).forEach(concealed::remove);
         md.opens().stream().map(Opens::source).forEach(concealed::remove);
-        concealed.stream().sorted()
-                 .forEach(p -> sb.append("\n  contains ").append(p));
+        concealed.forEach(p -> sb.append("contains ").append(p).append("\n"));
 
-        md.provides().stream()
-            .sorted(Comparator.comparing(Provides::service))
-            .forEach(p -> sb.append("\n  provides ").append(p.service())
-                            .append(" with ")
-                            .append(toString(p.providers())));
-
-        md.mainClass().ifPresent(v -> sb.append("\n  main-class " + v));
+        md.mainClass().ifPresent(v -> sb.append("main-class ").append(v).append("\n"));
 
         if (target != null) {
-            String osName = target.osName();
-            if (osName != null)
-                sb.append("\n  operating-system-name " + osName);
-            String osArch = target.osArch();
-            if (osArch != null)
-                sb.append("\n  operating-system-architecture " + osArch);
-        }
+            String targetPlatform = target.targetPlatform();
+            if (!targetPlatform.isEmpty())
+                sb.append("platform ").append(targetPlatform).append("\n");
+       }
 
-        if (hashes != null) {
-            hashes.names().stream().sorted().forEach(
-                    mod -> sb.append("\n  hashes ").append(mod).append(" ")
-                             .append(hashes.algorithm()).append(" ")
-                             .append(toHex(hashes.hashFor(mod))));
+       if (hashes != null) {
+           hashes.names().stream().sorted().forEach(
+                   mod -> sb.append("hashes ").append(mod).append(" ")
+                            .append(hashes.algorithm()).append(" ")
+                            .append(toHex(hashes.hashFor(mod)))
+                            .append("\n"));
         }
 
         out.println(sb.toString());
@@ -437,8 +462,7 @@
 
         final Version moduleVersion = options.moduleVersion;
         final String mainClass = options.mainClass;
-        final String osName = options.osName;
-        final String osArch = options.osArch;
+        final String targetPlatform = options.targetPlatform;
         final List<PathMatcher> excludes = options.excludes;
         final ModuleResolution moduleResolution = options.moduleResolution;
 
@@ -534,9 +558,10 @@
                 if (mainClass != null)
                     extender.mainClass(mainClass);
 
-                // --os-name, --os-arch
-                if (osName != null || osArch != null)
-                    extender.targetPlatform(osName, osArch);
+                // --target-platform
+                if (targetPlatform != null) {
+                    extender.targetPlatform(targetPlatform);
+                }
 
                 // --module-version
                 if (moduleVersion != null)
@@ -1327,15 +1352,10 @@
                         .withRequiredArg()
                         .withValuesConvertedBy(new ModuleVersionConverter());
 
-        OptionSpec<String> osName
-                = parser.accepts("os-name", getMessage("main.opt.os-name"))
+        OptionSpec<String> targetPlatform
+                = parser.accepts("target-platform", getMessage("main.opt.target-platform"))
                         .withRequiredArg()
-                        .describedAs(getMessage("main.opt.os-name.arg"));
-
-        OptionSpec<String> osArch
-                = parser.accepts("os-arch", getMessage("main.opt.os-arch"))
-                        .withRequiredArg()
-                        .describedAs(getMessage("main.opt.os-arch.arg"));
+                        .describedAs(getMessage("main.opt.target-platform.arg"));
 
         OptionSpec<Void> doNotResolveByDefault
                 = parser.accepts("do-not-resolve-by-default",
@@ -1400,10 +1420,8 @@
                 options.moduleVersion = getLastElement(opts.valuesOf(moduleVersion));
             if (opts.has(mainClass))
                 options.mainClass = getLastElement(opts.valuesOf(mainClass));
-            if (opts.has(osName))
-                options.osName = getLastElement(opts.valuesOf(osName));
-            if (opts.has(osArch))
-                options.osArch = getLastElement(opts.valuesOf(osArch));
+            if (opts.has(targetPlatform))
+                options.targetPlatform = getLastElement(opts.valuesOf(targetPlatform));
             if (opts.has(warnIfResolved))
                 options.moduleResolution = getLastElement(opts.valuesOf(warnIfResolved));
             if (opts.has(doNotResolveByDefault)) {
--- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jmod/resources/jmod.properties	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jmod/resources/jmod.properties	Thu May 04 07:26:55 2017 +0000
@@ -64,10 +64,8 @@
 main.opt.main-class=Main class
 main.opt.main-class.arg=class-name
 main.opt.man-pages=Location of man pages
-main.opt.os-name=Operating system name
-main.opt.os-name.arg=os-name
-main.opt.os-arch=Operating system architecture
-main.opt.os-arch.arg=os-arch
+main.opt.target-platform=Target platform
+main.opt.target-platform.arg=target-platform
 main.opt.module-path=Module path
 main.opt.hash-modules=Compute and record hashes to tie a packaged module\
 \ with modules matching the given <regex-pattern> and depending upon it directly\
--- a/jdk/test/com/sun/management/HotSpotDiagnosticMXBean/CheckOrigin.java	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/test/com/sun/management/HotSpotDiagnosticMXBean/CheckOrigin.java	Thu May 04 07:26:55 2017 +0000
@@ -66,6 +66,7 @@
                     "-XX:+UseCodeAging",
                     "-XX:+UseCerealGC",         // Should be ignored.
                     "-XX:Flags=" + flagsFile.getAbsolutePath(),
+                    "-Djdk.attach.allowAttachSelf",
                     "-cp", System.getProperty("test.class.path"),
                     "CheckOrigin",
                     "-runtests");
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/com/sun/tools/attach/AttachSelf.java	Thu May 04 07:26:55 2017 +0000
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ */
+
+import com.sun.tools.attach.VirtualMachine;
+
+import java.io.IOException;
+
+/**
+ * @test
+ * @modules jdk.attach
+ * @run main AttachSelf
+ * @run main/othervm -Djdk.attach.allowAttachSelf AttachSelf
+ * @run main/othervm -Djdk.attach.allowAttachSelf=true AttachSelf
+ * @run main/othervm -Djdk.attach.allowAttachSelf=false AttachSelf
+ */
+
+public class AttachSelf {
+
+    public static void main(String[] args) throws Exception {
+
+        String value = System.getProperty("jdk.attach.allowAttachSelf");
+        boolean canAttachSelf = (value != null) && !value.equals("false");
+
+        String vmid = "" + ProcessHandle.current().pid();
+
+        VirtualMachine vm = null;
+        try {
+            vm = VirtualMachine.attach(vmid);
+            if (!canAttachSelf)
+                throw new RuntimeException("Attached to self not expected");
+        } catch (IOException ioe) {
+            if (canAttachSelf)
+                throw ioe;
+        } finally {
+            if (vm != null) vm.detach();
+        }
+
+    }
+
+}
+
--- a/jdk/test/com/sun/tools/attach/RunnerUtil.java	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/test/com/sun/tools/attach/RunnerUtil.java	Thu May 04 07:26:55 2017 +0000
@@ -48,7 +48,10 @@
      */
     public static ProcessThread startApplication(String... additionalOpts) throws Throwable {
         String classpath = System.getProperty("test.class.path", ".");
-        String[] myArgs = concat(additionalOpts, new String [] { "-XX:+UsePerfData", "-Dattach.test=true", "-classpath", classpath, "Application" });
+        String[] myArgs = concat(additionalOpts, new String [] {
+            "-XX:+UsePerfData", "-XX:+EnableDynamicAgentLoading",
+            "-Dattach.test=true", "-classpath", classpath, "Application"
+        });
         String[] args = Utils.addTestJavaOpts(myArgs);
         ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(args);
         ProcessThread pt = new ProcessThread("runApplication", (line) -> line.equals(Application.READY_MSG), pb);
--- a/jdk/test/com/sun/tools/attach/java.policy.allow	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/test/com/sun/tools/attach/java.policy.allow	Thu May 04 07:26:55 2017 +0000
@@ -8,7 +8,8 @@
     permission com.sun.tools.attach.AttachPermission "createAttachProvider";
 
     /* implementation specific */
-    permission java.lang.RuntimePermission "accessClassInPackage.sun.misc";
+    permission java.lang.RuntimePermission "manageProcess";
+    permission java.lang.RuntimePermission "accessClassInPackage.jdk.internal.misc";
     permission java.lang.RuntimePermission "accessClassInPackage.sun.tools.attach";
     permission java.lang.RuntimePermission "accessClassInPackage.sun.jvmstat.monitor";
     permission java.lang.RuntimePermission "loadLibrary.attach";
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/com/sun/tools/attach/modules/Agent.java	Thu May 04 07:26:55 2017 +0000
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ */
+
+import java.lang.instrument.Instrumentation;
+
+public class Agent {
+
+    public static void agentmain(String agentArgs, Instrumentation inst) {
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/com/sun/tools/attach/modules/Driver.java	Thu May 04 07:26:55 2017 +0000
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * 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
+ * @modules jdk.attach
+ * @build m/* Agent
+ * @run main/othervm -Djdk.attach.allowAttachSelf m/p.Main jmx javaagent
+ * @run main/othervm -Djdk.attach.allowAttachSelf m/p.Main javaagent jmx
+ * @run main/othervm --limit-modules=jdk.attach -Djdk.attach.allowAttachSelf m/p.Main jmx javaagent
+ * @run main/othervm --limit-modules=jdk.attach -Djdk.attach.allowAttachSelf m/p.Main javaagent jmx
+ * @summary Basic test to ensure that a JMX agent or a tool agent can be loaded/started in
+  *         a modular application.
+ */
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/com/sun/tools/attach/modules/m/module-info.java	Thu May 04 07:26:55 2017 +0000
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * 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 m {
+    exports p;
+    requires jdk.attach;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/com/sun/tools/attach/modules/m/p/Main.java	Thu May 04 07:26:55 2017 +0000
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * 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 p;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.jar.Attributes;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Manifest;
+
+import com.sun.tools.attach.VirtualMachine;
+
+public class Main {
+
+    public static void main(String[] args) throws Exception {
+        System.out.println("#modules loaded: " + moduleInfoCont());
+
+        String vmid = "" + ProcessHandle.current().pid();
+        VirtualMachine vm = VirtualMachine.attach(vmid);
+
+        for (String test : args) {
+            switch (test) {
+                case "jmx" :
+                    startJMXAgent(vm);
+                    break;
+                case "javaagent" :
+                    startJavaAgent(vm, createAgentJar());
+                    break;
+            }
+
+            System.out.println("#modules loaded: " + moduleInfoCont());
+        }
+    }
+
+    /**
+     * Locates module-info.class resources to get a count of the module of system
+     * modules.
+     */
+    static long moduleInfoCont() {
+        ClassLoader scl = ClassLoader.getSystemClassLoader();
+        return scl.resources("module-info.class").count();
+    }
+
+    /**
+     * Starts a JMX agent and checks that java.management is loaded.
+     */
+    static void startJMXAgent(VirtualMachine vm) throws Exception {
+        System.out.println("Start JMX agent");
+        vm.startLocalManagementAgent();
+
+        // types in java.management should be visible
+        Class.forName("javax.management.MXBean");
+    }
+
+    /**
+     * Loads a java agent into the VM and checks that java.instrument is loaded.
+     */
+    static void startJavaAgent(VirtualMachine vm, Path agent) throws Exception {
+        System.out.println("Load java agent ...");
+        vm.loadAgent(agent.toString());
+
+        // the Agent class should be visible
+        Class.forName("Agent");
+
+        // types in java.instrument should be visible
+        Class.forName("java.lang.instrument.Instrumentation");
+    }
+
+    /**
+     * Creates a java agent, return the file path to the agent JAR file.
+     */
+    static Path createAgentJar() throws IOException {
+        Manifest man = new Manifest();
+        Attributes attrs = man.getMainAttributes();
+        attrs.put(Attributes.Name.MANIFEST_VERSION, "1.0.0");
+        attrs.put(new Attributes.Name("Agent-Class"), "Agent");
+        Path agent = Paths.get("agent.jar");
+        Path dir = Paths.get(System.getProperty("test.classes"));
+        createJarFile(agent, man, dir, "Agent.class");
+        return agent;
+    }
+
+    /**
+     * Creates a JAR file.
+     *
+     * Equivalent to {@code jar cfm <jarfile> <manifest> -C <dir> file...}
+     *
+     * The input files are resolved against the given directory. Any input
+     * files that are directories are processed recursively.
+     */
+    static void createJarFile(Path jarfile, Manifest man, Path dir, String... files)
+        throws IOException
+    {
+        // create the target directory
+        Path parent = jarfile.getParent();
+        if (parent != null)
+            Files.createDirectories(parent);
+
+        List<Path> entries = new ArrayList<>();
+        for (String file : files) {
+            Files.find(dir.resolve(file), Integer.MAX_VALUE,
+                    (p, attrs) -> attrs.isRegularFile())
+                    .map(e -> dir.relativize(e))
+                    .forEach(entries::add);
+        }
+
+        try (OutputStream out = Files.newOutputStream(jarfile);
+             JarOutputStream jos = new JarOutputStream(out))
+        {
+            if (man != null) {
+                JarEntry je = new JarEntry(JarFile.MANIFEST_NAME);
+                jos.putNextEntry(je);
+                man.write(jos);
+                jos.closeEntry();
+            }
+
+            for (Path entry : entries) {
+                String name = toJarEntryName(entry);
+                jos.putNextEntry(new JarEntry(name));
+                Files.copy(dir.resolve(entry), jos);
+                jos.closeEntry();
+            }
+        }
+    }
+
+    /**
+     * Map a file path to the equivalent name in a JAR file
+     */
+    static String toJarEntryName(Path file) {
+        Path normalized = file.normalize();
+        return normalized.subpath(0, normalized.getNameCount())
+                .toString()
+                .replace(File.separatorChar, '/');
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/instrument/executableJAR/Agent.java	Thu May 04 07:26:55 2017 +0000
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ */
+
+import java.lang.instrument.Instrumentation;
+
+public class Agent {
+
+    public static Instrumentation inst;
+
+    public static void agentmain(String agentArgs, Instrumentation inst) {
+        Agent.inst = inst;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/instrument/executableJAR/AgentHelper.java	Thu May 04 07:26:55 2017 +0000
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ */
+
+public class AgentHelper {
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/instrument/executableJAR/ExecJarWithAgent.java	Thu May 04 07:26:55 2017 +0000
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * 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
+ * @library /lib/testlibrary
+ * @build ExecJarWithAgent Main Agent AgentHelper JarUtils jdk.testlibrary.*
+ * @run testng ExecJarWithAgent
+ * @summary Test starting agents in executable JAR files
+ */
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.jar.Attributes;
+import java.util.jar.Manifest;
+import java.util.stream.Stream;
+
+import org.testng.annotations.Test;
+import static org.testng.Assert.*;
+
+import jdk.testlibrary.ProcessTools;
+import jdk.testlibrary.OutputAnalyzer;
+
+@Test
+public class ExecJarWithAgent {
+
+    /**
+     * Basic test of java -jar with agent in the executable JAR
+     */
+    public void testBasic() throws Exception {
+        Manifest man = new Manifest();
+        Attributes attrs = man.getMainAttributes();
+        attrs.put(Attributes.Name.MANIFEST_VERSION, "1.0.0");
+        attrs.put(Attributes.Name.MAIN_CLASS, "Main");
+        attrs.put(new Attributes.Name("Launcher-Agent-Class"), "Agent");
+
+        // require all capabilities
+        attrs.put(new Attributes.Name("Can-Redefine-Classes"), "true");
+        attrs.put(new Attributes.Name("Can-Retransform-Classes"), "true");
+        attrs.put(new Attributes.Name("Can-Set-Native-Method-Prefix"), "true");
+        attrs.put(new Attributes.Name("Boot-Class-Path"), "helper.jar");
+
+        Path app = Paths.get("app.jar");
+        Path dir = Paths.get(System.getProperty("test.classes"));
+
+        Path[] paths = Stream.of("Main.class", "Agent.class")
+                .map(Paths::get)
+                .toArray(Path[]::new);
+
+        JarUtils.createJarFile(app, man, dir, paths);
+
+        // helper API to test that the BCP has been extended
+        Path helper = Paths.get("helper.jar");
+        JarUtils.createJarFile(helper, dir, "AgentHelper.class");
+
+        // java -jar app.jar
+        assertEquals(exec(app).getExitValue(), 0);
+    }
+
+    /**
+     * Test that java -jar fails when the executable JAR has the
+     * Launcher-Agent-Class attribute but the class cannot be loaded.
+     */
+    public void testBadAgentClass() throws Exception {
+        Manifest man = new Manifest();
+        Attributes attrs = man.getMainAttributes();
+        attrs.put(Attributes.Name.MANIFEST_VERSION, "1.0.0");
+        attrs.put(Attributes.Name.MAIN_CLASS, "Main");
+
+        // agent class does not exist
+        attrs.put(new Attributes.Name("Launcher-Agent-Class"), "BadAgent");
+
+        Path app = Paths.get("app.jar");
+        Path dir = Paths.get(System.getProperty("test.classes"));
+
+        JarUtils.createJarFile(app, man, dir, Paths.get("Main.class"));
+
+        // java -jar app.jar
+        int exitCode = exec(app).shouldContain("ClassNotFoundException").getExitValue();
+        assertNotEquals(exitCode, 0);
+    }
+
+    /**
+     * Test that java -jar fails when the executable JAR has the
+     * Launcher-Agent-Class attribute and the class does not define an
+     * agentmain method.
+     */
+    public void testNoAgentMain() throws Exception {
+        // manifest for the executable JAR
+        Manifest man = new Manifest();
+        Attributes attrs = man.getMainAttributes();
+        attrs.put(Attributes.Name.MANIFEST_VERSION, "1.0.0");
+        attrs.put(Attributes.Name.MAIN_CLASS, "Main");
+
+        // the main class does not define the agentmain method
+        attrs.put(new Attributes.Name("Launcher-Agent-Class"), "Main");
+
+        Path app = Paths.get("app.jar");
+        Path dir = Paths.get(System.getProperty("test.classes"));
+
+        JarUtils.createJarFile(app, man, dir, Paths.get("Main.class"));
+
+        // java -jar app.jar
+        int exitCode = exec(app).shouldContain("NoSuchMethodException").getExitValue();
+        assertNotEquals(exitCode, 0);
+    }
+
+    /**
+     * java -jar app.jar, returning the OutputAnalyzer to analyze the output
+     */
+    private OutputAnalyzer exec(Path appJar) throws Exception {
+        return ProcessTools.executeTestJava("-jar", appJar.toString())
+                .outputTo(System.out)
+                .errorTo(System.out);
+    }
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/instrument/executableJAR/Main.java	Thu May 04 07:26:55 2017 +0000
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ */
+
+import java.lang.instrument.Instrumentation;
+
+public class Main {
+    public static void main(String[] args) throws Exception {
+        Instrumentation inst = Agent.inst;
+        if (inst == null)
+            throw new RuntimeException("Agent not loaded");
+
+        // check boot class path has been extended
+        Class<?> helper = Class.forName("AgentHelper");
+        if (helper.getClassLoader() != null)
+            throw new RuntimeException("AgentHelper not loaded by boot loader");
+
+        // check Instrumentation object can be used
+        Class<?>[] classes = inst.getAllLoadedClasses();
+        System.out.println(classes.length + " classes loaded");
+    }
+}
--- a/jdk/test/java/lang/invoke/DefineClassTest.java	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/test/java/lang/invoke/DefineClassTest.java	Thu May 04 07:26:55 2017 +0000
@@ -75,7 +75,7 @@
     @Test
     public void testDefineClass() throws Exception {
         final String CLASS_NAME = THIS_PACKAGE + ".Foo";
-        Lookup lookup = lookup().dropLookupMode(PRIVATE);
+        Lookup lookup = lookup();
         Class<?> clazz = lookup.defineClass(generateClass(CLASS_NAME));
 
         // test name
@@ -101,7 +101,7 @@
     public void testAccess() throws Exception {
         final String THIS_CLASS = this.getClass().getName();
         final String CLASS_NAME = THIS_PACKAGE + ".Runner";
-        Lookup lookup = lookup().dropLookupMode(PRIVATE);
+        Lookup lookup = lookup();
 
         // public
         byte[] classBytes = generateRunner(CLASS_NAME + nextNumber(), THIS_CLASS, "method1");
@@ -144,9 +144,8 @@
         final String CLASS_NAME = THIS_PACKAGE + ".ClassWithClinit";
 
         byte[] classBytes = generateClassWithInitializer(CLASS_NAME, THIS_CLASS, "fail");
-        Lookup lookup = lookup().dropLookupMode(PRIVATE);
+        Class<?> clazz = lookup().defineClass(classBytes);
 
-        Class<?> clazz = lookup.defineClass(classBytes);
         // trigger initializer to run
         try {
             clazz.newInstance();
@@ -186,14 +185,14 @@
         assertNotEquals(target1.getProtectionDomain(), target2.getProtectionDomain());
 
         // protection domain 1
-        Lookup lookup1 = privateLookupIn(target1, lookup()).dropLookupMode(PRIVATE);
+        Lookup lookup1 = privateLookupIn(target1, lookup());
 
         Class<?> clazz = lookup1.defineClass(generateClass("p.Foo"));
         testSameAbode(clazz, lookup1.lookupClass());
         testDiscoverable(clazz, lookup1);
 
         // protection domain 2
-        Lookup lookup2 = privateLookupIn(target2, lookup()).dropLookupMode(PRIVATE);
+        Lookup lookup2 = privateLookupIn(target2, lookup());
 
         clazz = lookup2.defineClass(generateClass("p.Bar"));
         testSameAbode(clazz, lookup2.lookupClass());
@@ -205,7 +204,7 @@
      */
     @Test
     public void testBootLoader() throws Exception {
-        Lookup lookup = privateLookupIn(Thread.class, lookup()).dropLookupMode(PRIVATE);
+        Lookup lookup = privateLookupIn(Thread.class, lookup());
         assertTrue(lookup.getClass().getClassLoader() == null);
 
         Class<?> clazz = lookup.defineClass(generateClass("java.lang.Foo"));
@@ -216,8 +215,7 @@
 
     @Test(expectedExceptions = { IllegalArgumentException.class })
     public void testWrongPackage() throws Exception {
-        Lookup lookup = lookup().dropLookupMode(PRIVATE);
-        lookup.defineClass(generateClass("other.C"));
+        lookup().defineClass(generateClass("other.C"));
     }
 
     @Test(expectedExceptions = { IllegalAccessException.class })
@@ -226,23 +224,14 @@
         lookup.defineClass(generateClass(THIS_PACKAGE + ".C"));
     }
 
-    @Test(expectedExceptions = { UnsupportedOperationException.class })
-    public void testHasPrivateAccess() throws Exception {
-        Lookup lookup = lookup();
-        assertTrue(lookup.hasPrivateAccess());
-        lookup.defineClass(generateClass(THIS_PACKAGE + ".C"));
-    }
-
     @Test(expectedExceptions = { ClassFormatError.class })
     public void testTruncatedClassFile() throws Exception {
-        Lookup lookup = lookup().dropLookupMode(PRIVATE);
-        lookup.defineClass(new byte[0]);
+        lookup().defineClass(new byte[0]);
     }
 
     @Test(expectedExceptions = { NullPointerException.class })
     public void testNull() throws Exception {
-        Lookup lookup = lookup().dropLookupMode(PRIVATE);
-        lookup.defineClass(null);
+        lookup().defineClass(null);
     }
 
     /**
--- a/jdk/test/java/lang/module/AutomaticModulesTest.java	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/test/java/lang/module/AutomaticModulesTest.java	Thu May 04 07:26:55 2017 +0000
@@ -402,9 +402,7 @@
     // Main-Class files that do not map to a legal qualified type name
     @DataProvider(name = "badmainclass")
     public Object[][] createBadMainClass() {
-        return new Object[][]{
-
-            { "Main",        null },
+        return new Object[][] {
             { "p..Main",     null },
             { "p-.Main",     null },
 
@@ -415,7 +413,7 @@
      * Test that a JAR file with a Main-Class attribute that is not a qualified
      * type name.
      */
-    @Test(dataProvider = "badmainclass", expectedExceptions = FindException.class)
+    @Test(dataProvider = "badmainclass")
     public void testBadMainClass(String mainClass, String ignore) throws IOException {
         Manifest man = new Manifest();
         Attributes attrs = man.getMainAttributes();
@@ -426,14 +424,16 @@
         String entry = mainClass.replace('.', '/') + ".class";
         createDummyJarFile(dir.resolve("m.jar"), man, entry);
 
-        // should throw FindException
-        ModuleFinder.of(dir).findAll();
+        // bad Main-Class value should be ignored
+        Optional<ModuleReference> omref = ModuleFinder.of(dir).find("m");
+        assertTrue(omref.isPresent());
+        ModuleDescriptor descriptor = omref.get().descriptor();
+        assertFalse(descriptor.mainClass().isPresent());
     }
 
     /**
      * Test that a JAR file with a Main-Class attribute that is not in the module
      */
-    @Test(expectedExceptions = FindException.class)
     public void testMissingMainClassPackage() throws IOException {
         Manifest man = new Manifest();
         Attributes attrs = man.getMainAttributes();
@@ -443,8 +443,11 @@
         Path dir = Files.createTempDirectory(USER_DIR, "mods");
         createDummyJarFile(dir.resolve("m.jar"), man);
 
-        // should throw FindException
-        ModuleFinder.of(dir).findAll();
+        // Main-Class should be ignored because package p is not in module
+        Optional<ModuleReference> omref = ModuleFinder.of(dir).find("m");
+        assertTrue(omref.isPresent());
+        ModuleDescriptor descriptor = omref.get().descriptor();
+        assertFalse(descriptor.mainClass().isPresent());
     }
 
 
--- a/jdk/test/java/lang/module/ConfigurationTest.java	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/test/java/lang/module/ConfigurationTest.java	Thu May 04 07:26:55 2017 +0000
@@ -1843,17 +1843,9 @@
     public Object[][] createPlatformMatches() {
         return new Object[][]{
 
-            { "linux-arm",     "*-*" },
-            { "linux-*",       "*-*" },
-            { "*-arm",         "*-*" },
-
-            { "linux-*",       "linux-*" },
-            { "linux-arm",     "linux-*" },
-
-            { "*-arm",         "*-arm" },
-            { "linux-arm",     "*-arm" },
-
-            { "linux-arm",   "linux-arm" },
+            { "",              "" },
+            { "linux-arm",     "" },
+            { "linux-arm",     "linux-arm" },
 
         };
 
@@ -1863,9 +1855,8 @@
     public Object[][] createBad() {
         return new Object[][] {
 
-            { "linux-*",        "solaris-*"     },
-            { "*-arm",          "*-sparc"       },
-            { "linux-x86",      "solaris-sparc" },
+            { "linux-x64",        "linux-arm" },
+            { "linux-x64",        "windows-x64" },
 
         };
     }
@@ -1877,7 +1868,7 @@
     public void testPlatformMatch(String s1, String s2) throws IOException {
 
         ModuleDescriptor base =  ModuleDescriptor.newModule("java.base").build();
-        Path system = writeModule(base, "*-*");
+        Path system = writeModule(base, null);
 
         ModuleDescriptor descriptor1 = ModuleDescriptor.newModule("m1")
                 .requires("m2")
@@ -1928,7 +1919,7 @@
         throws IOException
     {
         ModuleDescriptor base =  ModuleDescriptor.newModule("java.base").build();
-        Path system = writeModule(base, "*-*");
+        Path system = writeModule(base, null);
 
         ModuleDescriptor descriptor1 = ModuleDescriptor.newModule("m1").build();
         Path dir1 = writeModule(descriptor1, s1);
@@ -2113,17 +2104,18 @@
 
 
     /**
-     * Decodes the platform string and calls the builder osName/osArch/osVersion
-     * methods to set the platform constraints.
+     * Writes a module-info.class. If {@code targetPlatform} is not null then
+     * it includes the ModuleTarget class file attribute with the target platform.
      */
-    static Path writeModule(ModuleDescriptor descriptor, String platformString)
+    static Path writeModule(ModuleDescriptor descriptor, String targetPlatform)
         throws IOException
     {
-        String[] s = platformString.split("-");
-        String osName = !s[0].equals("*") ? s[0] : null;
-        String osArch = !s[1].equals("*") ? s[1] : null;
-        ModuleTarget target = new ModuleTarget(osName, osArch);
-
+        ModuleTarget target;
+        if (targetPlatform != null) {
+            target = new ModuleTarget(targetPlatform);
+        } else {
+            target = null;
+        }
         String name = descriptor.name();
         Path dir = Files.createTempDirectory(name);
         Path mi = dir.resolve("module-info.class");
--- a/jdk/test/java/lang/module/ModuleDescriptorTest.java	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/test/java/lang/module/ModuleDescriptorTest.java	Thu May 04 07:26:55 2017 +0000
@@ -65,8 +65,8 @@
 @Test
 public class ModuleDescriptorTest {
 
-    @DataProvider(name = "invalidjavaidentifiers")
-    public Object[][] invalidJavaIdentifiers() {
+    @DataProvider(name = "invalidNames")
+    public Object[][] invalidNames() {
         return new Object[][]{
 
             { null,             null },
@@ -84,6 +84,32 @@
             { "foo.bar.1gus",   null },
             { "foo.bar.[gus]",  null },
 
+            { "class",          null },
+            { "interface",      null },
+            { "true",           null },
+            { "false",          null },
+            { "null",           null },
+
+            { "x.class",        null },
+            { "x.interface",    null },
+            { "x.true",         null },
+            { "x.false",        null },
+            { "x.null",         null },
+
+            { "class.x",        null },
+            { "interface.x",    null },
+            { "true.x",         null },
+            { "false.x",        null },
+            { "null.x",         null },
+
+            { "x.class.x",      null },
+            { "x.interface.x",  null },
+            { "x.true.x",       null },
+            { "x.false.x",      null },
+            { "x.null.x",       null },
+
+            { "_",              null },
+
         };
     }
 
@@ -199,7 +225,7 @@
         ModuleDescriptor.newModule("m").requires(EnumSet.allOf(Modifier.class), "m");
     }
 
-    @Test(dataProvider = "invalidjavaidentifiers",
+    @Test(dataProvider = "invalidNames",
           expectedExceptions = IllegalArgumentException.class )
     public void testRequiresWithBadModuleName(String mn, String ignore) {
         requires(EnumSet.noneOf(Modifier.class), mn);
@@ -406,7 +432,7 @@
         ModuleDescriptor.newModule("foo").exports("p", Collections.emptySet());
     }
 
-    @Test(dataProvider = "invalidjavaidentifiers",
+    @Test(dataProvider = "invalidNames",
           expectedExceptions = IllegalArgumentException.class )
     public void testExportsWithBadName(String pn, String ignore) {
         ModuleDescriptor.newModule("foo").exports(pn);
@@ -568,7 +594,7 @@
         ModuleDescriptor.newModule("foo").opens("p", Collections.emptySet());
     }
 
-    @Test(dataProvider = "invalidjavaidentifiers",
+    @Test(dataProvider = "invalidNames",
             expectedExceptions = IllegalArgumentException.class )
     public void testOpensWithBadName(String pn, String ignore) {
         ModuleDescriptor.newModule("foo").opens(pn);
@@ -664,7 +690,7 @@
         ModuleDescriptor.newModule("foo").uses("S");
     }
 
-    @Test(dataProvider = "invalidjavaidentifiers",
+    @Test(dataProvider = "invalidNames",
           expectedExceptions = IllegalArgumentException.class )
     public void testUsesWithBadName(String service, String ignore) {
         ModuleDescriptor.newModule("foo").uses(service);
@@ -737,13 +763,13 @@
         ModuleDescriptor.newModule("foo").provides("p.S", List.of("P"));
     }
 
-    @Test(dataProvider = "invalidjavaidentifiers",
+    @Test(dataProvider = "invalidNames",
           expectedExceptions = IllegalArgumentException.class )
     public void testProvidesWithBadService(String service, String ignore) {
         ModuleDescriptor.newModule("foo").provides(service, List.of("p.Provider"));
     }
 
-    @Test(dataProvider = "invalidjavaidentifiers",
+    @Test(dataProvider = "invalidNames",
           expectedExceptions = IllegalArgumentException.class )
     public void testProvidesWithBadProvider(String provider, String ignore) {
         List<String> names = new ArrayList<>(); // allows nulls
@@ -928,7 +954,7 @@
         assertTrue(Objects.equals(packages, Set.of("p1", "p2", "p3", "p4", "p5")));
     }
 
-    @Test(dataProvider = "invalidjavaidentifiers",
+    @Test(dataProvider = "invalidNames",
           expectedExceptions = IllegalArgumentException.class )
     public void testPackagesWithBadName(String pn, String ignore) {
         Set<String> pkgs = new HashSet<>();  // allows nulls
@@ -943,7 +969,7 @@
         assertEquals(mn, "foo");
     }
 
-    @Test(dataProvider = "invalidjavaidentifiers",
+    @Test(dataProvider = "invalidNames",
           expectedExceptions = IllegalArgumentException.class )
     public void testBadModuleName(String mn, String ignore) {
         ModuleDescriptor.newModule(mn);
@@ -1264,7 +1290,7 @@
         ModuleDescriptor.newModule("foo").mainClass("Main");
     }
 
-    @Test(dataProvider = "invalidjavaidentifiers",
+    @Test(dataProvider = "invalidNames",
           expectedExceptions = IllegalArgumentException.class )
     public void testMainClassWithBadName(String mainClass, String ignore) {
         Builder builder = ModuleDescriptor.newModule("foo");
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/module/customfs/ModulesInCustomFileSystem.java	Thu May 04 07:26:55 2017 +0000
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * 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
+ * @modules jdk.zipfs
+ * @library /lib/testlibrary
+ * @build ModulesInCustomFileSystem JarUtils m1/* m2/*
+ * @run testng/othervm ModulesInCustomFileSystem
+ * @summary Test ModuleFinder to find modules in a custom file system
+ */
+
+import java.io.File;
+import java.lang.module.Configuration;
+import java.lang.module.ModuleFinder;
+import java.lang.module.ModuleReader;
+import java.lang.module.ModuleReference;
+import java.lang.reflect.Method;
+import java.nio.file.FileSystem;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Set;
+
+import org.testng.annotations.Test;
+import static org.testng.Assert.*;
+
+@Test
+public class ModulesInCustomFileSystem {
+
+    /**
+     * Test exploded modules in a JAR file system.
+     */
+    public void testExplodedModulesInJarFileSystem() throws Exception {
+        Path m1 = findModuleDirectory("m1");
+        Path m2 = findModuleDirectory("m2");
+        Path mlib = m1.getParent();
+        assertEquals(mlib, m2.getParent());
+
+        // create JAR file containing m1/** and m2/**
+        Path jar = Files.createTempDirectory("mlib").resolve("modules.jar");
+        JarUtils.createJarFile(jar, mlib);
+        testJarFileSystem(jar);
+    }
+
+    /**
+     * Test modular JARs in a JAR file system
+     */
+    public void testModularJARsInJarFileSystem() throws Exception {
+        Path m1 = findModuleDirectory("m1");
+        Path m2 = findModuleDirectory("m2");
+        Path contents = Files.createTempDirectory("contents");
+        JarUtils.createJarFile(contents.resolve("m1.jar"), m1);
+        JarUtils.createJarFile(contents.resolve("m2.jar"), m2);
+
+        // create JAR file containing m1.jar and m2.jar
+        Path jar = Files.createTempDirectory("mlib").resolve("modules.jar");
+        JarUtils.createJarFile(jar, contents);
+        testJarFileSystem(jar);
+    }
+
+    /**
+     * Opens a JAR file as a file system
+     */
+    private void testJarFileSystem(Path jar) throws Exception {
+        ClassLoader scl = ClassLoader.getSystemClassLoader();
+        try (FileSystem fs = FileSystems.newFileSystem(jar, scl)) {
+            // ModuleFinder to find modules in top-level directory
+            Path top = fs.getPath("/");
+            ModuleFinder finder = ModuleFinder.of(top);
+
+            // list the modules
+            listAllModules(finder);
+
+            // load modules into child layer, invoking m1/p.Main
+            loadAndRunModule(finder);
+        }
+    }
+
+    /**
+     * List all modules that the finder finds and the resources in the module.
+     */
+    private void listAllModules(ModuleFinder finder) throws Exception {
+        for (ModuleReference mref : finder.findAll()) {
+            System.out.println(mref.descriptor());
+            try (ModuleReader reader = mref.open()) {
+                reader.list().forEach(name -> System.out.format("  %s%n", name));
+            }
+        }
+    }
+
+    /**
+     * Creates a child layer with m1 and m2, invokes m1/p.Main to ensure that
+     * classes can be loaded.
+     */
+    private void loadAndRunModule(ModuleFinder finder) throws Exception {
+        ModuleLayer bootLayer = ModuleLayer.boot();
+        Configuration cf = bootLayer.configuration()
+                .resolve(finder, ModuleFinder.of(), Set.of("m1"));
+        ClassLoader scl = ClassLoader.getSystemClassLoader();
+        ModuleLayer layer = bootLayer.defineModulesWithOneLoader(cf, scl);
+        Class<?> c = layer.findLoader("m1").loadClass("p.Main");
+        Method m = c.getMethod("main", String[].class);
+        m.invoke(null, (Object)new String[0]);
+    }
+
+    /**
+     * Find the directory for a module on the module path
+     */
+    private Path findModuleDirectory(String name) {
+        String mp = System.getProperty("jdk.module.path");
+        for (String element : mp.split(File.pathSeparator)) {
+            Path dir = Paths.get(element).resolve(name);
+            if (Files.exists(dir)) {
+                return dir;
+            }
+        }
+        assertFalse(true);
+        return null;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/module/customfs/m1/module-info.java	Thu May 04 07:26:55 2017 +0000
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * 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 {
+    exports p;
+    requires m2;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/module/customfs/m1/p/Main.java	Thu May 04 07:26:55 2017 +0000
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * 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 p;
+
+public class Main {
+    public static void main(String[] args) {
+        q.Hello.hello();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/module/customfs/m2/module-info.java	Thu May 04 07:26:55 2017 +0000
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * 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 q;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/module/customfs/m2/q/Hello.java	Thu May 04 07:26:55 2017 +0000
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * 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 q;
+
+public class Hello {
+    public static void hello() {
+        System.out.println("hello");
+    }
+}
--- a/jdk/test/java/nio/file/spi/SetDefaultProvider.java	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/test/java/nio/file/spi/SetDefaultProvider.java	Thu May 04 07:26:55 2017 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2008, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -21,23 +21,141 @@
  * questions.
  */
 
-/* @test
- * @bug 4313887 7006126
- * @summary Unit test for java.nio.file.spi.FileSystemProvider
- * @build TestProvider SetDefaultProvider
- * @run main/othervm -Djava.nio.file.spi.DefaultFileSystemProvider=TestProvider SetDefaultProvider
+/**
+ * @test
+ * @modules jdk.jartool
+ * @library /lib/testlibrary
+ * @build SetDefaultProvider TestProvider m/* jdk.testlibrary.ProcessTools
+ * @run testng/othervm SetDefaultProvider
+ * @summary Runs tests with -Djava.nio.file.spi.DefaultFileSystemProvider set on
+ *          the command line to override the default file system provider
  */
 
-import java.nio.file.*;
+import java.io.File;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.spi.ToolProvider;
 
+import jdk.testlibrary.ProcessTools;
+
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.Test;
+import static org.testng.Assert.*;
+
+@Test
 public class SetDefaultProvider {
-    public static void main(String[] args) throws Exception {
-        Class<?> c = FileSystems.getDefault().provider().getClass();
+
+    private static String SET_DEFAULT_FSP =
+        "-Djava.nio.file.spi.DefaultFileSystemProvider=TestProvider";
+
+    private static final ToolProvider JAR_TOOL = ToolProvider.findFirst("jar")
+        .orElseThrow(() ->
+            new RuntimeException("jar tool not found")
+        );
+
+    /**
+     * Test override of default FileSystemProvider with the main application
+     * on the class path.
+     */
+    public void testClassPath() throws Exception {
+        String moduleClasses = moduleClasses();
+        String testClasses = System.getProperty("test.classes");
+        String classpath = moduleClasses + File.pathSeparator + testClasses;
+        int exitValue = exec(SET_DEFAULT_FSP, "-cp", classpath, "p.Main");
+        assertTrue(exitValue == 0);
+    }
+
+    /**
+     * Test override of default FileSystemProvider with the main application
+     * on the module path as an exploded module.
+     */
+    public void testExplodedModule() throws Exception {
+        String modulePath = System.getProperty("jdk.module.path");
+        int exitValue = exec(SET_DEFAULT_FSP, "-p", modulePath, "-m", "m/p.Main");
+        assertTrue(exitValue == 0);
+    }
+
+    /**
+     * Test override of default FileSystemProvider with the main application
+     * on the module path as a modular JAR.
+     */
+    public void testModularJar() throws Exception {
+        String jarFile = createModularJar();
+        int exitValue = exec(SET_DEFAULT_FSP, "-p", jarFile, "-m", "m/p.Main");
+        assertTrue(exitValue == 0);
+    }
 
-        Class<?> expected = Class.forName("TestProvider", false,
-            ClassLoader.getSystemClassLoader());
+    /**
+     * Test override of default FileSystemProvider where the main application
+     * is a module that is patched by an exploded patch.
+     */
+    public void testExplodedModuleWithExplodedPatch() throws Exception {
+        Path patchdir = Files.createTempDirectory("patch");
+        String modulePath = System.getProperty("jdk.module.path");
+        int exitValue = exec(SET_DEFAULT_FSP,
+                             "--patch-module", "m=" + patchdir,
+                             "-p", modulePath,
+                             "-m", "m/p.Main");
+        assertTrue(exitValue == 0);
+    }
+
+    /**
+     * Test override of default FileSystemProvider where the main application
+     * is a module that is patched by an exploded patch.
+     */
+    public void testExplodedModuleWithJarPatch() throws Exception {
+        Path patchdir = Files.createTempDirectory("patch");
+        Files.createDirectory(patchdir.resolve("m.properties"));
+        Path patch = createJarFile(patchdir);
+        String modulePath = System.getProperty("jdk.module.path");
+        int exitValue = exec(SET_DEFAULT_FSP,
+                             "--patch-module", "m=" + patch,
+                             "-p", modulePath,
+                             "-m", "m/p.Main");
+        assertTrue(exitValue == 0);
+    }
 
-        if (c != expected)
-            throw new RuntimeException();
+    /**
+     * Returns the directory containing the classes for module "m".
+     */
+    private String moduleClasses() {
+        String mp = System.getProperty("jdk.module.path");
+        for (String dir : mp.split(File.pathSeparator)) {
+            Path m = Paths.get(dir, "m");
+            if (Files.exists(m)) return m.toString();
+        }
+        assertFalse(true);
+        return null;
+    }
+
+    /**
+     * Creates a modular JAR containing module "m".
+     */
+    private String createModularJar() throws Exception {
+        Path dir = Paths.get(moduleClasses());
+        Path jar = createJarFile(dir);
+        return jar.toString();
+    }
+
+    /**
+     * Creates a JAR file containing the entries in the given file tree.
+     */
+    private Path createJarFile(Path dir) throws Exception {
+        Path jar = Files.createTempDirectory("tmp").resolve("m.jar");
+        String[] args = { "--create", "--file=" + jar, "-C", dir.toString(), "." };
+        int ret = JAR_TOOL.run(System.out, System.out, args);
+        assertTrue(ret == 0);
+        return jar;
+    }
+
+    /**
+     * Invokes the java launcher with the given arguments, returning the exit code.
+     */
+    private int exec(String... args) throws Exception {
+       return ProcessTools.executeTestJava(args)
+                .outputTo(System.out)
+                .errorTo(System.out)
+                .getExitValue();
     }
 }
--- a/jdk/test/java/nio/file/spi/TestProvider.java	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/test/java/nio/file/spi/TestProvider.java	Thu May 04 07:26:55 2017 +0000
@@ -77,7 +77,7 @@
                              LinkOption... options)
         throws IOException
     {
-        throw new ReadOnlyFileSystemException();
+        throw new RuntimeException("not implemented");
     }
 
     @Override
@@ -110,19 +110,20 @@
 
     @Override
     public void delete(Path file) throws IOException {
-        throw new ReadOnlyFileSystemException();
+        Path delegate = theFileSystem.unwrap(file);
+        defaultProvider.delete(delegate);
     }
 
     @Override
     public void createSymbolicLink(Path link, Path target, FileAttribute<?>... attrs)
         throws IOException
     {
-        throw new ReadOnlyFileSystemException();
+        throw new RuntimeException("not implemented");
     }
 
     @Override
     public void createLink(Path link, Path existing) throws IOException {
-        throw new ReadOnlyFileSystemException();
+        throw new RuntimeException("not implemented");
     }
 
     @Override
@@ -136,14 +137,14 @@
     public void copy(Path source, Path target, CopyOption... options)
         throws IOException
     {
-        throw new ReadOnlyFileSystemException();
+        throw new RuntimeException("not implemented");
     }
 
     @Override
     public void move(Path source, Path target, CopyOption... options)
         throws IOException
     {
-        throw new ReadOnlyFileSystemException();
+        throw new RuntimeException("not implemented");
     }
 
     @Override
@@ -158,7 +159,8 @@
     public void createDirectory(Path dir, FileAttribute<?>... attrs)
         throws IOException
     {
-        throw new ReadOnlyFileSystemException();
+        Path delegate = theFileSystem.unwrap(dir);
+        defaultProvider.createDirectory(delegate, attrs);
     }
 
     @Override
@@ -167,13 +169,8 @@
                                               FileAttribute<?>... attrs)
         throws IOException
     {
-        if (options.contains(StandardOpenOption.READ) && options.size() == 1) {
-            Path delegate = theFileSystem.unwrap(file);
-            options = Collections.singleton(StandardOpenOption.READ);
-            return defaultProvider.newByteChannel(delegate, options, attrs);
-        }
-
-        throw new RuntimeException("not implemented");
+        Path delegate = theFileSystem.unwrap(file);
+        return defaultProvider.newByteChannel(delegate, options, attrs);
     }
 
     @Override
@@ -236,7 +233,7 @@
 
         @Override
         public boolean isReadOnly() {
-            return true;
+            return false;
         }
 
         @Override
@@ -419,7 +416,7 @@
 
         @Override
         public File toFile() {
-            return delegate.toFile();
+            return new File(toString());
         }
 
         @Override
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/nio/file/spi/m/module-info.java	Thu May 04 07:26:55 2017 +0000
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * 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 m {
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/nio/file/spi/m/p/Main.java	Thu May 04 07:26:55 2017 +0000
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * 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 p;
+
+import java.io.File;
+import java.nio.file.FileSystem;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+/**
+ * Launched by SetDefaultProvider to test startup with the default file system
+ * provider overridden.
+ */
+
+public class Main {
+    public static void main(String[] args) throws Exception {
+        FileSystem fs = FileSystems.getDefault();
+        if (fs.getClass().getModule() == Object.class.getModule())
+            throw new RuntimeException("FileSystemProvider not overridden");
+
+        // exercise the file system
+        Path dir = Files.createTempDirectory("tmp");
+        if (dir.getFileSystem() != fs)
+            throw new RuntimeException("'dir' not in default file system");
+        System.out.println("created: " + dir);
+
+        Path foo = Files.createFile(dir.resolve("foo"));
+        if (foo.getFileSystem() != fs)
+            throw new RuntimeException("'foo' not in default file system");
+        System.out.println("created: " + foo);
+
+        // exercise interop with java.io.File
+        File file = foo.toFile();
+        Path path = file.toPath();
+        if (path.getFileSystem() != fs)
+            throw new RuntimeException("'path' not in default file system");
+        if (!path.equals(foo))
+            throw new RuntimeException(path + " not equal to " + foo);
+    }
+}
--- a/jdk/test/java/util/logging/TestLoggerWeakRefLeak.java	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/test/java/util/logging/TestLoggerWeakRefLeak.java	Thu May 04 07:26:55 2017 +0000
@@ -41,8 +41,8 @@
  * @modules jdk.attach/sun.tools.attach
  *          java.logging
  * @build jdk.testlibrary.ProcessTools
- * @run main/othervm TestLoggerWeakRefLeak Logger
- * @run main/othervm TestLoggerWeakRefLeak AnonymousLogger
+ * @run main/othervm -Djdk.attach.allowAttachSelf TestLoggerWeakRefLeak Logger
+ * @run main/othervm -Djdk.attach.allowAttachSelf TestLoggerWeakRefLeak AnonymousLogger
  */
 public class TestLoggerWeakRefLeak {
 
--- a/jdk/test/lib/testlibrary/ModuleTargetHelper.java	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/test/lib/testlibrary/ModuleTargetHelper.java	Thu May 04 07:26:55 2017 +0000
@@ -41,19 +41,14 @@
     private ModuleTargetHelper() {}
 
     public static final class ModuleTarget {
-        private String osName, osArch;
+        private String targetPlatform;
 
-        public ModuleTarget(String osName, String osArch) {
-            this.osName = osName;
-            this.osArch = osArch;
+        public ModuleTarget(String targetPlatform) {
+            this.targetPlatform = targetPlatform;
         }
 
-        public String osName() {
-            return osName;
-        }
-
-        public String osArch() {
-            return osArch;
+        public String targetPlatform() {
+            return targetPlatform;
         }
     }
 
@@ -84,7 +79,7 @@
         ClassReader cr = new ClassReader(in);
         cr.accept(cv, attrs, 0);
         if (modTargets[0] != null) {
-            return new ModuleTarget(modTargets[0].osName(), modTargets[0].osArch());
+            return new ModuleTarget(modTargets[0].targetPlatform());
         }
 
         return null;
--- a/jdk/test/sun/management/jmxremote/bootstrap/JvmstatCountersTest.java	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/test/sun/management/jmxremote/bootstrap/JvmstatCountersTest.java	Thu May 04 07:26:55 2017 +0000
@@ -37,7 +37,7 @@
  * @run main/othervm/timeout=600 -XX:+UsePerfData JvmstatCountersTest 1
  * @run main/othervm/timeout=600 -XX:+UsePerfData -Dcom.sun.management.jmxremote JvmstatCountersTest 2
  * @run main/othervm/timeout=600 -XX:+UsePerfData -Dcom.sun.management.jmxremote.port=0 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false JvmstatCountersTest 3
- * @run main/othervm/timeout=600 -XX:+UsePerfData JvmstatCountersTest 4
+ * @run main/othervm/timeout=600 -XX:+UsePerfData -Djdk.attach.allowAttachSelf JvmstatCountersTest 4
  */
 
 import java.io.*;
--- a/jdk/test/tools/jar/mmrjar/Basic.java	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/test/tools/jar/mmrjar/Basic.java	Thu May 04 07:26:55 2017 +0000
@@ -221,7 +221,8 @@
         Assert.assertEquals(actual, expected);
     }
 
-    // jar tool does two updates, no exported packages, all concealed
+    // jar tool does two updates, no exported packages, all concealed.
+    // Along with various --describe-module variants
     @Test
     public void test5() throws IOException {
         // compile the mr10 directory
@@ -266,10 +267,13 @@
 
         jar("-d --file mr.jar");
 
+        String uri = (Paths.get("mr.jar")).toUri().toString();
+        uri = "jar:" + uri + "/!module-info.class";
+
         actual = lines(outbytes);
         expected = Set.of(
-                "module hi (module-info.class)",
-                "requires mandated java.base",
+                "hi " + uri,
+                "requires java.base mandated",
                 "contains p",
                 "contains p.internal"
         );
@@ -304,13 +308,19 @@
 
         actual = lines(outbytes);
         expected = Set.of(
-                "module hi (module-info.class)",
-                "requires mandated java.base",
+                "hi " + uri,
+                "requires java.base mandated",
                 "contains p",
                 "contains p.internal",
                 "contains p.internal.bar"
         );
         Assert.assertEquals(actual, expected);
+
+        for (String release : new String[] {"9" , "10", "100", "1000"}) {
+            jar("-d --file mr.jar --release " + release);
+            actual = lines(outbytes);
+            Assert.assertEquals(actual, expected);
+        }
     }
 
     // root and versioned module-info entries have different main-class, version
@@ -399,15 +409,42 @@
         Assert.assertEquals(rc, 0);
 
         jar("-d --file=mmr.jar");
-        System.out.println("-----------------------");
-        System.out.println( new String(outbytes.toByteArray()));
-        Assert.assertEquals(lines(outbytes),
-                            Set.of(
-                           "module m1 (META-INF/versions/9/module-info.class)",
-                           "module m1 (META-INF/versions/10/module-info.class)",
-                           "requires mandated java.base",
-                           "exports p",
-                           "main-class p.Main"));
+        Set<String> actual = lines(outbytes);
+        Set<String> expected = Set.of(
+                "releases: 9 10",
+                "No root module descriptor, specify --release"
+        );
+        Assert.assertEquals(actual, expected);
+
+        String uriPrefix = "jar:" + (Paths.get("mmr.jar")).toUri().toString();
+
+        jar("-d --file=mmr.jar --release 9");
+        actual = lines(outbytes);
+        expected = Set.of(
+                "releases: 9 10",
+                "m1 " + uriPrefix + "/!META-INF/versions/9/module-info.class",
+                "requires java.base mandated",
+                "exports p",
+                "main-class p.Main"
+        );
+        Assert.assertEquals(actual, expected);
+
+        jar("-d --file=mmr.jar --release 10");
+        actual = lines(outbytes);
+        expected = Set.of(
+                "releases: 9 10",
+                "m1 " + uriPrefix + "/!META-INF/versions/10/module-info.class",
+                "requires java.base mandated",
+                "exports p",
+                "main-class p.Main"
+        );
+        Assert.assertEquals(actual, expected);
+
+        for (String release : new String[] {"11", "12", "15", "100"}) {
+            jar("-d --file mmr.jar --release " + release);
+            actual = lines(outbytes);
+            Assert.assertEquals(actual, expected);
+        }
 
         Optional<String> exp = Optional.of("p.Main");
         try (ZipFile zf = new ZipFile("mmr.jar")) {
--- a/jdk/test/tools/jar/modularJar/Basic.java	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/test/tools/jar/modularJar/Basic.java	Thu May 04 07:26:55 2017 +0000
@@ -478,13 +478,13 @@
             "--file=" + modularJar.toString())
             .assertSuccess()
             .resultChecker(r -> {
-                // Expect similar output: "bar, requires mandated foo, ...
+                // Expect "bar jar:file:/.../!module-info.class"
                 // conceals jdk.test.foo, conceals jdk.test.foo.internal"
-                Pattern p = Pattern.compile("module bar \\(module-info.class\\)\\s+requires\\s++foo");
-                assertTrue(p.matcher(r.output).find(),
-                           "Expecting to find \"bar, requires foo,...\"",
+                String uri = "jar:" + modularJar.toUri().toString() + "/!module-info.class";
+                assertTrue(r.output.contains("bar " + uri),
+                           "Expecting to find \"bar " + uri + "\"",
                            "in output, but did not: [" + r.output + "]");
-                p = Pattern.compile(
+                Pattern p = Pattern.compile(
                         "contains\\s+jdk.test.foo\\s+contains\\s+jdk.test.foo.internal");
                 assertTrue(p.matcher(r.output).find(),
                            "Expecting to find \"contains jdk.test.foo,...\"",
@@ -758,14 +758,15 @@
         for (String option : new String[]  {"--describe-module", "-d" }) {
 
             jar(option,
-                "--file=" + modularJar.toString())
+                "--file=" + modularJar.toString(),
+                "--release", "9")
                 .assertSuccess()
                 .resultChecker(r ->
                     assertTrue(r.output.contains("main-class jdk.test.baz.Baz"),
                               "Expected to find ", "main-class jdk.test.baz.Baz",
                                " in [", r.output, "]"));
 
-            jarWithStdin(modularJar.toFile(),  option)
+            jarWithStdin(modularJar.toFile(), option, "--release", "9")
                 .assertSuccess()
                 .resultChecker(r ->
                     assertTrue(r.output.contains("main-class jdk.test.baz.Baz"),
@@ -773,7 +774,7 @@
                                " in [", r.output, "]"));
 
         }
-        // run module maain class
+        // run module main class
         java(mp, "baz/jdk.test.baz.Baz")
             .assertSuccess()
             .resultChecker(r ->
@@ -900,7 +901,7 @@
                 .resultChecker(r -> {
                     assertTrue(r.output.contains("No module descriptor found"));
                     assertTrue(r.output.contains("Derived automatic module"));
-                    assertTrue(r.output.contains("module " + mid),
+                    assertTrue(r.output.contains(mid + " automatic"),
                                "Expected [", "module " + mid,"] in [", r.output, "]");
                     }
                 );
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/tools/jar/multiRelease/whitebox/Driver.java	Thu May 04 07:26:55 2017 +0000
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * 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
+ * @modules jdk.jartool/sun.tools.jar
+ * @run testng/othervm jdk.jartool/sun.tools.jar.ValidatorComparatorTest
+ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/tools/jar/multiRelease/whitebox/jdk.jartool/sun/tools/jar/ValidatorComparatorTest.java	Thu May 04 07:26:55 2017 +0000
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ */
+
+/*
+ * @summary White-box test for Validator.ENTRYNAME_COMPARATOR ( currently just
+ *          checks module descriptors ).
+ */
+package sun.tools.jar;
+
+import java.util.List;
+import static java.util.stream.Collectors.toList;
+import static sun.tools.jar.Validator.ENTRYNAME_COMPARATOR;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+public class ValidatorComparatorTest {
+
+    @Test
+    public void testModuleInfo() throws Throwable {
+        List<String> list =
+                List.of("module-info.class",
+                        "META-INF/versions/9/module-info.class",
+                        "META-INF/versions/10/module-info.class");
+        List<String> sorted = list.stream()
+                .sorted(ENTRYNAME_COMPARATOR)
+                .collect(toList());
+        List<String> expected = list;
+        Assert.assertEquals(sorted, expected);
+
+
+        list =  List.of("META-INF/versions/10/module-info.class",
+                        "META-INF/versions/9/module-info.class",
+                        "module-info.class");
+        sorted = list.stream().sorted(ENTRYNAME_COMPARATOR).collect(toList());
+        expected =
+                List.of("module-info.class",
+                        "META-INF/versions/9/module-info.class",
+                        "META-INF/versions/10/module-info.class");
+        Assert.assertEquals(sorted, expected);
+
+
+        list =  List.of("META-INF/versions/1001/module-info.class",
+                        "META-INF/versions/1000/module-info.class",
+                        "META-INF/versions/999/module-info.class",
+                        "META-INF/versions/101/module-info.class",
+                        "META-INF/versions/100/module-info.class",
+                        "META-INF/versions/99/module-info.class",
+                        "META-INF/versions/31/module-info.class",
+                        "META-INF/versions/30/module-info.class",
+                        "META-INF/versions/29/module-info.class",
+                        "META-INF/versions/21/module-info.class",
+                        "META-INF/versions/20/module-info.class",
+                        "META-INF/versions/13/module-info.class",
+                        "META-INF/versions/12/module-info.class",
+                        "META-INF/versions/11/module-info.class",
+                        "META-INF/versions/10/module-info.class",
+                        "META-INF/versions/9/module-info.class",
+                        "module-info.class");
+        sorted = list.stream().sorted(ENTRYNAME_COMPARATOR).collect(toList());
+        expected =
+                List.of("module-info.class",
+                        "META-INF/versions/9/module-info.class",
+                        "META-INF/versions/10/module-info.class",
+                        "META-INF/versions/11/module-info.class",
+                        "META-INF/versions/12/module-info.class",
+                        "META-INF/versions/13/module-info.class",
+                        "META-INF/versions/20/module-info.class",
+                        "META-INF/versions/21/module-info.class",
+                        "META-INF/versions/29/module-info.class",
+                        "META-INF/versions/30/module-info.class",
+                        "META-INF/versions/31/module-info.class",
+                        "META-INF/versions/99/module-info.class",
+                        "META-INF/versions/100/module-info.class",
+                        "META-INF/versions/101/module-info.class",
+                        "META-INF/versions/999/module-info.class",
+                        "META-INF/versions/1000/module-info.class",
+                        "META-INF/versions/1001/module-info.class");
+        Assert.assertEquals(sorted, expected);
+    }
+}
--- a/jdk/test/tools/jlink/IntegrationTest.java	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/test/tools/jlink/IntegrationTest.java	Thu May 04 07:26:55 2017 +0000
@@ -207,11 +207,6 @@
         }
 
         checkReleaseProperty(props, "JAVA_VERSION");
-        checkReleaseProperty(props, "JAVA_FULL_VERSION");
-        checkReleaseProperty(props, "OS_NAME");
-        checkReleaseProperty(props, "OS_ARCH");
-        // OS_VERSION is added from makefile. We're testing API-way to create image here!
-        // checkReleaseProperty(props, "OS_VERSION");
 
         if (!Files.exists(output.resolve("toto.txt"))) {
             throw new AssertionError("Post processing not called");
--- a/jdk/test/tools/jlink/JLinkNegativeTest.java	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/test/tools/jlink/JLinkNegativeTest.java	Thu May 04 07:26:55 2017 +0000
@@ -178,7 +178,7 @@
                     .output(imageFile)
                     .addMods("not_zip")
                     .modulePath(helper.defaultModulePath())
-                    .call().assertFailure("Error: java.util.zip.ZipException: zip file is empty");
+                    .call().assertFailure("Error: Error reading");
         } finally {
             deleteDirectory(jar);
         }
--- a/jdk/test/tools/jlink/plugins/SystemModuleDescriptors/SystemModulesTest.java	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/test/tools/jlink/plugins/SystemModuleDescriptors/SystemModulesTest.java	Thu May 04 07:26:55 2017 +0000
@@ -113,12 +113,13 @@
         try {
             if (modRef.descriptor().name().equals("java.base")) {
                 ModuleTargetHelper.ModuleTarget mt = ModuleTargetHelper.read(modRef);
-                assertTrue(checkOSName(mt.osName()));
-                assertTrue(checkOSArch(mt.osArch()));
+                String[] values = mt.targetPlatform().split("-");
+                assertTrue(checkOSName(values[0]));
+                assertTrue(checkOSArch(values[1]));
             } else {
                 // target platform attribute is dropped by jlink plugin for other modules
                 ModuleTargetHelper.ModuleTarget mt = ModuleTargetHelper.read(modRef);
-                assertTrue(mt == null || (mt.osName() == null && mt.osArch() == null));
+                assertTrue(mt == null || mt.targetPlatform() == null);
             }
         } catch (IOException exp) {
             throw new UncheckedIOException(exp);
--- a/jdk/test/tools/jlink/plugins/SystemModuleDescriptors/UserModuleTest.java	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/test/tools/jlink/plugins/SystemModuleDescriptors/UserModuleTest.java	Thu May 04 07:26:55 2017 +0000
@@ -220,16 +220,16 @@
             throw new RuntimeException("ModuleTarget is missing for java.base");
         }
 
-        String osName = mt.osName();
-        String osArch = mt.osArch();
+        String[] values = mt.targetPlatform().split("-");
+        String osName = values[0];
+        String osArch = values[1];
 
         // create JMOD files
         Files.createDirectories(JMODS_DIR);
         Stream.of(modules).forEach(mn ->
             assertTrue(jmod("create",
                 "--class-path", MODS_DIR.resolve(mn).toString(),
-                "--os-name", osName,
-                "--os-arch", osArch,
+                "--target-platform", mt.targetPlatform(),
                 "--main-class", mn.replace('m', 'p') + ".Main",
                 JMODS_DIR.resolve(mn + ".jmod").toString()) == 0)
         );
--- a/jdk/test/tools/jlink/plugins/SystemModuleDescriptors/src/m1/p1/Main.java	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/test/tools/jlink/plugins/SystemModuleDescriptors/src/m1/p1/Main.java	Thu May 04 07:26:55 2017 +0000
@@ -62,8 +62,7 @@
         // parse module-info.class
         ClassReader cr = new ClassReader(in);
         cr.accept(cv, attrs, 0);
-        return modTargets[0] != null &&
-            (modTargets[0].osName() != null || modTargets[0].osArch() != null);
+        return modTargets[0] != null && modTargets[0].targetPlatform() != null;
     }
 
     public static void main(String... args) throws Exception {
--- a/jdk/test/tools/jlink/plugins/SystemModuleDescriptors/src/m4/p4/Main.java	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/test/tools/jlink/plugins/SystemModuleDescriptors/src/m4/p4/Main.java	Thu May 04 07:26:55 2017 +0000
@@ -63,8 +63,7 @@
         // parse module-info.class
         ClassReader cr = new ClassReader(in);
         cr.accept(cv, attrs, 0);
-        return modTargets[0] != null &&
-            (modTargets[0].osName() != null || modTargets[0].osArch() != null);
+        return modTargets[0] != null && modTargets[0].targetPlatform() != null;
     }
 
     private static boolean hasModuleTarget(String modName) throws IOException {
--- a/jdk/test/tools/jmod/JmodTest.java	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/test/tools/jmod/JmodTest.java	Thu May 04 07:26:55 2017 +0000
@@ -393,16 +393,17 @@
              MODS_DIR.resolve("describeFoo.jmod").toString())
              .assertSuccess()
              .resultChecker(r -> {
-                 // Expect similar output: "foo,  requires mandated java.base
-                 // exports jdk.test.foo,  contains jdk.test.foo.internal"
-                 Pattern p = Pattern.compile("\\s+foo\\s+requires\\s+mandated\\s+java.base");
+                 // Expect similar output: "foo... exports jdk.test.foo ...
+                 //   ... requires java.base mandated... contains jdk.test.foo.internal"
+                 Pattern p = Pattern.compile("foo\\s+exports\\s+jdk.test.foo");
                  assertTrue(p.matcher(r.output).find(),
-                           "Expecting to find \"foo, requires java.base\"" +
+                           "Expecting to find \"foo... exports jdk.test.foo\"" +
                                 "in output, but did not: [" + r.output + "]");
                  p = Pattern.compile(
-                        "exports\\s+jdk.test.foo\\s+contains\\s+jdk.test.foo.internal");
+                        "requires\\s+java.base\\s+mandated\\s+contains\\s+jdk.test.foo.internal");
                  assertTrue(p.matcher(r.output).find(),
-                           "Expecting to find \"exports ..., contains ...\"" +
+                           "Expecting to find \"requires java.base mandated..., " +
+                                "contains jdk.test.foo.internal ...\"" +
                                 "in output, but did not: [" + r.output + "]");
              });
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/tools/launcher/modules/describe/DescribeModuleTest.java	Thu May 04 07:26:55 2017 +0000
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * 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
+ * @modules java.xml
+ * @library /lib/testlibrary
+ * @build DescribeModuleTest jdk.testlibrary.*
+ * @run testng DescribeModuleTest
+ * @summary Basic test for java --describe-module
+ */
+
+import jdk.testlibrary.ProcessTools;
+
+import org.testng.annotations.Test;
+import static org.testng.Assert.*;
+
+@Test
+public class DescribeModuleTest {
+
+    /**
+     * Test that the output describes java.base
+     */
+    private void expectJavaBase(String... args) throws Exception {
+        int exitValue = ProcessTools.executeTestJava(args)
+                .outputTo(System.out)
+                .errorTo(System.out)
+                .stdoutShouldContain("java.base")
+                .stdoutShouldContain("exports java.lang")
+                .stdoutShouldContain("uses java.nio.file.spi.FileSystemProvider")
+                .stdoutShouldContain("contains sun.launcher")
+                .stdoutShouldNotContain("requires ")
+                .getExitValue();
+        assertTrue(exitValue == 0);
+    }
+
+    /**
+     * Test that the output describes java.xml
+     */
+    private void expectJavaXml(String... args) throws Exception {
+        int exitValue = ProcessTools.executeTestJava(args)
+                .outputTo(System.out)
+                .errorTo(System.out)
+                .stdoutShouldContain("java.xml")
+                .stdoutShouldContain("exports javax.xml")
+                .stdoutShouldContain("requires java.base")
+                .stdoutShouldContain("uses javax.xml.stream.XMLInputFactory")
+                .getExitValue();
+        assertTrue(exitValue == 0);
+    }
+
+    /**
+     * Test output/exitValue when describing an unknown module
+     */
+    private void expectUnknownModule(String... args) throws Exception {
+        int exitValue = ProcessTools.executeTestJava(args)
+                .outputTo(System.out)
+                .errorTo(System.out)
+                .stdoutShouldNotContain("requires java.base")
+                .getExitValue();
+        assertTrue(exitValue != 0);
+    }
+
+
+    public void testDescribeJavaBase() throws Exception {
+        expectJavaBase("--describe-module", "java.base");
+        expectJavaBase("--describe-module=java.base");
+        expectJavaBase("-d", "java.base");
+    }
+
+    public void testDescribeJavaXml() throws Exception {
+        expectJavaXml("--describe-module", "java.xml");
+        expectJavaXml("--describe-module=java.xml");
+        expectJavaXml("-d", "java.xml");
+    }
+
+    public void testDescribeUnknownModule() throws Exception {
+        expectUnknownModule("--describe-module", "jdk.rhubarb");
+        expectUnknownModule("--describe-module=jdk.rhubarb");
+        expectUnknownModule("-d", "jdk.rhubarb");
+    }
+
+}
--- a/jdk/test/tools/launcher/modules/listmods/ListModsTest.java	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/test/tools/launcher/modules/listmods/ListModsTest.java	Thu May 04 07:26:55 2017 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -33,7 +33,7 @@
 import java.nio.file.Path;
 import java.nio.file.Paths;
 
-import static jdk.testlibrary.ProcessTools.*;
+import jdk.testlibrary.ProcessTools;
 import jdk.testlibrary.OutputAnalyzer;
 
 import org.testng.annotations.BeforeTest;
@@ -66,138 +66,80 @@
                 SRC_DIR.resolve("java.transaction"),
                 UPGRADEMODS_DIR.resolve("java.transaction"));
         assertTrue(compiled);
-
     }
 
-
     @Test
     public void testListAll() throws Exception {
-        OutputAnalyzer output
-            = executeTestJava("--list-modules")
-                .outputTo(System.out)
-                .errorTo(System.out);
-        output.shouldContain("java.base");
-        output.shouldContain("java.xml");
-        assertTrue(output.getExitValue() == 0);
-    }
-
-
-    @Test
-    public void testListOneModule() throws Exception {
-        OutputAnalyzer output
-            = executeTestJava("--list-modules=java.base")
-                .outputTo(System.out)
-                .errorTo(System.out);
-        output.shouldContain("java.base");
-        output.shouldContain("exports java.lang");
-        assertTrue(output.getExitValue() == 0);
+        exec("--list-modules")
+                .shouldContain("java.base")
+                .shouldContain("java.xml")
+                .shouldHaveExitValue(0);
     }
 
-
-    @Test
-    public void testListTwoModules() throws Exception {
-        OutputAnalyzer output
-            = executeTestJava("--list-modules", "java.base,java.xml")
-                .outputTo(System.out)
-                .errorTo(System.out);
-        output.shouldContain("java.base");
-        output.shouldContain("exports java.lang");
-        output.shouldContain("java.xml");
-        output.shouldContain("exports javax.xml");
-        assertTrue(output.getExitValue() == 0);
-    }
-
-
-    @Test
-    public void testListUnknownModule() throws Exception {
-        OutputAnalyzer output
-            = executeTestJava("--list-modules", "java.rhubarb")
-                .outputTo(System.out)
-                .errorTo(System.out);
-        output.shouldNotContain("java.base");
-        output.shouldContain("java.rhubarb not found");
-        assertTrue(output.getExitValue() == 0);
-    }
-
-
     @Test
     public void testListWithModulePath() throws Exception {
-        OutputAnalyzer output
-            = executeTestJava("--module-path", MODS_DIR.toString(), "--list-modules")
-                .outputTo(System.out)
-                .errorTo(System.out);
-        output.shouldContain("java.base");
-        output.shouldContain("m1");
-        assertTrue(output.getExitValue() == 0);
+        exec("--list-modules", "--module-path", MODS_DIR.toString())
+                .shouldContain("java.base")
+                .shouldContain("m1")
+                .shouldHaveExitValue(0);
     }
 
-
     @Test
     public void testListWithUpgradeModulePath() throws Exception {
-        OutputAnalyzer output
-            = executeTestJava("--upgrade-module-path", UPGRADEMODS_DIR.toString(),
-                              "--list-modules", "java.transaction")
-                .outputTo(System.out)
-                .errorTo(System.out);
-        output.shouldContain("exports javax.transaction.atomic");
-        assertTrue(output.getExitValue() == 0);
+        String dir = UPGRADEMODS_DIR.toString();
+        exec("--list-modules", "--upgrade-module-path", dir)
+                .shouldContain(UPGRADEMODS_DIR.toString())
+                .shouldHaveExitValue(0);
     }
 
-
     @Test
     public void testListWithLimitMods1() throws Exception {
-        OutputAnalyzer output
-            = executeTestJava("--limit-modules", "java.management.rmi", "--list-modules")
-                .outputTo(System.out)
-                .errorTo(System.out);
-        output.shouldContain("java.rmi");
-        output.shouldContain("java.base");
-        output.shouldNotContain("java.scripting");
-        assertTrue(output.getExitValue() == 0);
+        exec("--limit-modules", "java.management.rmi", "--list-modules")
+                .shouldContain("java.rmi")
+                .shouldContain("java.base")
+                .shouldNotContain("java.scripting")
+                .shouldHaveExitValue(0);
     }
 
-
     @Test
     public void testListWithLimitMods2() throws Exception {
-        OutputAnalyzer output
-            = executeTestJava("--module-path", MODS_DIR.toString(),
-                              "--limit-modules", "java.management",
-                              "--list-modules")
-                .outputTo(System.out)
-                .errorTo(System.out);
-        output.shouldContain("java.base");
-        output.shouldNotContain("m1");
-        assertTrue(output.getExitValue() == 0);
+        exec("--list-modules",
+                    "--module-path", MODS_DIR.toString(),
+                    "--limit-modules", "java.management")
+                .shouldContain("java.base")
+                .shouldNotContain("m1")
+                .shouldHaveExitValue(0);
     }
 
-
     /**
      * java -version --list-modules => should print version and exit
      */
     @Test
     public void testListWithPrintVersion1() throws Exception {
-        OutputAnalyzer output
-            = executeTestJava("-version", "--list-modules")
-                .outputTo(System.out)
-                .errorTo(System.out);
-        output.shouldNotContain("java.base");
-        output.shouldContain("Runtime Environment");
-        assertTrue(output.getExitValue() == 0);
+        exec("-version", "--list-modules")
+                .shouldNotContain("java.base")
+                .shouldContain("Runtime Environment")
+                .shouldHaveExitValue(0);
     }
 
-
     /**
      * java --list-modules -version => should list modules and exit
      */
     @Test
     public void testListWithPrintVersion2() throws Exception {
-        OutputAnalyzer output
-            = executeTestJava("--list-modules", "-version")
+        exec("--list-modules", "-version")
+                .shouldContain("java.base")
+                .shouldNotContain("Runtime Environment")
+                .shouldHaveExitValue(0);
+    }
+
+    /**
+     * java args... returning the OutputAnalyzer to analyzer the output
+     */
+    private OutputAnalyzer exec(String... args) throws Exception {
+        return ProcessTools.executeTestJava(args)
                 .outputTo(System.out)
                 .errorTo(System.out);
-        output.shouldContain("java.base");
-        output.shouldNotContain("Runtime Environment");
-        assertTrue(output.getExitValue() == 0);
     }
 
 }
--- a/jdk/test/tools/launcher/modules/listmods/src/java.transaction/javax/transaction/atomic/Atomic.java	Mon Apr 24 13:43:34 2017 +0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,28 +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 javax.transaction.atomic;
-
-public interface Atomic {
-
-}
--- a/jdk/test/tools/launcher/modules/listmods/src/java.transaction/module-info.java	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/test/tools/launcher/modules/listmods/src/java.transaction/module-info.java	Thu May 04 07:26:55 2017 +0000
@@ -23,5 +23,4 @@
 
 module java.transaction {
     exports javax.transaction;
-    exports javax.transaction.atomic;
 }
--- a/jdk/test/tools/launcher/modules/patch/systemmodules/PatchSystemModules.java	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/test/tools/launcher/modules/patch/systemmodules/PatchSystemModules.java	Thu May 04 07:26:55 2017 +0000
@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -23,9 +23,8 @@
 
 /*
  * @test
- * @bug 8157068
- * @summary Patch java.base and user module with Hashes attribute tied with
- *          other module.
+ * @bug 8157068 8177844
+ * @summary Patch java.base and user module with ModuleHashes attribute
  * @library /lib/testlibrary
  * @modules jdk.compiler
  * @build CompilerUtils
@@ -59,6 +58,7 @@
     private static final Path JARS_DIR = Paths.get("jars");
     private static final Path PATCH_DIR = Paths.get("patches");
     private static final Path IMAGE = Paths.get("image");
+    private static final Path NEW_M1_JAR = JARS_DIR.resolve("new_m1.jar");
 
     private static final String JAVA_BASE = "java.base";
     private final String[] modules = new String[] { "m1", "m2" };
@@ -80,13 +80,23 @@
         assertTrue(CompilerUtils.compile(PATCH_SRC_DIR.resolve("m2"),
                                          PATCH_DIR.resolve("m2")));
 
+        createJars();
+
         // create an image with only m1 and m2
         if (Files.exists(JMODS)) {
             // create an image with m1,m2
             createImage();
         }
+
+        // create new copy of m1.jar
+        jar("--create",
+            "--file=" + NEW_M1_JAR.toString(),
+            "-C", MODS_DIR.resolve("m1").toString(), ".");
     }
 
+    /*
+     * Test patching system module and user module on module path
+     */
     @Test
     public void test() throws Throwable {
         Path patchedJavaBase = PATCH_DIR.resolve(JAVA_BASE);
@@ -107,6 +117,9 @@
                 "-m", "m1/p1.Main", "2");
     }
 
+    /*
+     * Test --patch-module on a custom image
+     */
     @Test
     public void testImage() throws Throwable {
         if (Files.notExists(JMODS))
@@ -125,27 +138,49 @@
                 "-m", "m1/p1.Main", "2");
     }
 
+    /*
+     * Test a module linked in a system hashed in ModuleHashes attribute
+     * cannot be upgraded
+     */
     @Test
-    public void upgradeTiedModule() throws Throwable {
+    public void upgradeHashedModule() throws Throwable {
         if (Files.notExists(JMODS))
             return;
 
-        Path m1 = MODS_DIR.resolve("m1.jar");
-
-        // create another m1.jar
-        jar("--create",
-            "--file=" + m1.toString(),
-            "-C", MODS_DIR.resolve("m1").toString(), ".");
-
         // Fail to upgrade m1.jar with mismatched hash
         runTestWithExitCode(getJava(IMAGE),
-                "--upgrade-module-path", m1.toString(),
-                "-m", "m1/p1.Main");
+                "--upgrade-module-path", NEW_M1_JAR.toString(),
+                "-m", "m1/p1.Main", "ShouldNeverRun");
 
+        // test when SystemModules fast path is not enabled, i.e. exploded image
         runTestWithExitCode(getJava(IMAGE),
                 "--patch-module", "java.base=" + PATCH_DIR.resolve(JAVA_BASE),
-                "--upgrade-module-path", m1.toString(),
-                "-m", "m1/p1.Main", "1");
+                "--upgrade-module-path", NEW_M1_JAR.toString(),
+                "-m", "m1/p1.Main", "ShouldNeverRun");
+    }
+
+    /*
+     * Test a module linked in a system hashed in ModuleHashes attribute
+     * cannot be upgraded combining with --patch-module and --upgrade-module-path
+     */
+    @Test
+    public void patchHashedModule() throws Throwable {
+        if (Files.notExists(JMODS))
+            return;
+
+        // --patch-module does not disable hash check.
+        // Test that a hashed module cannot be upgraded.
+        runTestWithExitCode(getJava(IMAGE),
+                "--patch-module", "m1=.jar",
+                "--upgrade-module-path", NEW_M1_JAR.toString(),
+                "-m", "m1/p1.Main", "ShouldNeverRun");
+
+        // test when SystemModules fast path is not enabled, i.e. exploded image
+        runTestWithExitCode(getJava(IMAGE),
+                "--patch-module", "java.base=" + PATCH_DIR.resolve(JAVA_BASE),
+                "--patch-module", "m1=.jar",
+                "--upgrade-module-path", NEW_M1_JAR.toString(),
+                "-m", "m1/p1.Main", "ShouldNeverRun");
     }
 
     private void runTestWithExitCode(String... options) throws Throwable {
@@ -171,9 +206,8 @@
         assertTrue(exitValue == 0);
     }
 
-    static void createImage() throws Throwable {
+    static void createJars() throws Throwable {
         FileUtils.deleteFileTreeUnchecked(JARS_DIR);
-        FileUtils.deleteFileTreeUnchecked(IMAGE);
 
         Files.createDirectories(JARS_DIR);
         Path m1 = JARS_DIR.resolve("m1.jar");
@@ -189,7 +223,10 @@
             "--module-path", JARS_DIR.toString(),
             "--hash-modules", "m1",
             "-C", MODS_DIR.resolve("m2").toString(), ".");
+    }
 
+    static void createImage() throws Throwable {
+        FileUtils.deleteFileTreeUnchecked(IMAGE);
 
         String mpath = JARS_DIR.toString() + File.pathSeparator + JMODS.toString();
         execTool("jlink", "--module-path", mpath,
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/tools/launcher/modules/showmoduleresolution/ShowModuleResolutionTest.java	Thu May 04 07:26:55 2017 +0000
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * 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
+ * @modules jdk.jdeps jdk.zipfs
+ * @library /lib/testlibrary
+ * @build ShowModuleResolutionTest jdk.testlibrary.*
+ * @run testng ShowModuleResolutionTest
+ * @summary Basic test for java --show-module-resolution
+ */
+
+import jdk.testlibrary.ProcessTools;
+
+import org.testng.annotations.Test;
+import static org.testng.Assert.*;
+
+@Test
+public class ShowModuleResolutionTest {
+
+    /**
+     * Test that the resolution does not bind any services
+     */
+    private void expectJavaBase(String... args) throws Exception {
+        int exitValue = ProcessTools.executeTestJava(args)
+                .outputTo(System.out)
+                .errorTo(System.out)
+                .stdoutShouldContain("root java.base")
+                .stdoutShouldNotContain("java.base binds")
+                .getExitValue();
+        assertTrue(exitValue == 0);
+    }
+
+    /**
+     * Test that the resolution binds services that resolves additional
+     * modules
+     */
+    private void expectProviders(String... args) throws Exception {
+        int exitValue = ProcessTools.executeTestJava(args)
+                .outputTo(System.out)
+                .errorTo(System.out)
+                .stdoutShouldContain("root java.base")
+                .stdoutShouldContain("root java.compiler")
+                .stdoutShouldContain("root jdk.compiler")
+                .stdoutShouldContain("root java.compiler")
+                .stdoutShouldContain("jdk.compiler requires java.compiler")
+                .stdoutShouldContain("java.base binds jdk.compiler")
+                .stdoutShouldContain("java.base binds jdk.jdeps")
+                .stdoutShouldContain("java.base binds jdk.zipfs")
+                .stdoutShouldContain("java.compiler binds jdk.compiler")
+                .stdoutShouldContain("jdk.jdeps requires jdk.compiler")
+                .getExitValue();
+        assertTrue(exitValue == 0);
+    }
+
+    public void test() throws Exception {
+        expectJavaBase("--show-module-resolution",
+                       "--limit-modules", "java.base",
+                       "-version");
+        expectProviders("--show-module-resolution",
+                        "--limit-modules", "java.base,jdk.jdeps,jdk.zipfs",
+                        "-version");
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/tools/launcher/modules/validate/ValidateModulesTest.java	Thu May 04 07:26:55 2017 +0000
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * 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
+ * @modules java.xml
+ * @library /lib/testlibrary
+ * @build ValidateModulesTest JarUtils jdk.testlibrary.*
+ * @run testng ValidateModulesTest
+ * @summary Basic test for java --validate-modules
+ */
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+import jdk.testlibrary.ProcessTools;
+import jdk.testlibrary.OutputAnalyzer;
+
+import org.testng.annotations.Test;
+import static org.testng.Assert.*;
+
+@Test
+public class ValidateModulesTest {
+
+    /**
+     * Test that the system modules validate.
+     */
+    public void testSystemModules() throws Exception {
+        run("--validate-modules")
+                .stdoutShouldContain("java.base")
+                .stdoutShouldContain("java.xml")
+                .shouldHaveExitValue(0);
+    }
+
+    /**
+     * Test an automatic module on the module path with classes in the same
+     * package as a system module.
+     */
+    public void testPackageConflict() throws Exception {
+        Path tmpdir = Files.createTempDirectory("tmp");
+
+        Path classes = Files.createDirectory(tmpdir.resolve("classes"));
+        touch(classes, "javax/xml/XMLConstants.class");
+        touch(classes, "javax/xml/parsers/SAXParser.class");
+
+        Path lib = Files.createDirectory(tmpdir.resolve("lib"));
+        JarUtils.createJarFile(lib.resolve("xml.jar"), classes);
+
+        int exitValue = run("-p", lib.toString(), "--validate-modules")
+                .shouldContain("xml automatic")
+                .shouldContain("conflicts with module java.xml")
+                .getExitValue();
+        assertTrue(exitValue != 0);
+
+    }
+
+    /**
+     * Test two modules with the same name in a directory.
+     */
+    public void testDuplicateModule() throws Exception {
+        Path tmpdir = Files.createTempDirectory("tmp");
+
+        Path classes = Files.createDirectory(tmpdir.resolve("classes"));
+        touch(classes, "org/foo/Bar.class");
+
+        Path lib = Files.createDirectory(tmpdir.resolve("lib"));
+        JarUtils.createJarFile(lib.resolve("foo-1.0.jar"), classes);
+        JarUtils.createJarFile(lib.resolve("foo-2.0.jar"), classes);
+
+        int exitValue = run("-p", lib.toString(), "--validate-modules")
+                .shouldContain("contains same module")
+                .getExitValue();
+        assertTrue(exitValue != 0);
+    }
+
+    /**
+     * Test two modules with the same name in different directories.
+     */
+    public void testShadowed() throws Exception {
+        Path tmpdir = Files.createTempDirectory("tmp");
+
+        Path classes = Files.createDirectory(tmpdir.resolve("classes"));
+        touch(classes, "org/foo/Bar.class");
+
+        Path lib1 = Files.createDirectory(tmpdir.resolve("lib1"));
+        JarUtils.createJarFile(lib1.resolve("foo-1.0.jar"), classes);
+
+        Path lib2 = Files.createDirectory(tmpdir.resolve("lib2"));
+        JarUtils.createJarFile(lib2.resolve("foo-2.0.jar"), classes);
+
+        run("-p", lib1 + File.pathSeparator + lib2, "--validate-modules")
+                .shouldContain("shadowed by")
+                .shouldHaveExitValue(0);
+    }
+
+    /**
+     * Runs the java launcher with the given arguments.
+     */
+    private OutputAnalyzer run(String... args) throws Exception {
+        return ProcessTools.executeTestJava(args)
+                .outputTo(System.out)
+                .errorTo(System.out);
+    }
+
+    /**
+     * Creates a file relative the given directory.
+     */
+    private void touch(Path dir, String relPath) throws IOException {
+        Path file = dir.resolve(relPath.replace('/', File.separatorChar));
+        Files.createDirectories(file.getParent());
+        Files.createFile(file);
+    }
+}
--- a/jdk/test/tools/pack200/pack200-verifier/src/xmlkit/ClassReader.java	Mon Apr 24 13:43:34 2017 +0800
+++ b/jdk/test/tools/pack200/pack200-verifier/src/xmlkit/ClassReader.java	Thu May 04 07:26:55 2017 +0000
@@ -1542,8 +1542,7 @@
     @Override
     public Element visitModuleTarget(ModuleTarget_attribute attr, Element p) {
         Element e = new Element(x.getCpString(attr.attribute_name_index));
-        e.add(x.getCpString(attr.os_name_index));
-        e.add(x.getCpString(attr.os_arch_index));
+        e.add(x.getCpString(attr.target_platform_index));
         e.trimToSize();
         p.add(e);
         return null;