changeset 19571:2453f243bcb9

Merge
author alanb
date Wed, 26 Apr 2017 14:11:27 +0100
parents 750ed72c847b 24ff045e949f
children a3eb50edd55f
files
diffstat 28 files changed, 848 insertions(+), 192 deletions(-) [+]
line wrap: on
line diff
--- a/src/java.base/share/classes/java/lang/Module.java	Thu Apr 20 08:00:35 2017 -0700
+++ b/src/java.base/share/classes/java/lang/Module.java	Wed Apr 26 14:11:27 2017 +0100
@@ -1469,7 +1469,7 @@
      *     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 the caller's module. If the resource is not in a
+     *     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>
      *
--- a/src/java.base/share/classes/java/lang/SecurityManager.java	Thu Apr 20 08:00:35 2017 -0700
+++ b/src/java.base/share/classes/java/lang/SecurityManager.java	Wed Apr 26 14:11:27 2017 +0100
@@ -42,9 +42,11 @@
 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 jdk.internal.module.ModuleBootstrap;
@@ -1435,20 +1437,27 @@
     // 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 Set<String> nonExportedPkgs;
+    private static final Map<String, Boolean> nonExportedPkgs = new ConcurrentHashMap<>();
+    static {
+        addNonExportedPackages(ModuleLayer.boot());
+    }
 
-    static {
+    /**
+     * 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();
-        nonExportedPkgs = ModuleBootstrap.unlimitedFinder().findAll().stream()
-                .map(ModuleReference::descriptor)
+        layer.modules().stream()
+                .map(Module::getDescriptor)
                 .filter(md -> bootModules.contains(md.name())
-                              || platformModules.contains(md.name()))
+                        || platformModules.contains(md.name()))
                 .map(SecurityManager::nonExportedPkgs)
                 .flatMap(Set::stream)
-                .collect(Collectors.toSet());
+                .forEach(pn -> nonExportedPkgs.put(pn, Boolean.TRUE));
     }
 
+
     /**
      * Called by java.security.Security
      */
@@ -1521,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;
@@ -1620,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/src/java.base/share/classes/java/lang/System.java	Thu Apr 20 08:00:35 2017 -0700
+++ b/src/java.base/share/classes/java/lang/System.java	Wed Apr 26 14:11:27 2017 +0100
@@ -2119,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/src/java.base/share/classes/java/lang/invoke/MethodHandles.java	Thu Apr 20 08:00:35 2017 -0700
+++ b/src/java.base/share/classes/java/lang/invoke/MethodHandles.java	Wed Apr 26 14:11:27 2017 +0100
@@ -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/src/java.base/share/classes/java/lang/module/ModuleFinder.java	Thu Apr 20 08:00:35 2017 -0700
+++ b/src/java.base/share/classes/java/lang/module/ModuleFinder.java	Wed Apr 26 14:11:27 2017 +0100
@@ -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/src/java.base/share/classes/java/lang/module/Resolver.java	Thu Apr 20 08:00:35 2017 -0700
+++ b/src/java.base/share/classes/java/lang/module/Resolver.java	Wed Apr 26 14:11:27 2017 +0100
@@ -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;
@@ -147,8 +148,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 +180,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 +211,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 +281,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 +295,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);
                                     }
@@ -412,12 +407,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();
@@ -964,9 +953,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/src/java.base/share/classes/java/nio/file/FileSystems.java	Thu Apr 20 08:00:35 2017 -0700
+++ b/src/java.base/share/classes/java/nio/file/FileSystems.java	Wed Apr 26 14:11:27 2017 +0100
@@ -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 = VM.getSavedProperty(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/src/java.base/share/classes/jdk/internal/misc/JavaLangAccess.java	Thu Apr 20 08:00:35 2017 -0700
+++ b/src/java.base/share/classes/jdk/internal/misc/JavaLangAccess.java	Wed Apr 26 14:11:27 2017 +0100
@@ -170,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/src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java	Thu Apr 20 08:00:35 2017 -0700
+++ b/src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java	Wed Apr 26 14:11:27 2017 +0100
@@ -144,6 +144,10 @@
 
         PerfCounters.defineBaseTime.addElapsedTimeFrom(t1);
 
+        // special mode to boot with only java.base, ignores other options
+        if (System.getProperty("jdk.module.minimumBoot") != null) {
+            return createMinimalBootLayer();
+        }
 
         long t2 = System.nanoTime();
 
@@ -201,7 +205,6 @@
         }
         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
         // is implemented by resolving "java.se" and all (non-java.*) modules
@@ -280,7 +283,7 @@
         }
 
         PrintStream traceOutput = null;
