changeset 16389:93a9053691a4

Merge
author prr
date Mon, 19 Dec 2016 15:33:32 -0800
parents 59f845ce3f51 ec574eb3aee4
children eef0714323de
files test/java/util/spi/ResourceBundleControlProvider/UserDefaultControlTest.java test/java/util/spi/ResourceBundleControlProvider/UserDefaultControlTest.sh test/java/util/spi/ResourceBundleControlProvider/providersrc/Makefile test/java/util/spi/ResourceBundleControlProvider/providersrc/UserControlProvider.java test/java/util/spi/ResourceBundleControlProvider/providersrc/UserXMLControl.java test/java/util/spi/ResourceBundleControlProvider/providersrc/XmlRB.xml test/java/util/spi/ResourceBundleControlProvider/providersrc/XmlRB_ja.xml test/java/util/spi/ResourceBundleControlProvider/providersrc/java.util.spi.ResourceBundleControlProvider test/java/util/spi/ResourceBundleControlProvider/rbcontrolprovider.jar
diffstat 31 files changed, 1386 insertions(+), 491 deletions(-) [+]
line wrap: on
line diff
--- a/src/java.base/share/classes/java/lang/reflect/Layer.java	Mon Dec 19 09:16:40 2016 -0800
+++ b/src/java.base/share/classes/java/lang/reflect/Layer.java	Mon Dec 19 15:33:32 2016 -0800
@@ -602,12 +602,8 @@
 
         checkGetClassLoaderPermission();
 
-        // For now, no two modules in the boot Layer may contain the same
-        // package so we use a simple check for the boot Layer to keep
-        // the overhead at startup to a minimum
-        if (boot() == null) {
-            checkBootModulesForDuplicatePkgs(cf);
-        } else {
+        // The boot layer is checked during module system initialization
+        if (boot() != null) {
             checkForDuplicatePkgs(cf, clf);
         }
 
@@ -657,27 +653,6 @@
     }
 
     /**
-     * Checks a configuration for the boot Layer to ensure that no two modules
-     * have the same package.
-     *
-     * @throws LayerInstantiationException
-     */
-    private static void checkBootModulesForDuplicatePkgs(Configuration cf) {
-        Map<String, String> packageToModule = new HashMap<>();
-        for (ResolvedModule resolvedModule : cf.modules()) {
-            ModuleDescriptor descriptor = resolvedModule.reference().descriptor();
-            String name = descriptor.name();
-            for (String p : descriptor.packages()) {
-                String other = packageToModule.putIfAbsent(p, name);
-                if (other != null) {
-                    throw fail("Package " + p + " in both module "
-                               + name + " and module " + other);
-                }
-            }
-        }
-    }
-
-    /**
      * Checks a configuration and the module-to-loader mapping to ensure that
      * no two modules mapped to the same class loader have the same package.
      * It also checks that no two automatic modules have the same package.
--- a/src/java.base/share/classes/java/util/ResourceBundle.java	Mon Dec 19 09:16:40 2016 -0800
+++ b/src/java.base/share/classes/java/util/ResourceBundle.java	Mon Dec 19 15:33:32 2016 -0800
@@ -60,7 +60,6 @@
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 import java.util.jar.JarEntry;
-import java.util.spi.ResourceBundleControlProvider;
 import java.util.spi.ResourceBundleProvider;
 
 import jdk.internal.loader.BootLoader;
@@ -232,8 +231,6 @@
  * <li>{@code ResourceBundle.Control} is <em>not</em> supported in named modules.
  * If the {@code getBundle} method with a {@code ResourceBundle.Control} is called
  * in a named module, the method will throw an {@code UnsupportedOperationException}.
- * Any service providers of {@link ResourceBundleControlProvider} are ignored in
- * named modules.
  * </li>
  * </ul>
  *
@@ -264,17 +261,6 @@
  * {@link #getBundle(String, Locale, ClassLoader, Control) getBundle}
  * factory method for details.
  *
- * <p><a name="modify_default_behavior">For the {@code getBundle} factory</a>
- * methods that take no {@link Control} instance, their <a
- * href="#default_behavior"> default behavior</a> of resource bundle loading
- * can be modified with <em>installed</em> {@link
- * ResourceBundleControlProvider} implementations. Any installed providers are
- * detected at the {@code ResourceBundle} class loading time. If any of the
- * providers provides a {@link Control} for the given base name, that {@link
- * Control} will be used instead of the default {@link Control}. If there is
- * more than one service provider installed for supporting the same base name,
- * the first one returned from {@link ServiceLoader} will be used.
- *
  * <h3>Cache Management</h3>
  *
  * Resource bundle instances created by the <code>getBundle</code> factory
@@ -469,21 +455,6 @@
      */
     private volatile Set<String> keySet;
 
-    private static final List<ResourceBundleControlProvider> providers;
-
-    static {
-        List<ResourceBundleControlProvider> list = null;
-        ServiceLoader<ResourceBundleControlProvider> serviceLoaders
-                = ServiceLoader.loadInstalled(ResourceBundleControlProvider.class);
-        for (ResourceBundleControlProvider provider : serviceLoaders) {
-            if (list == null) {
-                list = new ArrayList<>();
-            }
-            list.add(provider);
-        }
-        providers = list;
-    }
-
     /**
      * Sole constructor.  (For invocation by subclass constructors, typically
      * implicit.)
@@ -948,7 +919,7 @@
     {
         Class<?> caller = Reflection.getCallerClass();
         return getBundleImpl(baseName, Locale.getDefault(),
-                             caller, getDefaultControl(caller, baseName));
+                             caller, Control.INSTANCE);
     }
 
     /**
@@ -1022,7 +993,7 @@
     {
         Class<?> caller = Reflection.getCallerClass();
         return getBundleImpl(baseName, locale,
-                             caller, getDefaultControl(caller, baseName));
+                             caller, Control.INSTANCE);
     }
 
     /**
@@ -1163,10 +1134,7 @@
      *
      * <p>This method behaves the same as calling
      * {@link #getBundle(String, Locale, ClassLoader, Control)} passing a
-     * default instance of {@link Control} unless another {@link Control} is
-     * provided with the {@link ResourceBundleControlProvider} SPI. Refer to the
-     * description of <a href="#modify_default_behavior">modifying the default
-     * behavior</a>.
+     * default instance of {@link Control}.
      *
      * <p><a name="default_behavior">The following describes the default
      * behavior</a>.
@@ -1364,7 +1332,7 @@
             throw new NullPointerException();
         }
         Class<?> caller = Reflection.getCallerClass();
-        return getBundleImpl(baseName, locale, caller, loader, getDefaultControl(caller, baseName));
+        return getBundleImpl(baseName, locale, caller, loader, Control.INSTANCE);
     }
 
     /**
@@ -1589,18 +1557,6 @@
         return getBundleImpl(baseName, targetLocale, caller, loader, control);
     }
 
-    private static Control getDefaultControl(Class<?> caller, String baseName) {
-        if (providers != null && !caller.getModule().isNamed()) {
-            for (ResourceBundleControlProvider provider : providers) {
-                Control control = provider.getControl(baseName);
-                if (control != null) {
-                    return control;
-                }
-            }
-        }
-        return Control.INSTANCE;
-    }
-
     private static void checkNamedModule(Class<?> caller) {
         if (caller.getModule().isNamed()) {
             throw new UnsupportedOperationException(
@@ -2573,8 +2529,7 @@
      * @apiNote <a name="note">{@code ResourceBundle.Control} is not supported
      * in named modules.</a> If the {@code ResourceBundle.getBundle} method with
      * a {@code ResourceBundle.Control} is called in a named module, the method
-     * will throw an {@link UnsupportedOperationException}. Any service providers
-     * of {@link ResourceBundleControlProvider} are ignored in named modules.
+     * will throw an {@link UnsupportedOperationException}.
      *
      * @since 1.6
      * @see java.util.spi.ResourceBundleProvider
--- a/src/java.base/share/classes/java/util/spi/ResourceBundleControlProvider.java	Mon Dec 19 09:16:40 2016 -0800
+++ b/src/java.base/share/classes/java/util/spi/ResourceBundleControlProvider.java	Mon Dec 19 15:33:32 2016 -0800
@@ -35,21 +35,19 @@
  * no {@link java.util.ResourceBundle.Control} instance can be modified with {@code
  * ResourceBundleControlProvider} implementations.
  *
- * <p>Provider implementations must be packaged using the <a
- * href="../../../../technotes/guides/extensions/index.html">Java Extension
- * Mechanism</a> as installed extensions. Refer to {@link java.util.ServiceLoader}
- * for the extension packaging. Any installed {@code
- * ResourceBundleControlProvider} implementations are loaded using {@link
- * java.util.ServiceLoader} at the {@code ResourceBundle} class loading time.
- *
- * <p>All {@code ResourceBundleControlProvider}s are ignored in named modules.
- *
  * @author Masayoshi Okutsu
  * @since 1.8
  * @see ResourceBundle#getBundle(String, java.util.Locale, ClassLoader, ResourceBundle.Control)
  *      ResourceBundle.getBundle
  * @see java.util.ServiceLoader#loadInstalled(Class)
+ * @deprecated There is no longer any mechanism to install a custom
+ * {@code ResourceBundleControlProvider} implementation defined
+ * by the platform class loader or its ancestor. The recommended
+ * way to use a custom {@code Control} implementation to load resource bundle
+ * is to use {@link java.util.ResourceBundle#getBundle(String, Control)}
+ * or other factory methods that take custom {@link java.util.ResourceBundle.Control}.
  */