-        if (Boolean.getBoolean("jdk.launcher.traceResolver"))
+        if (Boolean.getBoolean("jdk.module.showModuleResolution"))
             traceOutput = System.out;
 
         // run the resolver to create the configuration
@@ -379,6 +382,20 @@
     }
 
     /**
+     * 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);
+    }
+
+    /**
      * Returns a ModuleFinder that limits observability to the given root
      * modules, their transitive dependences, plus a set of other modules.
      */
--- a/src/java.base/share/classes/jdk/internal/module/ModulePatcher.java	Thu Apr 20 08:00:35 2017 -0700
+++ b/src/java.base/share/classes/jdk/internal/module/ModulePatcher.java	Wed Apr 26 14:11:27 2017 +0100
@@ -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();
         }
 
--- a/src/java.base/share/classes/jdk/internal/module/ModulePath.java	Thu Apr 20 08:00:35 2017 -0700
+++ b/src/java.base/share/classes/jdk/internal/module/ModulePath.java	Wed Apr 26 14:11:27 2017 +0100
@@ -435,7 +435,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
@@ -531,12 +531,10 @@
             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);
             }
         }
 
--- a/src/java.base/share/classes/jdk/internal/module/ModuleReferences.java	Thu Apr 20 08:00:35 2017 -0700
+++ b/src/java.base/share/classes/jdk/internal/module/ModuleReferences.java	Wed Apr 26 14:11:27 2017 +0100
@@ -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/src/java.base/share/classes/jdk/internal/module/Modules.java	Thu Apr 20 08:00:35 2017 -0700
+++ b/src/java.base/share/classes/jdk/internal/module/Modules.java	Wed Apr 26 14:11:27 2017 +0100
@@ -223,6 +223,9 @@
             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();
--- a/src/java.base/share/classes/jdk/internal/module/Resources.java	Thu Apr 20 08:00:35 2017 -0700
+++ b/src/java.base/share/classes/jdk/internal/module/Resources.java	Wed Apr 26 14:11:27 2017 +0100
@@ -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/src/java.base/share/classes/sun/launcher/LauncherHelper.java	Thu Apr 20 08:00:35 2017 -0700
+++ b/src/java.base/share/classes/sun/launcher/LauncherHelper.java	Wed Apr 26 14:11:27 2017 +0100
@@ -44,6 +44,7 @@
 import java.io.PrintStream;
 import java.io.UnsupportedEncodingException;
 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;
@@ -61,17 +62,21 @@
 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.HashMap;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Locale;
 import java.util.Locale.Category;
+import java.util.Map;
 import java.util.Optional;
 import java.util.Properties;
 import java.util.ResourceBundle;
@@ -935,18 +940,18 @@
     /**
      * Called by the launcher to list the observable modules.
      */