+@Deprecated(since="9", forRemoval=true)
 public interface ResourceBundleControlProvider {
     /**
      * Returns a {@code ResourceBundle.Control} instance that is used
--- a/src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java	Mon Dec 19 09:16:40 2016 -0800
+++ b/src/java.base/share/classes/jdk/internal/module/ModuleBootstrap.java	Mon Dec 19 15:33:32 2016 -0800
@@ -306,8 +306,32 @@
                         fail(name + ": cannot be loaded from application module path");
                 }
             }
+
+            // check if module specified in --patch-module is present
+            for (String mn: patcher.patchedModules()) {
+                if (!cf.findModule(mn).isPresent()) {
+                    warnUnknownModule(PATCH_MODULE, mn);
+                }
+            }
         }
 
+        // if needed check that there are no split packages in the set of
+        // resolved modules for the boot layer
+        if (SystemModules.hasSplitPackages() || needPostResolutionChecks) {
+                Map<String, String> packageToModule = new HashMap<>();
+                for (ResolvedModule resolvedModule : cf.modules()) {
+                    ModuleDescriptor descriptor =
+                        resolvedModule.reference().descriptor();
+                    String name = descriptor.name();
+                    for (String p : descriptor.packages()) {
+                        String other = packageToModule.putIfAbsent(p, name);
+                        if (other != null) {
+                            fail("Package " + p + " in both module "
+                                 + name + " and module " + other);
+                        }
+                    }
+                }
+            }
 
         long t4 = System.nanoTime();
 
@@ -464,7 +488,7 @@
             String mn = e.getKey();
             Optional<Module> om = bootLayer.findModule(mn);
             if (!om.isPresent()) {
-                warn("Unknown module: " + mn);
+                warnUnknownModule(ADD_READS, mn);
                 continue;
             }
             Module m = om.get();
@@ -478,7 +502,7 @@
                     if (om.isPresent()) {
                         Modules.addReads(m, om.get());
                     } else {
-                        warn("Unknown module: " + name);
+                        warnUnknownModule(ADD_READS, name);
                     }
                 }
             }
@@ -510,24 +534,25 @@
                                                Map<String, List<String>> map,
                                                boolean opens)
     {
+        String option = opens ? ADD_OPENS : ADD_EXPORTS;
         for (Map.Entry<String, List<String>> e : map.entrySet()) {
 
             // the key is $MODULE/$PACKAGE
             String key = e.getKey();
             String[] s = key.split("/");
             if (s.length != 2)
-                fail("Unable to parse as <module>/<package>: " + key);
+                fail(unableToParse(option,  "<module>/<package>", key));
 
             String mn = s[0];
             String pn = s[1];
             if (mn.isEmpty() || pn.isEmpty())
-                fail("Module and package name must be specified: " + key);
+                fail(unableToParse(option,  "<module>/<package>", key));
 
             // The exporting module is in the boot layer
             Module m;
             Optional<Module> om = bootLayer.findModule(mn);
             if (!om.isPresent()) {
-                warn("Unknown module: " + mn);
+                warnUnknownModule(option, mn);
                 continue;
             }
 
@@ -549,7 +574,7 @@
                     if (om.isPresent()) {
                         other = om.get();
                     } else {
-                        warn("Unknown module: " + name);
+                        warnUnknownModule(option, name);
                         continue;
                     }
                 }
@@ -593,24 +618,30 @@
 
             int pos = value.indexOf('=');
             if (pos == -1)
-                fail("Unable to parse as <module>=<value>: " + value);
+                fail(unableToParse(option(prefix), "<module>=<value>", value));
             if (pos == 0)
-                fail("Missing module name in: " + value);
+                fail(unableToParse(option(prefix), "<module>=<value>", value));
 
             // key is <module> or <module>/<package>
             String key = value.substring(0, pos);
 
             String rhs = value.substring(pos+1);
             if (rhs.isEmpty())
-                fail("Unable to parse as <module>=<value>: " + value);
+                fail(unableToParse(option(prefix), "<module>=<value>", value));
 
             // value is <module>(,<module>)* or <file>(<pathsep><file>)*
             if (!allowDuplicates && map.containsKey(key))
-                fail(key + " specified more than once");
+                fail(key + " specified more than once in " + option(prefix));
             List<String> values = map.computeIfAbsent(key, k -> new ArrayList<>());
+            int ntargets = 0;
             for (String s : rhs.split(regex)) {
-                if (s.length() > 0) values.add(s);
+                if (s.length() > 0) {
+                    values.add(s);
+                    ntargets++;
+                }
             }
+            if (ntargets == 0)
+                fail("Target must be specified: " + option(prefix) + " " + value);
 
             index++;
             value = getAndRemoveProperty(prefix + index);
@@ -672,6 +703,42 @@
         System.err.println("WARNING: " + m);
     }
 
+    static void warnUnknownModule(String option, String mn) {
+        warn("Unknown module: " + mn + " specified in " + option);
+    }
+
+    static String unableToParse(String option, String text, String value) {
+        return "Unable to parse " +  option + " " + text + ": " + value;
+    }
+
+    private static final String ADD_MODULES  = "--add-modules";
+    private static final String ADD_EXPORTS  = "--add-exports";
+    private static final String ADD_OPENS    = "--add-opens";
+    private static final String ADD_READS    = "--add-reads";
+    private static final String PATCH_MODULE = "--patch-module";
+
+
+    /*
+     * Returns the command-line option name corresponds to the specified
+     * system property prefix.
+     */
+    static String option(String prefix) {
+        switch (prefix) {
+            case "jdk.module.addexports.":
+                return ADD_EXPORTS;
+            case "jdk.module.addopens.":
+                return ADD_OPENS;
+            case "jdk.module.addreads.":
+                return ADD_READS;
+            case "jdk.module.patch.":
+                return PATCH_MODULE;
+            case "jdk.module.addmods.":
+                return ADD_MODULES;
+            default:
+                throw new IllegalArgumentException(prefix);
+        }
+    }
+
     static class PerfCounters {
 
         static PerfCounter systemModulesTime
--- a/src/java.base/share/classes/jdk/internal/module/ModulePatcher.java	Mon Dec 19 09:16:40 2016 -0800
+++ b/src/java.base/share/classes/jdk/internal/module/ModulePatcher.java	Mon Dec 19 15:33:32 2016 -0800
@@ -175,6 +175,12 @@
         return map.isEmpty();
     }
 
+    /*
+     * Returns the names of the patched modules.
+     */
+    Set<String> patchedModules() {
+        return map.keySet();
+    }
 
     /**
      * A ModuleReader that reads resources from a patched module.
--- a/src/java.base/share/classes/jdk/internal/module/SystemModules.java	Mon Dec 19 09:16:40 2016 -0800
+++ b/src/java.base/share/classes/jdk/internal/module/SystemModules.java	Mon Dec 19 15:33:32 2016 -0800
@@ -57,6 +57,14 @@
     public static int PACKAGES_IN_BOOT_LAYER = 1024;
 
     /**
+     * @return {@code false} if there are no split packages in the run-time
+     *         image, {@code true} if there are or if it's not been checked.
+     */
+    public static boolean hasSplitPackages() {
+        return true;
+    }
+
+    /**
      * Returns a non-empty array of ModuleDescriptors in the run-time image.
      *
      * When running an exploded image it returns an empty array.
--- a/src/java.base/share/native/libjli/java.c	Mon Dec 19 09:16:40 2016 -0800
+++ b/src/java.base/share/native/libjli/java.c	Mon Dec 19 15:33:32 2016 -0800
@@ -573,6 +573,17 @@
            JLI_StrCmp(name, "--patch-module") == 0;
 }
 
+static jboolean
+IsLongFormModuleOption(const char* name) {
+    return JLI_StrCCmp(name, "--module-path=") == 0 ||
+           JLI_StrCCmp(name, "--upgrade-module-path=") == 0 ||
+           JLI_StrCCmp(name, "--add-modules=") == 0 ||
+           JLI_StrCCmp(name, "--limit-modules=") == 0 ||
+           JLI_StrCCmp(name, "--add-exports=") == 0 ||
+           JLI_StrCCmp(name, "--add-reads=") == 0 ||
+           JLI_StrCCmp(name, "--patch-module=") == 0;
+}
+
 /*
  * Test if the given name has a white space option.
  */
@@ -1236,7 +1247,7 @@
         char *option = NULL;
         char *value = NULL;
         int kind = GetOpt(&argc, &argv, &option, &value);
-        jboolean has_arg = value != NULL;
+        jboolean has_arg = value != NULL && JLI_StrLen(value) > 0;
 
 /*
  * Option to set main entry point
@@ -1285,19 +1296,13 @@
 /*
  * Error missing argument
  */
-        } else if (!has_arg && IsWhiteSpaceOption(arg)) {
-            if (JLI_StrCmp(arg, "--module-path") == 0 ||
-                JLI_StrCmp(arg, "-p") == 0 ||
-                JLI_StrCmp(arg, "--upgrade-module-path") == 0) {
-                REPORT_ERROR (has_arg, ARG_ERROR4, arg);
-            } else if (JLI_StrCmp(arg, "--add-modules") == 0 ||
-                       JLI_StrCmp(arg, "--limit-modules") == 0 ||
-                       JLI_StrCmp(arg, "--add-exports") == 0 ||
-                       JLI_StrCmp(arg, "--add-opens") == 0 ||
-                       JLI_StrCmp(arg, "--add-reads") == 0 ||
-                       JLI_StrCmp(arg, "--patch-module") == 0) {
-                REPORT_ERROR (has_arg, ARG_ERROR6, arg);
-            }
+        } else if (!has_arg && (JLI_StrCmp(arg, "--module-path") == 0 ||
+                                JLI_StrCmp(arg, "-p") == 0 ||
+                                JLI_StrCmp(arg, "--upgrade-module-path") == 0)) {
+            REPORT_ERROR (has_arg, ARG_ERROR4, arg);
+
+        } else if (!has_arg && (IsModuleOption(arg) || IsLongFormModuleOption(arg))) {
+            REPORT_ERROR (has_arg, ARG_ERROR6, arg);
 /*
  * The following cases will cause the argument parsing to stop
  */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/unix/conf/arm/jvm.cfg	Mon Dec 19 15:33:32 2016 -0800
@@ -0,0 +1,36 @@
+# Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# This code is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License version 2 only, as
+# published by the Free Software Foundation.  Oracle designates this
+# particular file as subject to the "Classpath" exception as provided
+# by Oracle in the LICENSE file that accompanied this code.
+#
+# This code is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+# version 2 for more details (a copy is included in the LICENSE file that
+# accompanied this code).
+#
+# You should have received a copy of the GNU General Public License version
+# 2 along with this work; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+# or visit www.oracle.com if you need additional information or have any
+# questions.
+#
+# 
+# List of JVMs that can be used as an option to java, javac, etc.
+# Order is important -- first in this list is the default JVM.
+# NOTE that this both this file and its format are UNSUPPORTED and
+# WILL GO AWAY in a future release.
+#
+# You may also select a JVM in an arbitrary location with the
+# "-XXaltjvm=<jvm_dir>" option, but that too is unsupported
+# and may not be available in a future release.
+#
+-server KNOWN
+-client KNOWN
+-minimal KNOWN
--- a/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/SystemModulesPlugin.java	Mon Dec 19 09:16:40 2016 -0800
+++ b/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/SystemModulesPlugin.java	Mon Dec 19 15:33:32 2016 -0800
@@ -34,6 +34,7 @@
 import java.util.Collection;
 import java.util.EnumSet;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -342,7 +343,8 @@
          *
          * static Map<String, ModuleDescriptor> map = new HashMap<>();
          */
-        private void clinit(int numModules, int numPackages) {
+        private void clinit(int numModules, int numPackages,
+                            boolean hasSplitPackages) {
             cw.visit(Opcodes.V1_8, ACC_PUBLIC+ACC_FINAL+ACC_SUPER, CLASSNAME,
                      null, "java/lang/Object", null);
 
@@ -379,6 +381,17 @@
             clinit.visitInsn(RETURN);
             clinit.visitMaxs(0, 0);
             clinit.visitEnd();
+
+            // public static boolean hasSplitPackages();
+            MethodVisitor split =
+                cw.visitMethod(ACC_PUBLIC+ACC_STATIC, "hasSplitPackages",
+                               "()Z", null, null);
+            split.visitCode();
+            split.visitInsn(hasSplitPackages ? ICONST_1 : ICONST_0);
+            split.visitInsn(IRETURN);
+            split.visitMaxs(0, 0);
+            split.visitEnd();
+
         }
 
         /*
@@ -416,12 +429,16 @@
          */
         public ClassWriter getClassWriter() {
             int numModules = moduleInfos.size();
-            int numPackages = 0;
+            Set<String> allPackages = new HashSet<>();
+            int packageCount = 0;
             for (ModuleInfo minfo : moduleInfos) {
-                numPackages += minfo.packages.size();
+                allPackages.addAll(minfo.packages);
+                packageCount += minfo.packages.size();
             }
 
-            clinit(numModules, numPackages);
+            int numPackages = allPackages.size();
+            boolean hasSplitPackages = (numPackages < packageCount);
+            clinit(numModules, numPackages, hasSplitPackages);
 
             // generate SystemModules::descriptors
             genDescriptorsMethod();
--- a/test/ProblemList.txt	Mon Dec 19 09:16:40 2016 -0800
+++ b/test/ProblemList.txt	Mon Dec 19 15:33:32 2016 -0800
@@ -292,8 +292,6 @@
 
 # jdk_util
 
-java/util/spi/ResourceBundleControlProvider/UserDefaultControlTest.java 8062512 generic-all
-
 java/util/BitSet/BitSetStreamTest.java                          8079538 generic-all
 
 
--- a/test/java/util/spi/ResourceBundleControlProvider/UserDefaultControlTest.java	Mon Dec 19 09:16:40 2016 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,59 +0,0 @@
-/*
- * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-/*
- * @test
- * @bug 6959653
- * @summary Test ResourceBundle.Control provided using SPI.
- * @build UserDefaultControlTest
- * @run shell UserDefaultControlTest.sh
- */
-
-import java.io.*;
-import java.net.*;
-import java.util.*;
-
-public class UserDefaultControlTest {
-    public static void main(String[] args) {
-        ResourceBundle rb = ResourceBundle.getBundle("com.foo.XmlRB", Locale.ROOT);
-        String type = rb.getString("type");
-        if (!type.equals("XML")) {
-            throw new RuntimeException("Root Locale: type: got " + type
-                                       + ", expected XML (ASCII)");
-        }
-
-        rb = ResourceBundle.getBundle("com.foo.XmlRB", Locale.JAPAN);
-        type = rb.getString("type");
-        // Expect fullwidth "XML"
-        if (!type.equals("\uff38\uff2d\uff2c")) {
-            throw new RuntimeException("Locale.JAPAN: type: got " + type
-                                       + ", expected \uff38\uff2d\uff2c (fullwidth XML)");
-        }
-
-        try {
-            rb = ResourceBundle.getBundle("com.bar.XmlRB", Locale.JAPAN);
-            throw new RuntimeException("com.bar.XmlRB test failed.");
-        } catch (MissingResourceException e) {
-            // OK
-        }
-    }
-}
--- a/test/java/util/spi/ResourceBundleControlProvider/UserDefaultControlTest.sh	Mon Dec 19 09:16:40 2016 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,25 +0,0 @@
-# 
-# Copyright (c) 2012, 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.
-#
-
-${TESTJAVA}/bin/java ${TESTVMOPTS} -Djava.ext.dirs=${TESTSRC} -cp ${TESTCLASSES} UserDefaultControlTest
-
--- a/test/java/util/spi/ResourceBundleControlProvider/providersrc/Makefile	Mon Dec 19 09:16:40 2016 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,63 +0,0 @@
-#
-# Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
-# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
-#
-# This code is free software; you can redistribute it and/or modify it
-# under the terms of the GNU General Public License version 2 only, as
-# published by the Free Software Foundation.  Oracle designates this
-# particular file as subject to the "Classpath" exception as provided
-# by Oracle in the LICENSE file that accompanied this code.
-#
-# This code is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
-# version 2 for more details (a copy is included in the LICENSE file that
-# accompanied this code).
-#
-# You should have received a copy of the GNU General Public License version
-# 2 along with this work; if not, write to the Free Software Foundation,
-# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
-#
-# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
-# or visit www.oracle.com if you need additional information or have any
-# questions.
-#
-
-#
-# Makefile for building a ResourceBundleControlProvider jar file for testing.
-#
-#    Usage: make JDK_HOME=... all install
-#
-
-DESTDIR = ..
-TMPDIR = tmp
-SERVICESDIR = $(TMPDIR)/META-INF/services
-TARGETJAR = rbcontrolprovider.jar
-BINDIR = $(JDK_HOME)/bin
-
-
-all: $(TARGETJAR)
-
-install: all
-	cp $(TARGETJAR) $(DESTDIR)
-
-SERVICES = java.util.spi.ResourceBundleControlProvider
-
-FILES_JAVA = UserControlProvider.java \
-             UserXMLControl.java
-
-RESOURCE_FILES = XmlRB.xml \
-                 XmlRB_ja.xml
-
-$(TARGETJAR): $(SERVICES) $(FILES_JAVA) $(RESOURCE_FILES)
-	rm -rf $(TMPDIR) $@
-	mkdir -p $(SERVICESDIR)
-	$(BINDIR)/javac -d $(TMPDIR) $(FILES_JAVA)
-	cp $(SERVICES) $(SERVICESDIR)
-	cp $(RESOURCE_FILES) $(TMPDIR)/com/foo
-	$(BINDIR)/jar  cvf $@ -C $(TMPDIR) .
-
-clean:
-	rm -rf $(TMPDIR) $(TARGETJAR)
-
-.PHONY: all install clean
--- a/test/java/util/spi/ResourceBundleControlProvider/providersrc/UserControlProvider.java	Mon Dec 19 09:16:40 2016 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,43 +0,0 @@
-/*
- * Copyright (c) 2012, 2013, 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 com.foo;
-
-import java.util.ResourceBundle;
-import java.util.spi.ResourceBundleControlProvider;
-
-public class UserControlProvider implements ResourceBundleControlProvider {
-    static final ResourceBundle.Control XMLCONTROL = new UserXMLControl();
-
-    public ResourceBundle.Control getControl(String baseName) {
-        System.out.println(getClass().getName()+".getControl called for " + baseName);
-
-        // Throws a NPE if baseName is null.
-        if (baseName.startsWith("com.foo.Xml")) {
-            System.out.println("\treturns " + XMLCONTROL);
-            return XMLCONTROL;
-        }
-        System.out.println("\treturns null");
-        return null;
-    }
-}
--- a/test/java/util/spi/ResourceBundleControlProvider/providersrc/UserXMLControl.java	Mon Dec 19 09:16:40 2016 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,95 +0,0 @@
-/*
- * Copyright (c) 2012, 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 com.foo;
-
-import java.io.*;
-import java.net.*;
-import java.util.*;
-import static java.util.ResourceBundle.Control.*;
-
-public class UserXMLControl extends ResourceBundle.Control {
-    @Override
-    public List<String> getFormats(String baseName) {
-        if (baseName == null) {
-            throw new NullPointerException();
-        }
-        return Arrays.asList("xml");
-    }
-
-    @Override
-    public ResourceBundle newBundle(String baseName, Locale locale,
-                                    String format,
-                                    ClassLoader loader,
-                                    boolean reload)
-        throws IllegalAccessException,
-               InstantiationException, IOException {
-        if (baseName == null || locale == null
-            || format == null || loader == null) {
-            throw new NullPointerException();
-        }
-        ResourceBundle bundle = null;
-        if (format.equals("xml")) {
-            String bundleName = toBundleName(baseName, locale);
-            String resourceName = toResourceName(bundleName, format);
-            URL url = loader.getResource(resourceName);
-            if (url != null) {
-                URLConnection connection = url.openConnection();
-                if (connection != null) {
-                    if (reload) {
-                        // disable caches if reloading
-                        connection.setUseCaches(false);
-                    }
-                    try (InputStream stream = connection.getInputStream()) {
-                        if (stream != null) {
-                            BufferedInputStream bis = new BufferedInputStream(stream);
-                            bundle = new XMLResourceBundle(bis);
-                        }
-                    }
-                }
-            }
-        }
-        return bundle;
-    }
-
-    private static class XMLResourceBundle extends ResourceBundle {
-        private Properties props;
-
-        XMLResourceBundle(InputStream stream) throws IOException {
-            props = new Properties();
-            props.loadFromXML(stream);
-        }
-
-        protected Object handleGetObject(String key) {
-            if (key == null) {
-                throw new NullPointerException();
-            }
-            return props.get(key);
-        }
-
-        public Enumeration<String> getKeys() {
-            // Not implemented
-            return null;
-        }
-    }
-}
--- a/test/java/util/spi/ResourceBundleControlProvider/providersrc/XmlRB.xml	Mon Dec 19 09:16:40 2016 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
- DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- 
- This code is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License version 2 only, as
- published by the Free Software Foundation.  Oracle designates this
- particular file as subject to the "Classpath" exception as provided
- by Oracle in the LICENSE file that accompanied this code.
- 
- This code is distributed in the hope that it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- version 2 for more details (a copy is included in the LICENSE file that
- accompanied this code).
- 
- You should have received a copy of the GNU General Public License version
- 2 along with this work; if not, write to the Free Software Foundation,
- Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- 
- Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- or visit www.oracle.com if you need additional information or have any
- questions.
--->
-<!---->
-
-<!-- DTD for properties -->
-<!DOCTYPE properties [
-<!ELEMENT properties ( comment?, entry* ) >
-<!ATTLIST properties version CDATA #FIXED "1.0">
-<!ELEMENT comment (#PCDATA) >
-<!ELEMENT entry (#PCDATA) >
-<!ATTLIST entry key CDATA #REQUIRED>
-]>
-
-<properties>
-    <comment>Test data for UserDefaultControlTest.java</comment>
-    <entry key="type">XML</entry>
-</properties>
--- a/test/java/util/spi/ResourceBundleControlProvider/providersrc/XmlRB_ja.xml	Mon Dec 19 09:16:40 2016 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
- DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- 
- This code is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License version 2 only, as
- published by the Free Software Foundation.  Oracle designates this
- particular file as subject to the "Classpath" exception as provided
- by Oracle in the LICENSE file that accompanied this code.
- 
- This code is distributed in the hope that it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- version 2 for more details (a copy is included in the LICENSE file that
- accompanied this code).
- 
- You should have received a copy of the GNU General Public License version
- 2 along with this work; if not, write to the Free Software Foundation,
- Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- 
- Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- or visit www.oracle.com if you need additional information or have any
- questions.
--->
-<!---->
-
-<!-- DTD for properties -->
-<!DOCTYPE properties [
-<!ELEMENT properties ( comment?, entry* ) >
-<!ATTLIST properties version CDATA #FIXED "1.0">
-<!ELEMENT comment (#PCDATA) >
-<!ELEMENT entry (#PCDATA) >
-<!ATTLIST entry key CDATA #REQUIRED>
-]>
-
-<properties>
-    <comment>Test data for UserDefaultControlTest.java</comment>
-    <entry key="type">XML</entry>
-</properties>
--- a/test/java/util/spi/ResourceBundleControlProvider/providersrc/java.util.spi.ResourceBundleControlProvider	Mon Dec 19 09:16:40 2016 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1 +0,0 @@
-com.foo.UserControlProvider
Binary file test/java/util/spi/ResourceBundleControlProvider/rbcontrolprovider.jar has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/lib/testlibrary/ModuleSourceBuilder.java	Mon Dec 19 15:33:32 2016 -0800
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Stream;
+
+import static org.testng.Assert.assertTrue;
+
+/**
+ * Utility class for creating test modules.
+ */
+public class ModuleSourceBuilder {
+    private static String MODULE_INFO_JAVA = "module-info.java";
+    private static Pattern MODULE_PATTERN =
+        Pattern.compile("module\\s+((?:\\w+\\.)*)");
+    private static Pattern PACKAGE_PATTERN =
+                       Pattern.compile("package\\s+(((?:\\w+\\.)*)(?:\\w+))");
+    private static Pattern CLASS_PATTERN =
+          Pattern.compile("(?:public\\s+)?(?:class|enum|interface)\\s+(\\w+)");
+
+    private final Path dir;
+    public ModuleSourceBuilder(Path dir) {
+        this.dir = dir;
+    }
+
+    /**
+     * Create java source files of the given module
+     */
+    public void writeJavaFiles(String module, String moduleInfoJava, String... contents)
+        throws IOException
+    {
+        Path msrc = dir.resolve(module);
+        new JavaSource(moduleInfoJava).write(msrc);
+        for (String c : contents) {
+            new JavaSource(c).write(msrc);
+        }
+    }
+
+    /**
+     * Compile the module to the given destination.
+     */
+    public void compile(String module, Path dest, String... options)
+        throws IOException
+    {
+        Path msrc = dir.resolve(module);
+        Stream<String> args =
+            Stream.concat(Arrays.stream(options),
+                          Stream.of("--module-source-path",
+                                    dir.toString()));
+        assertTrue(CompilerUtils.compile(msrc, dest, args.toArray(String[]::new)),
+                   "Fail to compile " + module);
+    }
+
+    static class JavaSource {
+        final String source;
+        JavaSource(String source) {
+            this.source = source;
+        }
+
+        /**
+         * Writes the source code to a file in a specified directory.
+         * @param dir the directory
+         * @throws IOException if there is a problem writing the file
+         */
+        public void write(Path dir) throws IOException {
+            Path file = dir.resolve(getJavaFileNameFromSource(source));
+            Files.createDirectories(file.getParent());
+            try (BufferedWriter out = Files.newBufferedWriter(file)) {
+                out.write(source.replace("\n", System.lineSeparator()));
+            }
+        }
+
+        /**
+         * Extracts the Java file name from the class declaration.
+         * This method is intended for simple files and uses regular expressions,
+         * so comments matching the pattern can make the method fail.
+         */
+        static String getJavaFileNameFromSource(String source) {
+            String packageName = null;
+
+            Matcher matcher = MODULE_PATTERN.matcher(source);
+            if (matcher.find())
+                return MODULE_INFO_JAVA;
+
+            matcher = PACKAGE_PATTERN.matcher(source);
+            if (matcher.find())
+                packageName = matcher.group(1).replace(".", "/");
+
+            matcher = CLASS_PATTERN.matcher(source);
+            if (matcher.find()) {
+                String className = matcher.group(1) + ".java";
+                return (packageName == null) ? className : packageName + "/" + className;
+            } else if (packageName != null) {
+                return packageName + "/package-info.java";
+            } else {
+                throw new Error("Could not extract the java class " +
+                    "name from the provided source");
+            }
+        }
+    }
+}
--- a/test/sun/management/jmxremote/bootstrap/CustomLauncherTest.java	Mon Dec 19 09:16:40 2016 -0800
+++ b/test/sun/management/jmxremote/bootstrap/CustomLauncherTest.java	Mon Dec 19 15:33:32 2016 -0800
@@ -56,8 +56,6 @@
     private static final  String TEST_SRC = System.getProperty("test.src");
     private static final  String OSNAME = System.getProperty("os.name");
     private static final  String ARCH;
-    private static final  String LIBARCH;
-
     static {
         // magic with os.arch
         String osarch = System.getProperty("os.arch");
@@ -84,7 +82,6 @@
                 ARCH = osarch;
             }
         }
-        LIBARCH = ARCH.equals("i586") ? "i386" : ARCH;
     }
 
     public static void main(String[] args) throws Exception {
@@ -184,15 +181,12 @@
     }
 
     private static Path findLibjvm(FileSystem FS) {
-        Path libjvmPath = findLibjvm(FS.getPath(TEST_JDK, "jre", "lib", LIBARCH));
-        if (libjvmPath == null) {
-            libjvmPath = findLibjvm(FS.getPath(TEST_JDK, "lib", LIBARCH));
-        }
+        Path libjvmPath = findLibjvm(FS.getPath(TEST_JDK, "lib"));
         return libjvmPath;
     }
 
     private static Path findLibjvm(Path libPath) {
-        // ARCH/libjvm.so -> ARCH/server/libjvm.so -> ARCH/client/libjvm.so
+        // libjvm.so -> server/libjvm.so -> client/libjvm.so
         Path libjvmPath = libPath.resolve("libjvm.so");
         if (isFileOk(libjvmPath)) {
             return libjvmPath;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/sun/security/tools/jarsigner/multiRelease/MVJarSigningTest.java	Mon Dec 19 15:33:32 2016 -0800
@@ -0,0 +1,228 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8047305 8075618
+ * @summary Tests jarsigner tool and JarSigner API work with multi-release JAR files.
+ * @library /test/lib
+ * @library /lib/testlibrary
+ * @run main MVJarSigningTest
+ */
+
+import jdk.security.jarsigner.JarSigner;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.security.KeyStore;
+import java.security.PrivateKey;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateFactory;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import java.util.jar.JarFile;
+import java.util.stream.Stream;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipOutputStream;
+
+import jdk.test.lib.JDKToolFinder;
+import jdk.test.lib.JDKToolLauncher;
+import jdk.test.lib.Utils;
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.process.ProcessTools;
+
+
+public class MVJarSigningTest {
+
+    private static final String TEST_SRC = System.getProperty("test.src", ".");
+    private static final String USR_DIR = System.getProperty("user.dir", ".");
+    private static final String JAR_NAME = "MV.jar";
+    private static final String KEYSTORE = "keystore.jks";
+    private static final String ALIAS = "JavaTest";
+    private static final String STOREPASS = "changeit";
+    private static final String KEYPASS = "changeit";
+    private static final String SIGNED_JAR = "Signed.jar";
+    private static final String POLICY_FILE = "SignedJar.policy";
+    private static final String VERSION_MESSAGE = "I am running on version 9";
+
+    public static void main(String[] args) throws Throwable {
+        // compile java files in jarContent directory
+        compile("jarContent");
+
+        // create multi-release jar
+        Path classes = Paths.get("classes");
+        jar("cf", JAR_NAME, "-C", classes.resolve("base").toString(), ".",
+                "--release", "9", "-C", classes.resolve("v9").toString(), ".",
+                "--release", "10", "-C", classes.resolve("v10").toString(), ".")
+            .shouldHaveExitValue(0);
+
+        genKey();
+        signJar(JAR_NAME)
+            .shouldHaveExitValue(0)
+            .shouldMatch("signing.*META-INF/versions/9/version/Version.class")
+            .shouldMatch("signing.*META-INF/versions/10/version/Version.class")
+            .shouldMatch("signing.*version/Main.class")
+            .shouldMatch("signing.*version/Version.class");
+        verify(SIGNED_JAR);
+
+        // test with JarSigner API
+        Files.deleteIfExists(Paths.get(SIGNED_JAR));
+        signWithJarSignerAPI(JAR_NAME);
+        verify(SIGNED_JAR);
+
+        // test Permission granted
+        File keypass = new File("keypass");
+        try (FileOutputStream fos = new FileOutputStream(keypass)) {
+            fos.write(KEYPASS.getBytes());
+        }
+        String[] cmd = {
+                "-classpath", SIGNED_JAR,
+                "-Djava.security.manager",
+                "-Djava.security.policy=" +
+                TEST_SRC + File.separator + POLICY_FILE,
+                "version.Main"};
+        ProcessTools.executeTestJvm(cmd)
+            .shouldHaveExitValue(0)
+            .shouldContain(VERSION_MESSAGE);
+    }
+
+    private static void compile (String jarContent_path) throws Throwable {
+        Path classes = Paths.get(USR_DIR, "classes", "base");
+        Path source = Paths.get(TEST_SRC, jarContent_path, "base", "version");
+        CompilerUtils.compile(source, classes);
+
+        classes = Paths.get(USR_DIR, "classes", "v9");
+        source = Paths.get(TEST_SRC, jarContent_path , "v9", "version");
+        CompilerUtils.compile(source, classes);
+
+        classes = Paths.get(USR_DIR, "classes", "v10");
+        source = Paths.get(TEST_SRC, jarContent_path, "v10", "version");
+        CompilerUtils.compile(source, classes);
+    }
+
+    private static OutputAnalyzer jar(String...args) throws Throwable {
+        JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("jar");
+        Stream.of(args).forEach(launcher::addToolArg);
+        return ProcessTools.executeCommand(launcher.getCommand());
+    }
+
+    private static void genKey() throws Throwable {
+        String keytool = JDKToolFinder.getJDKTool("keytool");
+        Files.deleteIfExists(Paths.get(KEYSTORE));
+        ProcessTools.executeCommand(keytool,
+                "-J-Duser.language=en",
+                "-J-Duser.country=US",
+                "-genkey",
+                "-alias", ALIAS,
+                "-keystore", KEYSTORE,
+                "-keypass", KEYPASS,
+                "-dname", "cn=sample",
+                "-storepass", STOREPASS
+        ).shouldHaveExitValue(0);
+    }
+
+    private static OutputAnalyzer signJar(String jarName) throws Throwable {
+        List<String> args = new ArrayList<>();
+        args.add("-verbose");
+        args.add("-signedjar");
+        args.add(SIGNED_JAR);
+        args.add(jarName);
+        args.add(ALIAS);
+
+        return jarsigner(args);
+    }
+
+    private static void verify(String signedJarName) throws Throwable {
+        verifyJar(signedJarName)
+            .shouldHaveExitValue(0)
+            .shouldContain("jar verified")
+            .shouldMatch("smk.*META-INF/versions/9/version/Version.class")
+            .shouldMatch("smk.*META-INF/versions/10/version/Version.class")
+            .shouldMatch("smk.*version/Main.class")
+            .shouldMatch("smk.*version/Version.class");
+    }
+
+    private static OutputAnalyzer verifyJar(String signedJarName) throws Throwable {
+        List<String> args = new ArrayList<>();
+        args.add("-verbose");
+        args.add("-verify");
+        args.add(signedJarName);
+
+        return jarsigner(args);
+    }
+
+    private static OutputAnalyzer jarsigner(List<String> extra)
+            throws Throwable {
+        JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("jarsigner")
+                .addVMArg("-Duser.language=en")
+                .addVMArg("-Duser.country=US")
+                .addToolArg("-keystore")
+                .addToolArg(KEYSTORE)
+                .addToolArg("-storepass")
+                .addToolArg(STOREPASS)
+                .addToolArg("-keypass")
+                .addToolArg(KEYPASS);
+        for (String s : extra) {
+            if (s.startsWith("-J")) {
+                launcher.addVMArg(s.substring(2));
+            } else {
+                launcher.addToolArg(s);
+            }
+        }
+        return ProcessTools.executeCommand(launcher.getCommand());
+    }
+
+    private static void signWithJarSignerAPI(String jarName)
+            throws Throwable {
+        // Get JarSigner
+        try (FileInputStream fis = new FileInputStream(KEYSTORE)) {
+                KeyStore ks = KeyStore.getInstance("JKS");
+                ks.load(fis, STOREPASS.toCharArray());
+                PrivateKey pk = (PrivateKey)ks.getKey(ALIAS, KEYPASS.toCharArray());
+                Certificate cert = ks.getCertificate(ALIAS);
+                JarSigner signer = new JarSigner.Builder(pk,
+                        CertificateFactory.getInstance("X.509").generateCertPath(
+                                Collections.singletonList(cert)))
+                        .build();
+            // Sign jar
+            try (ZipFile src = new JarFile(jarName);
+                    FileOutputStream out = new FileOutputStream(SIGNED_JAR)) {
+                signer.sign(src,out);
+            }
+        }
+    }
+
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/sun/security/tools/jarsigner/multiRelease/SignedJar.policy	Mon Dec 19 15:33:32 2016 -0800
@@ -0,0 +1,10 @@
+keystore "file:keystore.jks";
+keystorePasswordURL "file:keypass";
+
+grant signedBy "JavaTest" {
+    permission java.lang.RuntimePermission "setIO";
+};
+
+grant signedBy "other" {
+    permission java.lang.RuntimePermission "setFactory";
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/sun/security/tools/jarsigner/multiRelease/jarContent/base/version/Main.java	Mon Dec 19 15:33:32 2016 -0800
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package version;
+
+import java.security.Permission;
+
+public class Main {
+
+    public static void main(String[] args) {
+        Version v = new Version();
+        System.out.println("I am running on version " + v.getVersion());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/sun/security/tools/jarsigner/multiRelease/jarContent/base/version/Version.java	Mon Dec 19 15:33:32 2016 -0800
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package version;
+
+public class Version {
+
+    public int getVersion() {
+        return 8;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/sun/security/tools/jarsigner/multiRelease/jarContent/v10/version/Version.java	Mon Dec 19 15:33:32 2016 -0800
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package version;
+
+public class Version {
+
+    public int getVersion() {
+        return 10;
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/sun/security/tools/jarsigner/multiRelease/jarContent/v9/version/Version.java	Mon Dec 19 15:33:32 2016 -0800
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package version;
+
+import java.security.AccessControlException;
+import java.security.AccessController;
+import java.security.Permission;
+import java.security.PrivilegedAction;
+
+public class Version {
+    private static final Permission PERM1 = new RuntimePermission("setIO");
+    private static final Permission PERM2 = new RuntimePermission("setFactory");
+
+    public int getVersion() {
+        checkPermission(PERM1, false);
+        checkPermission(PERM2, true);
+        return 9;
+    }
+
+    private void checkPermission(Permission perm, boolean expectException) {
+        boolean getException = (Boolean) AccessController
+                .doPrivileged((PrivilegedAction) () -> {
+            try {
+                AccessController.checkPermission(perm);
+                return (Boolean) false;
+            } catch (AccessControlException ex) {
+                return (Boolean) true;
+            }
+        });
+
+        if (expectException ^ getException) {
+            String message = "Check Permission :" + perm + "\n ExpectException = "
+                    + expectException + "\n getException = " + getException;
+            throw new RuntimeException(message);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/launcher/modules/addexports/AddExportsTestWarningError.java	Mon Dec 19 15:33:32 2016 -0800
@@ -0,0 +1,234 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @bug 8168836
+ * @summary Basic argument validation for --add-exports
+ * @library /lib/testlibrary
+ * @modules jdk.compiler
+ * @build AddExportsTestWarningError CompilerUtils ModuleSourceBuilder
+ * @build jdk.testlibrary.*
+ * @run testng AddExportsTestWarningError
+ */
+
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.stream.Stream;
+
+import jdk.testlibrary.OutputAnalyzer;
+import static jdk.testlibrary.ProcessTools.*;
+
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+import static org.testng.Assert.*;
+
+
+@Test
+public class AddExportsTestWarningError {
+
+    private static final Path MODS_DIR = Paths.get("mods");
+    private static final Path SRC_DIR = Paths.get("src");
+    private static final String M1_MAIN = "m1/p1.C1";
+    private static final String M3_MAIN = "m3/p3.C3";
+
+    @BeforeTest
+    public void setup() throws Exception {
+        ModuleSourceBuilder builder = new ModuleSourceBuilder(SRC_DIR);
+        builder.writeJavaFiles("m1",
+            "module m1 { }",
+            "package p1; public class C1 { " +
+                "    public static void main(String... args) {}" +
+                "}");
+
+        builder.writeJavaFiles("m2",
+            "module m2 { requires m1; exports p2; }",
+            "package p2; public class C2 {  private p1.C1 c1; }");
+
+        builder.writeJavaFiles("m3",
+            "module m3 { requires m2; }",
+            "package p3; class C3 { " +
+                "    p1.C1 c; " +
+                "    public static void main(String... args) { new p2.C2(); }" +
+                "}");
+
+        builder.compile("m1", MODS_DIR);
+        builder.compile("m2", MODS_DIR, "--add-exports", "m1/p1=m2");
+        builder.compile("m3", MODS_DIR, "--add-exports", "m1/p1=m3");
+    }
+
+
+    @DataProvider(name = "goodcases")
+    public Object[][] goodCases() {
+        return new Object[][]{
+
+            // empty items
+            { "m1/p1=,m2,m3",       null },
+            { "m1/p1=m2,,m3",       null },
+            { "m1/p1=m2,m3,",       null },
+
+            // duplicates
+            { "m1/p1=m2,m2,m3,,",   null },
+
+        };
+    }
+
+
+    @Test(dataProvider = "goodcases")
+    public void test(String value, String ignore) throws Exception {
+        testNoWarning(value);
+    }
+
+
+    @DataProvider(name = "illFormedAddExports")
+    public Object[][] illFormedAddExports() {
+        return new Object[][]{
+            { "m1",         "Unable to parse --add-exports <module>=<value>: m1"},
+
+            // missing source part
+            { "=m2",        "Unable to parse --add-exports <module>=<value>: =m2"},
+            { "/=m2",       "Unable to parse --add-exports <module>/<package>: /" },
+            { "m1=m2",      "Unable to parse --add-exports <module>/<package>: m1" },
+            { "/p1=m2",     "Unable to parse --add-exports <module>/<package>: /p1" },
+            { "m1p1=m2",    "Unable to parse --add-exports <module>/<package>: m1p1" },
+
+            // empty list, missing target
+            { "m1/p1=",     "Unable to parse --add-exports <module>=<value>: m1/p1=" },
+            { "m1/p1=,,",   "Target must be specified: --add-exports m1/p1=,," },
+        };
+    }
+
+    @Test(dataProvider = "illFormedAddExports")
+    public void testIllFormedAddExports(String value, String msg) throws Exception {
+        testError(value, msg);
+    }
+
+
+    @DataProvider(name = "unknownNames")
+    public Object[][] unknownNames() {
+        return new Object[][]{
+
+            // source not found
+            {"DoesNotExist/p=m1",  "WARNING: Unknown module: DoesNotExist specified in --add-exports"},
+            {"m1/DoesNotExist=m2", "WARNING: package DoesNotExist not in m1"},
+
+            // target not found
+            {"m1/p1=DoesNotExist", "WARNING: Unknown module: DoesNotExist specified in --add-exports"},
+
+            // bad names
+            {"m*/p1=m2",           "WARNING: Unknown module: m* specified in --add-exports"},
+            {"m1/p!=m2",           "WARNING: package p! not in m1"},
+            {"m1/p1=m!",           "WARNING: Unknown module: m! specified in --add-exports"},
+
+        };
+    }
+
+
+    @Test(dataProvider = "unknownNames")
+    public void testUnknownNames(String value, String msg) throws Exception {
+        testWarning(value, msg);
+    }
+
+
+    @DataProvider(name = "missingArguments")
+    public Object[][] missingArguments() {
+        return new Object[][]{
+            { new String[] { "--add-exports" },
+                "Error: --add-exports requires modules to be specified"},
+
+            { new String[] { "--add-exports=" },
+                "Error: --add-exports= requires modules to be specified" },
+
+            { new String[] { "--add-exports", "" },
+                "Error: --add-exports requires modules to be specified"}
+
+        };
+    }
+
+
+    @Test(dataProvider = "missingArguments")
+    public void testMissingArguments(String[] options, String msg) throws Exception {
+        String[] args = Stream.concat(Arrays.stream(options),
+                                      Stream.of("-version"))
+                              .toArray(String[]::new);
+        int exitValue = executeTestJava(args)
+            .outputTo(System.out)
+            .errorTo(System.out)
+            .shouldContain(msg)
+            .getExitValue();
+
+        assertTrue(exitValue != 0);
+    }
+
+     private void testWarning(String value, String msg) throws Exception {
+        int exitValue =
+            executeTestJava("--add-exports", value,
+                            "--module-path", MODS_DIR.toString(),
+                            "-m", M1_MAIN)
+                .outputTo(System.out)
+                .errorTo(System.out)
+                .shouldContain(msg)
+                .getExitValue();
+
+        assertTrue(exitValue == 0);
+    }
+
+    private void testError(String value, String msg) throws Exception {
+        int exitValue =
+            executeTestJava("--add-exports", value,
+                            "--module-path", MODS_DIR.toString(),
+                            "-m", M1_MAIN)
+                .outputTo(System.out)
+                .errorTo(System.out)
+                .shouldContain(msg)
+                .getExitValue();
+
+        assertTrue(exitValue != 0);
+    }
+
+    private void testNoWarning(String value) throws Exception {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        PrintStream ps = new PrintStream(new BufferedOutputStream(baos));
+        OutputAnalyzer outputAnalyzer =
+            executeTestJava("--add-exports", value,
+                            "--module-path", MODS_DIR.toString(),
+                            "-m", M3_MAIN)
+                .outputTo(ps)
+                .errorTo(ps);
+
+        assertTrue(outputAnalyzer.getExitValue() == 0);
+
+        System.out.println(baos.toString());
+        String[] output = baos.toString().split("\\R");
+        assertFalse(Arrays.stream(output)
+                          .filter(s -> !s.matches("WARNING: Module name .* may soon be illegal"))
+                          .filter(s -> s.startsWith("WARNING:"))
+                          .findAny().isPresent());
+
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/launcher/modules/addreads/AddReadsTestWarningError.java	Mon Dec 19 15:33:32 2016 -0800
@@ -0,0 +1,224 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @bug 8168836
+ * @summary  Basic argument validation for --add-reads
+ * @library /lib/testlibrary
+ * @modules jdk.compiler
+ * @build AddReadsTestWarningError CompilerUtils ModuleSourceBuilder
+ * @build jdk.testlibrary.*
+ * @run testng AddReadsTestWarningError
+ */
+
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.stream.Stream;
+
+import jdk.testlibrary.OutputAnalyzer;
+import static jdk.testlibrary.ProcessTools.*;
+
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+import static org.testng.Assert.*;
+
+
+@Test
+public class AddReadsTestWarningError {
+
+    private static final Path MODS_DIR = Paths.get("mods");
+    private static final Path SRC_DIR = Paths.get("src");
+    private static final String M1_MAIN = "m1/p1.C1";
+    private static final String M4_MAIN = "m4/p4.C4";
+
+    @BeforeTest
+    public void setup() throws Exception {
+        ModuleSourceBuilder builder = new ModuleSourceBuilder(SRC_DIR);
+        builder.writeJavaFiles("m1",
+            "module m1 { requires m4; }",
+            "package p1; public class C1 { " +
+            "    public static void main(String... args) {" +
+            "        p2.C2 c2 = new p2.C2();" +
+            "        p3.C3 c3 = new p3.C3();" +
+            "    }" +
+            "}"
+        );
+
+        builder.writeJavaFiles("m2",
+            "module m2 { exports p2; }",
+            "package p2; public class C2 { }"
+        );
+
+        builder.writeJavaFiles("m3",
+            "module m3 { exports p3; }",
+            "package p3; public class C3 { }"
+        );
+
+        builder.writeJavaFiles("m4",
+            "module m4 { requires m2; requires m3; }",
+            "package p4; public class C4 { " +
+            "    public static void main(String... args) {}" +
+            "}"
+        );
+
+        builder.compile("m2", MODS_DIR);
+        builder.compile("m3", MODS_DIR);
+        builder.compile("m4", MODS_DIR);
+        builder.compile("m1", MODS_DIR, "--add-reads", "m1=m2,m3");
+    }
+
+
+    @DataProvider(name = "goodcases")
+    public Object[][] goodCases() {
+        return new Object[][]{
+            // empty items
+            { "m1=,m2,m3",       null },
+            { "m1=m2,,m3",       null },
+            { "m1=m2,m3,",       null },
+
+            // duplicates
+            { "m1=m2,m2,m3,,",  null },
+
+        };
+    }
+
+
+    @Test(dataProvider = "goodcases")
+    public void test(String value, String ignore) throws Exception {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        PrintStream ps = new PrintStream(new BufferedOutputStream(baos));
+        OutputAnalyzer outputAnalyzer =
+            executeTestJava("--add-reads", value,
+                            "--module-path", MODS_DIR.toString(),
+                            "-m", M1_MAIN)
+                .outputTo(ps)
+                .errorTo(ps);
+
+        assertTrue(outputAnalyzer.getExitValue() == 0);
+
+        System.out.println(baos.toString());
+        String[] output = baos.toString().split("\\R");
+        assertFalse(Arrays.stream(output)
+                          .filter(s -> !s.matches("WARNING: Module name .* may soon be illegal"))
+                          .filter(s -> s.startsWith("WARNING:"))
+                          .findAny().isPresent());
+    }
+
+
+    @DataProvider(name = "illFormedAddReads")
+    public Object[][] illFormedAddReads() {
+        return new Object[][]{
+            { "m1",         "Unable to parse --add-reads <module>=<value>: m1" },
+
+            // missing source part
+            { "=m2",        "Unable to parse --add-reads <module>=<value>: =m2" },
+
+            // empty list, missing target
+            { "m1=",        "Unable to parse --add-reads <module>=<value>: m1=" },
+
+            // empty list
+            { "m1=,,",      "Target must be specified: --add-reads m1=,," },
+        };
+    }
+
+
+    @Test(dataProvider = "illFormedAddReads")
+    public void testIllFormedAddReads(String value, String msg) throws Exception {
+        int exitValue =
+            executeTestJava("--add-reads", value,
+                            "--module-path", MODS_DIR.toString(),
+                            "-m", M4_MAIN)
+                .outputTo(System.out)
+                .errorTo(System.out)
+                .shouldContain(msg)
+                .getExitValue();
+
+        assertTrue(exitValue != 0);
+    }
+
+
+    @DataProvider(name = "unknownNames")
+    public Object[][] unknownNames() {
+        return new Object[][]{
+
+            // source not found
+            {"DoesNotExist=m2",    "WARNING: Unknown module: DoesNotExist specified in --add-reads"},
+
+            // target not found
+            {"m2=DoesNotExist",    "WARNING: Unknown module: DoesNotExist specified in --add-reads"},
+
+            // bad names
+            {"m*=m2",              "WARNING: Unknown module: m* specified in --add-reads"},
+            {"m2=m!",              "WARNING: Unknown module: m! specified in --add-reads"},
+
+        };
+    }
+
+    @Test(dataProvider = "unknownNames")
+    public void testUnknownNames(String value, String msg) throws Exception {
+        int exitValue =
+            executeTestJava("--add-reads", value,
+                            "--module-path", MODS_DIR.toString(),
+                            "-m", M4_MAIN)
+                .outputTo(System.out)
+                .errorTo(System.out)
+                .shouldContain(msg)
+                .getExitValue();
+
+        assertTrue(exitValue == 0);
+    }
+
+
+    @DataProvider(name = "missingArguments")
+    public Object[][] missingArguments() {
+        return new Object[][]{
+            { new String[] {"--add-reads" },
+                "Error: --add-reads requires modules to be specified"},
+
+            { new String[] { "--add-reads=" },
+                "Error: --add-reads= requires modules to be specified"},
+
+            { new String[] { "--add-reads", "" },
+                "Error: --add-reads requires modules to be specified"},
+        };
+    }
+
+    @Test(dataProvider = "missingArguments")
+    public void testEmptyArgument(String[] options, String msg) throws Exception {
+        String[] args = Stream.concat(Arrays.stream(options), Stream.of("-version"))
+                              .toArray(String[]::new);
+        int exitValue = executeTestJava(args)
+            .outputTo(System.out)
+            .errorTo(System.out)
+            .shouldContain(msg)
+            .getExitValue();
+
+        assertTrue(exitValue != 0);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/tools/launcher/modules/patch/basic/PatchTestWarningError.java	Mon Dec 19 15:33:32 2016 -0800
@@ -0,0 +1,220 @@
+/*
+ * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @bug 8168836
+ * @summary Basic argument validation for --patch-module
+ * @library /lib/testlibrary
+ * @modules jdk.compiler
+ * @build PatchTestWarningError CompilerUtils JarUtils jdk.testlibrary.*
+ * @run testng PatchTestWarningError
+ */
+
+import java.io.File;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import static jdk.testlibrary.ProcessTools.*;
+
+import org.testng.annotations.BeforeTest;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+import static org.testng.Assert.*;
+
+
+/**
+ * This test
+ * See PatchTestWarningError for test description.
+ */
+
+@Test
+public class PatchTestWarningError {
+
+    // top-level source directory
+    private static final String TEST_SRC = System.getProperty("test.src");
+
+    // source/destination tree for the test module
+    private static final Path SRC_DIR = Paths.get(TEST_SRC, "src");
+    private static final Path MODS_DIR = Paths.get("mods");
+
+    // source/destination tree for patch tree 1
+    private static final Path SRC1_DIR = Paths.get(TEST_SRC, "src1");
+    private static final Path PATCHES1_DIR = Paths.get("patches1");
+
+    // source/destination tree for patch tree 2
+    private static final Path SRC2_DIR = Paths.get(TEST_SRC, "src2");
+    private static final Path PATCHES2_DIR = Paths.get("patches2");
+
+    // patch path for java.base
+    private static final String PATCHES_PATH =
+        PATCHES1_DIR.resolve("java.base") + File.pathSeparator +
+            PATCHES2_DIR.resolve("java.base");
+
+    // the classes overridden or added with --patch-module
+    private static final String[] CLASSES = {
+
+        // java.base = boot loader
+        "java.base/java.text.Annotation",           // override class
+        "java.base/java.text.AnnotationBuddy",      // add class to package
+        "java.base/java.lang2.Object",              // new package
+
+    };
+
+
+    @BeforeTest
+    public void setup() throws Exception {
+
+        // javac -d mods/test src/test/**
+        boolean compiled= CompilerUtils.compile(SRC_DIR.resolve("test"),
+                                                MODS_DIR.resolve("test"));
+        assertTrue(compiled, "classes did not compile");
+
+        // javac -Xmodule:$MODULE -d patches1/$MODULE patches1/$MODULE/**
+        Path src = SRC1_DIR.resolve("java.base");
+        Path output = PATCHES1_DIR.resolve(src.getFileName());
+        Files.createDirectories(output);
+        String mn = src.getFileName().toString();
+        compiled  = CompilerUtils.compile(src, output, "-Xmodule:" + mn);
+        assertTrue(compiled, "classes did not compile");
+
+        // javac -Xmodule:$MODULE -d patches2/$MODULE patches2/$MODULE/**
+        src = SRC2_DIR.resolve("java.base");
+        output = PATCHES2_DIR.resolve(src.getFileName());
+        Files.createDirectories(output);
+        mn = src.getFileName().toString();
+        compiled  = CompilerUtils.compile(src, output, "-Xmodule:" + mn);
+        assertTrue(compiled, "classes did not compile");
+
+    }
+
+    /**
+     * Test with --patch-module options patching the same module
+     */
+    public void testDuplicateModule() throws Exception {
+        int exitValue =
+            executeTestJava("--patch-module", "java.base=" + PATCHES1_DIR.resolve("java.base"),
+                            "--patch-module", "java.base=" + PATCHES2_DIR.resolve("java.base"),
+                            "--module-path", MODS_DIR.toString(),
+                            "-m", "test/jdk.test.Main")
+                .outputTo(System.out)
+                .errorTo(System.out)
+                // error output by VM
+                .shouldContain("Cannot specify java.base more than once to --patch-module")
+                .getExitValue();
+
+        assertTrue(exitValue != 0);
+    }
+
+    @DataProvider(name = "emptyItem")
+    public Object[][] emptyItems() {
+        String patch1 = PATCHES1_DIR.resolve("java.base").toString();
+        String patch2 = PATCHES2_DIR.resolve("java.base").toString();
+        String pathSep = File.pathSeparator;
+        return new Object[][]{
+
+            { "java.base="+ pathSep + patch1 + pathSep + patch2,            null },
+            { "java.base="+ patch1 + pathSep + pathSep + patch2,            null },
+            { "java.base="+ patch1 + pathSep + patch2 + pathSep + pathSep,  null },
+        };
+    }
+
+    /**
+     * Empty item in a non-empty path list
+     */
+    @Test(dataProvider = "emptyItem")
+    public void testEmptyItem(String value, String msg) throws Exception {
+        // the argument to the test is the list of classes overridden or added
+        String arg = Stream.of(CLASSES).collect(Collectors.joining(","));
+
+        int exitValue =
+            executeTestJava("--patch-module", value,
+                            "--add-exports", "java.base/java.lang2=test",
+                            "--module-path", MODS_DIR.toString(),
+                            "-m", "test/jdk.test.Main", arg)
+                .outputTo(System.out)
+                .errorTo(System.out)
+                .getExitValue();
+
+        assertTrue(exitValue == 0);
+    }
+
+    /**
+     * Test bad module name that should emit a warning
+     */
+    public void testBadName() throws Exception {
+        // the argument to the test is the list of classes overridden or added
+        String arg = Stream.of(CLASSES).collect(Collectors.joining(","));
+
+        int exitValue =
+            executeTestJava("--patch-module", "DoesNotExist=tmp",
+                            "--patch-module", "java.base=" + PATCHES_PATH,
+                            "--add-exports", "java.base/java.lang2=test",
+                            "--module-path", MODS_DIR.toString(),
+                            "-m", "test/jdk.test.Main", arg)
+                .outputTo(System.out)
+                .errorTo(System.out)
+                .shouldContain("WARNING: Unknown module: DoesNotExist specified in --patch-module")
+                .getExitValue();
+
+        assertTrue(exitValue == 0);
+    }
+
+    @DataProvider(name = "badArguments")
+    public Object[][] badArguments() {
+        return new Object[][]{
+
+            // source not found
+            { "=tmp",            "Unable to parse --patch-module <module>=<value>: =tmp" },
+
+            // target not found: check by VM
+            { "java.base",       "Missing '=' in --patch-module specification" },
+            { "foo",             "Missing '=' in --patch-module specification" },
+
+            // target not found
+            { "java.base=",      "Unable to parse --patch-module <module>=<value>: java.base="  },
+            { "java.base=" + File.pathSeparator,
+              "Target must be specified: --patch-module java.base=" + File.pathSeparator }
+        };
+    }
+
+    /**
+     * Test ill-formed argument to --patch-module
+     */
+    @Test(dataProvider = "badArguments")
+    public void testBadArgument(String value, String msg) throws Exception {
+        int exitValue =
+            executeTestJava("--patch-module", value,
+                            "--module-path", MODS_DIR.toString(),
+                            "-m", "test/jdk.test.Main")
+                .outputTo(System.out)
+                .errorTo(System.out)
+                .shouldContain(msg)
+                .getExitValue();
+
+        assertTrue(exitValue != 0);
+    }
+}
--- a/test/tools/launcher/modules/patch/systemmodules/src1/java.base/jdk/internal/modules/SystemModules.java	Mon Dec 19 09:16:40 2016 -0800
+++ b/test/tools/launcher/modules/patch/systemmodules/src1/java.base/jdk/internal/modules/SystemModules.java	Mon Dec 19 15:33:32 2016 -0800
@@ -29,4 +29,8 @@
  */
 public final class SystemModules {
     public static final String[] MODULE_NAMES = new String[0];
+
+    public static boolean hasSplitPackages() {
+        return true;
+    }
 }