-    static void listAllModules(boolean printToStderr) {
+    static void listModules(boolean printToStderr) {
         initOutput(printToStderr);
 
         ModuleBootstrap.limitedFinder().findAll().stream()
             .sorted(new JrtFirstComparator())
-            .forEach(LauncherHelper::listModule);
+            .forEach(LauncherHelper::showModule);
     }
 
     /**
-     * Called by the launcher to list the resolved modules
+     * Called by the launcher to show the resolved modules
      */
-    static void listResolvedModules(boolean printToStderr) {
+    static void showResolvedModules(boolean printToStderr) {
         initOutput(printToStderr);
 
         ModuleLayer bootLayer = ModuleLayer.boot();
@@ -957,7 +962,7 @@
             .flatMap(Optional::stream)
             .map(ResolvedModule::reference)
             .sorted(new JrtFirstComparator())
-            .forEach(LauncherHelper::listModule);
+            .forEach(LauncherHelper::showModule);
     }
 
     /**
@@ -974,14 +979,14 @@
         ModuleDescriptor md = mref.descriptor();
 
         // one-line summary
-        listModule(mref);
+        showModule(mref);
 
         // unqualified exports (sorted by package)
         md.exports().stream()
             .filter(e -> !e.isQualified())
             .sorted(Comparator.comparing(Exports::source))
             .map(e -> Stream.concat(Stream.of(e.source()),
-                                    toStringStream(e.modifiers()))
+                    toStringStream(e.modifiers()))
                     .collect(Collectors.joining(" ")))
             .forEach(sourceAndMods -> ostream.format("exports %s%n", sourceAndMods));
 
@@ -1041,7 +1046,7 @@
     /**
      * Prints a single line with the module name, version and modifiers
      */
-    private static void listModule(ModuleReference mref) {
+    private static void showModule(ModuleReference mref) {
         ModuleDescriptor md = mref.descriptor();
         ostream.print(md.toNameAndVersion());
         mref.location()
@@ -1086,4 +1091,198 @@
     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(boolean printToStderr) {
+        initOutput(printToStderr);
+
+        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/src/java.base/share/classes/sun/launcher/resources/launcher.properties	Thu Apr 20 08:00:35 2017 -0700
+++ b/src/java.base/share/classes/sun/launcher/resources/launcher.properties	Wed Apr 26 14:11:27 2017 +0100
@@ -60,22 +60,28 @@
 \    --limit-modules <module name>[,<module name>...]\n\
 \                  limit the universe of observable modules\n\
 \    --list-modules\n\
-\                  list the observable modules and exit\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\
+\                  scan observable modules and exit\n\
+\                  The --validate-modules option may be is 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\
@@ -122,7 +128,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\
--- a/src/java.base/share/native/libjli/java.c	Thu Apr 20 08:00:35 2017 -0700
+++ b/src/java.base/share/native/libjli/java.c	Wed Apr 26 14:11:27 2017 +0100
@@ -71,9 +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 jboolean listAllModules = JNI_FALSE;
-static jboolean listResolvedModules = JNI_FALSE;
+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;
@@ -120,9 +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 ListAllModules(JNIEnv* env);
-static void ListResolvedModules(JNIEnv* env);
+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);
 
@@ -413,19 +415,19 @@
         CHECK_EXCEPTION_LEAVE(1);
     }
 
+    // show resolved modules and continue
+    if (showResolvedModules) {
+        ShowResolvedModules(env);
+        CHECK_EXCEPTION_LEAVE(1);
+    }
+
     // list observable modules, then exit
-    if (listAllModules) {
-        ListAllModules(env);
+    if (listModules) {
+        ListModules(env);
         CHECK_EXCEPTION_LEAVE(1);
         LEAVE();
     }
 
-    // list resolved modules and continue
-    if (listResolvedModules) {
-        ListResolvedModules(env);
-        CHECK_EXCEPTION_LEAVE(1);
-    }
-
     // describe a module, then exit
     if (describeModule != NULL) {
         DescribeModule(env, describeModule);
@@ -433,6 +435,14 @@
         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);
@@ -1283,10 +1293,14 @@
             SetClassPath(value);
             mode = LM_CLASS;
         } else if (JLI_StrCmp(arg, "--list-modules") == 0) {
-            listAllModules = JNI_TRUE;
+            listModules = JNI_TRUE;
             return JNI_TRUE;
-        } else if (JLI_StrCmp(arg, "--list-resolved-modules") == 0) {
-            listResolvedModules = 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;
+            return JNI_TRUE;
         } else if (JLI_StrCmp(arg, "--describe-module") == 0 ||
                    JLI_StrCCmp(arg, "--describe-module=") == 0 ||
                    JLI_StrCmp(arg, "-d") == 0) {
@@ -1354,9 +1368,9 @@
             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, "-Xdiag:resolver") == 0 ||
+                   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.
@@ -1846,31 +1860,31 @@
 }
 
 /**
+ * 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", "(Z)V"));
+    (*env)->CallStaticVoidMethod(env, cls, showResolvedModulesID, USE_STDOUT);
+}
+
+/**
  * List observable modules
  */
 static void
-ListAllModules(JNIEnv *env)
+ListModules(JNIEnv *env)
 {
-    jmethodID listAllModulesID;
+    jmethodID listModulesID;
     jclass cls = GetLauncherHelperClass(env);
     NULL_CHECK(cls);
-    NULL_CHECK(listAllModulesID = (*env)->GetStaticMethodID(env, cls,
-            "listAllModules", "(Z)V"));
-    (*env)->CallStaticVoidMethod(env, cls, listAllModulesID, USE_STDOUT);
-}
-
-/**
- * List resolved modules
- */
-static void
-ListResolvedModules(JNIEnv *env)
-{
-    jmethodID listResolvedModulesID;
-    jclass cls = GetLauncherHelperClass(env);
-    NULL_CHECK(cls);
-    NULL_CHECK(listResolvedModulesID = (*env)->GetStaticMethodID(env, cls,
-            "listResolvedModules", "(Z)V"));
-    (*env)->CallStaticVoidMethod(env, cls, listResolvedModulesID, USE_STDOUT);
+    NULL_CHECK(listModulesID = (*env)->GetStaticMethodID(env, cls,
+            "listModules", "(Z)V"));
+    (*env)->CallStaticVoidMethod(env, cls, listModulesID, USE_STDOUT);
 }
 
 /**
@@ -1891,6 +1905,20 @@
                                  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)Z");
+    NULL_CHECK_RETURN_VALUE(cls, JNI_FALSE);
+    return (*env)->CallStaticBooleanMethod(env, cls, validateModulesID, USE_STDOUT);
+}
+
 /*
  * Prints default usage or the Xusage message, see sun.launcher.LauncherHelper.java
  */
--- a/src/java.instrument/share/classes/java/lang/instrument/Instrumentation.java	Thu Apr 20 08:00:35 2017 -0700
+++ b/src/java.instrument/share/classes/java/lang/instrument/Instrumentation.java	Wed Apr 26 14:11:27 2017 +0100
@@ -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/src/java.instrument/share/classes/java/lang/instrument/package.html	Thu Apr 20 08:00:35 2017 -0700
+++ b/src/java.instrument/share/classes/java/lang/instrument/package.html	Wed Apr 26 14:11:27 2017 +0100
@@ -128,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>
@@ -193,21 +192,73 @@
 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.
+            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.
+            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
@@ -217,8 +268,14 @@
             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>			
-
+</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
@@ -236,9 +293,9 @@
             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.
+            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>
--- a/src/java.instrument/share/native/libinstrument/InvocationAdapter.c	Thu Apr 20 08:00:35 2017 -0700
+++ b/src/java.instrument/share/native/libinstrument/InvocationAdapter.c	Wed Apr 26 14:11:27 2017 +0100
@@ -446,9 +446,9 @@
 jint loadAgent(JNIEnv* env, jstring path) {
     JavaVM* vm;
     JPLISAgent* agent;
-    const char* jarfile;
-    jarAttribute* attributes;
-    char* agentClass;
+    const char* jarfile = NULL;
+    jarAttribute* attributes = NULL;
+    char* agentClass = NULL;
     char* bootClassPath;
     int oldLen, newLen;
     jint result = JNI_ERR;
@@ -526,7 +526,15 @@
     result = JNI_OK;
 
     releaseAndReturn:
-        (*env)->ReleaseStringUTFChars(env, path, jarfile);
+        if (agentClass != NULL) {
+            free(agentClass);
+        }
+        if (attributes != NULL) {
+            freeAttributes(attributes);
+        }
+        if (jarfile != NULL) {
+            (*env)->ReleaseStringUTFChars(env, path, jarfile);
+        }
 
     return result;
 }
--- a/src/jdk.attach/share/classes/sun/tools/attach/HotSpotVirtualMachine.java	Thu Apr 20 08:00:35 2017 -0700
+++ b/src/jdk.attach/share/classes/sun/tools/attach/HotSpotVirtualMachine.java	Wed Apr 26 14:11:27 2017 +0100
@@ -54,7 +54,7 @@
         CURRENT_PID = AccessController.doPrivileged(pa).pid();
 
         String s = VM.getSavedProperty("jdk.attach.allowAttachSelf");
-        ALLOW_ATTACH_SELF = (s != null) && !"false".equals(s);
+        ALLOW_ATTACH_SELF = "".equals(s) || Boolean.parseBoolean(s);
     }
 
     HotSpotVirtualMachine(AttachProvider provider, String id)
--- a/test/java/lang/invoke/DefineClassTest.java	Thu Apr 20 08:00:35 2017 -0700
+++ b/test/java/lang/invoke/DefineClassTest.java	Wed Apr 26 14:11:27 2017 +0100
@@ -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/test/java/lang/module/AutomaticModulesTest.java	Thu Apr 20 08:00:35 2017 -0700
+++ b/test/java/lang/module/AutomaticModulesTest.java	Wed Apr 26 14:11:27 2017 +0100
@@ -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/test/java/nio/file/spi/SetDefaultProvider.java	Thu Apr 20 08:00:35 2017 -0700
+++ b/test/java/nio/file/spi/SetDefaultProvider.java	Wed Apr 26 14:11:27 2017 +0100
@@ -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();
 
-        Class<?> expected = Class.forName("TestProvider", false,
-            ClassLoader.getSystemClassLoader());
+    private static String SET_DEFAULT_FSP =
+        "-Djava.nio.file.spi.DefaultFileSystemProvider=TestProvider";
 
-        if (c != expected)
-            throw new RuntimeException();
+    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);
+    }
+
+    /**
+     * 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);
+    }
+
+    /**
+     * 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/test/java/nio/file/spi/TestProvider.java	Thu Apr 20 08:00:35 2017 -0700
+++ b/test/java/nio/file/spi/TestProvider.java	Wed Apr 26 14:11:27 2017 +0100
@@ -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/test/java/nio/file/spi/m/module-info.java	Wed Apr 26 14:11:27 2017 +0100
@@ -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/test/java/nio/file/spi/m/p/Main.java	Wed Apr 26 14:11:27 2017 +0100
@@ -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);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/launcher/modules/validate/ValidateModulesTest.java	Wed Apr 26 14:11:27 2017 +0100
@@ -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);
+    }
+}