Merge
authorjiangli
Mon, 23 Nov 2015 14:37:04 -0500
changeset 34365 ee671d00723e
parent 34364 7ce97115585b (current diff)
parent 33875 c1c71107d45f (diff)
child 34366 91e243932012
child 34367 3dd14ced3947
Merge
jdk/src/java.base/share/classes/sun/util/logging/LoggingProxy.java
jdk/src/java.base/share/classes/sun/util/logging/LoggingSupport.java
jdk/src/java.logging/share/classes/java/util/logging/LoggingProxyImpl.java
jdk/test/java/util/stream/bootlib/java/util/stream/CollectorOps.java
jdk/test/java/util/stream/bootlib/java/util/stream/DefaultMethodStreams.java
jdk/test/java/util/stream/bootlib/java/util/stream/DoubleStreamTestDataProvider.java
jdk/test/java/util/stream/bootlib/java/util/stream/DoubleStreamTestScenario.java
jdk/test/java/util/stream/bootlib/java/util/stream/FlagDeclaringOp.java
jdk/test/java/util/stream/bootlib/java/util/stream/IntStreamTestDataProvider.java
jdk/test/java/util/stream/bootlib/java/util/stream/IntStreamTestScenario.java
jdk/test/java/util/stream/bootlib/java/util/stream/IntermediateTestOp.java
jdk/test/java/util/stream/bootlib/java/util/stream/LambdaTestHelpers.java
jdk/test/java/util/stream/bootlib/java/util/stream/LambdaTestMode.java
jdk/test/java/util/stream/bootlib/java/util/stream/LoggingTestCase.java
jdk/test/java/util/stream/bootlib/java/util/stream/LongStreamTestDataProvider.java
jdk/test/java/util/stream/bootlib/java/util/stream/LongStreamTestScenario.java
jdk/test/java/util/stream/bootlib/java/util/stream/OpTestCase.java
jdk/test/java/util/stream/bootlib/java/util/stream/SpliteratorTestHelper.java
jdk/test/java/util/stream/bootlib/java/util/stream/StatefulTestOp.java
jdk/test/java/util/stream/bootlib/java/util/stream/StatelessTestOp.java
jdk/test/java/util/stream/bootlib/java/util/stream/StreamOpFlagTestHelper.java
jdk/test/java/util/stream/bootlib/java/util/stream/StreamTestDataProvider.java
jdk/test/java/util/stream/bootlib/java/util/stream/StreamTestScenario.java
jdk/test/java/util/stream/bootlib/java/util/stream/TestData.java
jdk/test/java/util/stream/bootlib/java/util/stream/TestFlagExpectedOp.java
jdk/test/java/util/stream/bootlib/java/util/stream/ThowableHelper.java
jdk/test/java/util/stream/boottest/java/util/stream/DoubleNodeTest.java
jdk/test/java/util/stream/boottest/java/util/stream/FlagOpTest.java
jdk/test/java/util/stream/boottest/java/util/stream/IntNodeTest.java
jdk/test/java/util/stream/boottest/java/util/stream/LongNodeTest.java
jdk/test/java/util/stream/boottest/java/util/stream/NodeBuilderTest.java
jdk/test/java/util/stream/boottest/java/util/stream/NodeTest.java
jdk/test/java/util/stream/boottest/java/util/stream/SliceSpliteratorTest.java
jdk/test/java/util/stream/boottest/java/util/stream/SpinedBufferTest.java
jdk/test/java/util/stream/boottest/java/util/stream/StreamFlagsTest.java
jdk/test/java/util/stream/boottest/java/util/stream/StreamOpFlagsTest.java
jdk/test/java/util/stream/boottest/java/util/stream/StreamReuseTest.java
--- a/jdk/src/java.base/share/classes/java/lang/AbstractStringBuilder.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/src/java.base/share/classes/java/lang/AbstractStringBuilder.java	Mon Nov 23 14:37:04 2015 -0500
@@ -1584,7 +1584,7 @@
      * @param dstBegin  the char index, not offset of byte[]
      * @param coder     the coder of dst[]
      */
-    protected void getBytes(byte dst[], int dstBegin, byte coder) {
+    void getBytes(byte dst[], int dstBegin, byte coder) {
         if (this.coder == coder) {
             System.arraycopy(value, 0, dst, dstBegin << coder, count << coder);
         } else {        // this.coder == LATIN && coder == UTF16
@@ -1593,7 +1593,7 @@
     }
 
     /* for readObject() */
-    protected void initBytes(char[] value, int off, int len) {
+    void initBytes(char[] value, int off, int len) {
         if (String.COMPACT_STRINGS) {
             this.value = StringUTF16.compress(value, off, len);
             if (this.value != null) {
--- a/jdk/src/java.base/share/classes/java/lang/ProcessBuilder.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/src/java.base/share/classes/java/lang/ProcessBuilder.java	Mon Nov 23 14:37:04 2015 -0500
@@ -26,9 +26,11 @@
 package java.lang;
 
 import java.io.File;
+import java.io.FileDescriptor;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.nio.channels.Pipe;
 import java.util.Arrays;
 import java.util.ArrayList;
 import java.util.List;
@@ -43,6 +45,11 @@
  * {@link Process} instance with those attributes.  The {@link
  * #start()} method can be invoked repeatedly from the same instance
  * to create new subprocesses with identical or related attributes.
+ * <p>
+ * The {@link #startPipeline startPipeline} method can be invoked to create
+ * a pipeline of new processes that send the output of each process
+ * directly to the next process.  Each process has the attributes of
+ * its respective ProcessBuilder.
  *
  * <p>Each process builder manages these process attributes:
  *
@@ -696,11 +703,37 @@
         private Redirect() {}
     }
 
+    /**
+     * Private implementation subclass of Redirect that holds a FileDescriptor for the
+     * output of a previously started Process.
+     * The FileDescriptor is used as the standard input of the next Process
+     * to be started.
+     */
+    static class RedirectPipeImpl extends Redirect {
+        final FileDescriptor fd;
+
+        RedirectPipeImpl() {
+            this.fd = new FileDescriptor();
+        }
+        @Override
+        public Type type() { return Type.PIPE; }
+
+        @Override
+        public String toString() { return type().toString();}
+
+        FileDescriptor getFd() { return fd; }
+    }
+
+    /**
+     * Return the array of redirects, creating the default as needed.
+     * @return the array of redirects
+     */
     private Redirect[] redirects() {
-        if (redirects == null)
+        if (redirects == null) {
             redirects = new Redirect[] {
-                Redirect.PIPE, Redirect.PIPE, Redirect.PIPE
+                    Redirect.PIPE, Redirect.PIPE, Redirect.PIPE
             };
+        }
         return redirects;
     }
 
@@ -1039,6 +1072,18 @@
      * @see Runtime#exec(String[], String[], java.io.File)
      */
     public Process start() throws IOException {
+        return start(redirects);
+    }
+
+    /**
+     * Start a new Process using an explicit array of redirects.
+     * See {@link #start} for details of starting each Process.
+     *
+     * @param redirect array of redirects for stdin, stdout, stderr
+     * @return the new Process
+     * @throws IOException if an I/O error occurs
+     */
+    private Process start(Redirect[] redirects) throws IOException {
         // Must convert to array first -- a malicious user-supplied
         // list might try to circumvent the security check.
         String[] cmdarray = command.toArray(new String[command.size()]);
@@ -1089,4 +1134,171 @@
                 cause);
         }
     }
+
+    /**
+     * Starts a Process for each ProcessBuilder, creating a pipeline of
+     * processes linked by their standard output and standard input streams.
+     * The attributes of each ProcessBuilder are used to start the respective
+     * process except that as each process is started, its standard output
+     * is directed to the standard input of the next.  The redirects for standard
+     * input of the first process and standard output of the last process are
+     * initialized using the redirect settings of the respective ProcessBuilder.
+     * All other {@code ProcessBuilder} redirects should be
+     * {@link Redirect#PIPE Redirect.PIPE}.
+     * <p>
+     * All input and output streams between the intermediate processes are
+     * not accessible.
+     * The {@link Process#getOutputStream standard input} of all processes
+     * except the first process are <i>null output streams</i>
+     * The {@link Process#getInputStream standard output} of all processes
+     * except the last process are <i>null input streams</i>.
+     * <p>
+     * The {@link #redirectErrorStream} of each ProcessBuilder applies to the
+     * respective process.  If set to {@code true}, the error stream is written
+     * to the same stream as standard output.
+     * <p>
+     * If starting any of the processes throws an Exception, all processes
+     * are forcibly destroyed.
+     * <p>
+     * The {@code startPipeline} method performs the same checks on
+     * each ProcessBuilder as does the {@link #start} method. The new process
+     * will invoke the command and arguments given by {@link #command()},
+     * in a working directory as given by {@link #directory()},
+     * with a process environment as given by {@link #environment()}.
+     * <p>
+     * This method checks that the command is a valid operating
+     * system command.  Which commands are valid is system-dependent,
+     * but at the very least the command must be a non-empty list of
+     * non-null strings.
+     * <p>
+     * A minimal set of system dependent environment variables may
+     * be required to start a process on some operating systems.
+     * As a result, the subprocess may inherit additional environment variable
+     * settings beyond those in the process builder's {@link #environment()}.
+     * <p>
+     * If there is a security manager, its
+     * {@link SecurityManager#checkExec checkExec}
+     * method is called with the first component of this object's
+     * {@code command} array as its argument. This may result in
+     * a {@link SecurityException} being thrown.
+     * <p>
+     * Starting an operating system process is highly system-dependent.
+     * Among the many things that can go wrong are:
+     * <ul>
+     * <li>The operating system program file was not found.
+     * <li>Access to the program file was denied.
+     * <li>The working directory does not exist.
+     * <li>Invalid character in command argument, such as NUL.
+     * </ul>
+     * <p>
+     * In such cases an exception will be thrown.  The exact nature
+     * of the exception is system-dependent, but it will always be a
+     * subclass of {@link IOException}.
+     * <p>
+     * If the operating system does not support the creation of
+     * processes, an {@link UnsupportedOperationException} will be thrown.
+     * <p>
+     * Subsequent modifications to this process builder will not
+     * affect the returned {@link Process}.
+     * @apiNote
+     * For example to count the unique imports for all the files in a file hierarchy
+     * on a Unix compatible platform:
+     * <pre>{@code
+     * String directory = "/home/duke/src";
+     * ProcessBuilder[] builders = {
+     *              new ProcessBuilder("find", directory, "-type", "f"),
+                    new ProcessBuilder("xargs", "grep", "-h", "^import "),
+                    new ProcessBuilder("awk", "{print $2;}"),
+                    new ProcessBuilder("sort", "-u")};
+     * List<Process> processes = ProcessBuilder.startPipeline(
+     *         Arrays.asList(builders));
+     * Process last = processes.get(processes.size()-1);
+     * try (InputStream is = last.getInputStream();
+     *         Reader isr = new InputStreamReader(is);
+     *         BufferedReader r = new BufferedReader(isr)) {
+     *     long count = r.lines().count();
+     * }
+     * }</pre>
+     *
+     * @param builders a List of ProcessBuilders
+     * @return a {@code List<Process>}es started from the corresponding
+     *         ProcessBuilder
+     * @throws IllegalArgumentException any of the redirects except the
+     *          standard input of the first builder and the standard output of
+     *          the last builder are not {@link Redirect#PIPE}.
+     * @throws NullPointerException
+     *         if an element of the command list is null or
+     *         if an element of the ProcessBuilder list is null or
+     *         the builders argument is null
+     * @throws IndexOutOfBoundsException
+     *         if the command is an empty list (has size {@code 0})
+     * @throws SecurityException
+     *         if a security manager exists and
+     *         <ul>
+     *         <li>its
+     *         {@link SecurityManager#checkExec checkExec}
+     *         method doesn't allow creation of the subprocess, or
+     *         <li>the standard input to the subprocess was
+     *         {@linkplain #redirectInput redirected from a file}
+     *         and the security manager's
+     *         {@link SecurityManager#checkRead(String) checkRead} method
+     *         denies read access to the file, or
+     *         <li>the standard output or standard error of the
+     *         subprocess was
+     *         {@linkplain #redirectOutput redirected to a file}
+     *         and the security manager's
+     *         {@link SecurityManager#checkWrite(String) checkWrite} method
+     *         denies write access to the file
+     *         </ul>
+     *
+     * @throws  UnsupportedOperationException
+     *          If the operating system does not support the creation of processes
+     *
+     * @throws IOException if an I/O error occurs
+     */
+    public static List<Process> startPipeline(List<ProcessBuilder> builders) throws IOException {
+        // Accumulate and check the builders
+        final int numBuilders = builders.size();
+        List<Process> processes = new ArrayList<>(numBuilders);
+        try {
+            Redirect prevOutput = null;
+            for (int index = 0; index < builders.size(); index++) {
+                ProcessBuilder builder = builders.get(index);
+                Redirect[] redirects = builder.redirects();
+                if (index > 0) {
+                    // check the current Builder to see if it can take input from the previous
+                    if (builder.redirectInput() != Redirect.PIPE) {
+                        throw new IllegalArgumentException("builder redirectInput()" +
+                                " must be PIPE except for the first builder: "
+                                + builder.redirectInput());
+                    }
+                    redirects[0] = prevOutput;
+                }
+                if (index < numBuilders - 1) {
+                    // check all but the last stage has output = PIPE
+                    if (builder.redirectOutput() != Redirect.PIPE) {
+                        throw new IllegalArgumentException("builder redirectOutput()" +
+                                " must be PIPE except for the last builder: "
+                                + builder.redirectOutput());
+                    }
+                    redirects[1] = new RedirectPipeImpl();  // placeholder for new output
+                }
+                processes.add(builder.start(redirects));
+                prevOutput = redirects[1];
+            }
+        } catch (Exception ex) {
+            // Cleanup processes already started
+            processes.forEach(Process::destroyForcibly);
+            processes.forEach(p -> {
+                try {
+                    p.waitFor();        // Wait for it to exit
+                } catch (InterruptedException ie) {
+                    // If interrupted; continue with next Process
+                    Thread.currentThread().interrupt();
+                }
+            });
+            throw ex;
+        }
+        return processes;
+    }
 }
--- a/jdk/src/java.base/share/classes/java/lang/RuntimePermission.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/src/java.base/share/classes/java/lang/RuntimePermission.java	Mon Nov 23 14:37:04 2015 -0500
@@ -348,6 +348,19 @@
  *   {@code java.util.spi.LocaleServiceProvider}</a> for more
  *   information.</td>
  * </tr>
+ *
+ * <tr>
+ *   <td>loggerFinder</td>
+ *   <td>This {@code RuntimePermission} is required to be granted to
+ *   classes which subclass or call methods on
+ *   {@code java.lang.System.LoggerFinder}. The permission is
+ *   checked during invocation of the abstract base class constructor, as
+ *   well as on the invocation of its public methods.
+ *   This permission ensures trust in classes which provide loggers
+ *   to system classes.</td>
+ *   <td>See {@link java.lang.System.LoggerFinder java.lang.System.LoggerFinder}
+ *   for more information.</td>
+ * </tr>
  * </table>
  *
  * @implNote
--- a/jdk/src/java.base/share/classes/java/lang/System.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/src/java.base/share/classes/java/lang/System.java	Mon Nov 23 14:37:04 2015 -0500
@@ -30,13 +30,14 @@
 import java.security.AccessControlContext;
 import java.util.Properties;
 import java.util.PropertyPermission;
-import java.util.StringTokenizer;
 import java.util.Map;
 import java.security.AccessController;
 import java.security.PrivilegedAction;
-import java.security.AllPermission;
 import java.nio.channels.Channel;
 import java.nio.channels.spi.SelectorProvider;
+import java.util.Objects;
+import java.util.ResourceBundle;
+import java.util.function.Supplier;
 import sun.nio.ch.Interruptible;
 import sun.reflect.CallerSensitive;
 import sun.reflect.Reflection;
@@ -45,6 +46,9 @@
 import jdk.internal.HotSpotIntrinsicCandidate;
 import jdk.internal.misc.JavaLangAccess;;
 import jdk.internal.misc.SharedSecrets;;
+import jdk.internal.logger.LoggerFinderLoader;
+import jdk.internal.logger.LazyLoggers;
+import jdk.internal.logger.LocalizedLoggerWrapper;
 
 /**
  * The <code>System</code> class contains several useful class fields
@@ -944,6 +948,648 @@
     }
 
     /**
+     * {@code System.Logger} instances log messages that will be
+     * routed to the underlying logging framework the {@link System.LoggerFinder
+     * LoggerFinder} uses.
+     * <p>
+     * {@code System.Logger} instances are typically obtained from
+     * the {@link java.lang.System System} class, by calling
+     * {@link java.lang.System#getLogger(java.lang.String) System.getLogger(loggerName)}
+     * or {@link java.lang.System#getLogger(java.lang.String, java.util.ResourceBundle)
+     * System.getLogger(loggerName, bundle)}.
+     *
+     * @see java.lang.System#getLogger(java.lang.String)
+     * @see java.lang.System#getLogger(java.lang.String, java.util.ResourceBundle)
+     * @see java.lang.System.LoggerFinder
+     *
+     * @since 9
+     *
+     */
+    public interface Logger {
+
+        /**
+         * System {@linkplain Logger loggers} levels.
+         * <p>
+         * A level has a {@linkplain #getName() name} and {@linkplain
+         * #getSeverity() severity}.
+         * Level values are {@link #ALL}, {@link #TRACE}, {@link #DEBUG},
+         * {@link #INFO}, {@link #WARNING}, {@link #ERROR}, {@link #OFF},
+         * by order of increasing severity.
+         * <br>
+         * {@link #ALL} and {@link #OFF}
+         * are simple markers with severities mapped respectively to
+         * {@link java.lang.Integer#MIN_VALUE Integer.MIN_VALUE} and
+         * {@link java.lang.Integer#MAX_VALUE Integer.MAX_VALUE}.
+         * <p>
+         * <b>Severity values and Mapping to {@code java.util.logging.Level}.</b>
+         * <p>
+         * {@linkplain System.Logger.Level System logger levels} are mapped to
+         * {@linkplain java.util.logging.Level  java.util.logging levels}
+         * of corresponding severity.
+         * <br>The mapping is as follows:
+         * <br><br>
+         * <table border="1">
+         * <caption>System.Logger Severity Level Mapping</caption>
+         * <tr><td><b>System.Logger Levels</b></td>
+         * <td>{@link Logger.Level#ALL ALL}</td>
+         * <td>{@link Logger.Level#TRACE TRACE}</td>
+         * <td>{@link Logger.Level#DEBUG DEBUG}</td>
+         * <td>{@link Logger.Level#INFO INFO}</td>
+         * <td>{@link Logger.Level#WARNING WARNING}</td>
+         * <td>{@link Logger.Level#ERROR ERROR}</td>
+         * <td>{@link Logger.Level#OFF OFF}</td>
+         * </tr>
+         * <tr><td><b>java.util.logging Levels</b></td>
+         * <td>{@link java.util.logging.Level#ALL ALL}</td>
+         * <td>{@link java.util.logging.Level#FINER FINER}</td>
+         * <td>{@link java.util.logging.Level#FINE FINE}</td>
+         * <td>{@link java.util.logging.Level#INFO INFO}</td>
+         * <td>{@link java.util.logging.Level#WARNING WARNING}</td>
+         * <td>{@link java.util.logging.Level#SEVERE SEVERE}</td>
+         * <td>{@link java.util.logging.Level#OFF OFF}</td>
+         * </tr>
+         * </table>
+         *
+         * @since 9
+         *
+         * @see java.lang.System.LoggerFinder
+         * @see java.lang.System.Logger
+         */
+        public enum Level {
+
+            // for convenience, we're reusing java.util.logging.Level int values
+            // the mapping logic in sun.util.logging.PlatformLogger depends
+            // on this.
+            /**
+             * A marker to indicate that all levels are enabled.
+             * This level {@linkplain #getSeverity() severity} is
+             * {@link Integer#MIN_VALUE}.
+             */
+            ALL(Integer.MIN_VALUE),  // typically mapped to/from j.u.l.Level.ALL
+            /**
+             * {@code TRACE} level: usually used to log diagnostic information.
+             * This level {@linkplain #getSeverity() severity} is
+             * {@code 400}.
+             */
+            TRACE(400),   // typically mapped to/from j.u.l.Level.FINER
+            /**
+             * {@code DEBUG} level: usually used to log debug information traces.
+             * This level {@linkplain #getSeverity() severity} is
+             * {@code 500}.
+             */
+            DEBUG(500),   // typically mapped to/from j.u.l.Level.FINEST/FINE/CONFIG
+            /**
+             * {@code INFO} level: usually used to log information messages.
+             * This level {@linkplain #getSeverity() severity} is
+             * {@code 800}.
+             */
+            INFO(800),    // typically mapped to/from j.u.l.Level.INFO
+            /**
+             * {@code WARNING} level: usually used to log warning messages.
+             * This level {@linkplain #getSeverity() severity} is
+             * {@code 900}.
+             */
+            WARNING(900), // typically mapped to/from j.u.l.Level.WARNING
+            /**
+             * {@code ERROR} level: usually used to log error messages.
+             * This level {@linkplain #getSeverity() severity} is
+             * {@code 1000}.
+             */
+            ERROR(1000),  // typically mapped to/from j.u.l.Level.SEVERE
+            /**
+             * A marker to indicate that all levels are disabled.
+             * This level {@linkplain #getSeverity() severity} is
+             * {@link Integer#MAX_VALUE}.
+             */
+            OFF(Integer.MAX_VALUE);  // typically mapped to/from j.u.l.Level.OFF
+
+            private final int severity;
+
+            private Level(int severity) {
+                this.severity = severity;
+            }
+
+            /**
+             * Returns the name of this level.
+             * @return this level {@linkplain #name()}.
+             */
+            public final String getName() {
+                return name();
+            }
+
+            /**
+             * Returns the severity of this level.
+             * A higher severity means a more severe condition.
+             * @return this level severity.
+             */
+            public final int getSeverity() {
+                return severity;
+            }
+        }
+
+        /**
+         * Returns the name of this logger.
+         *
+         * @return the logger name.
+         */
+        public String getName();
+
+        /**
+         * Checks if a message of the given level would be logged by
+         * this logger.
+         *
+         * @param level the log message level.
+         * @return {@code true} if the given log message level is currently
+         *         being logged.
+         *
+         * @throws NullPointerException if {@code level} is {@code null}.
+         */
+        public boolean isLoggable(Level level);
+
+        /**
+         * Logs a message.
+         *
+         * @implSpec The default implementation for this method calls
+         * {@code this.log(level, (ResourceBundle)null, msg, (Object[])null);}
+         *
+         * @param level the log message level.
+         * @param msg the string message (or a key in the message catalog, if
+         * this logger is a {@link
+         * LoggerFinder#getLocalizedLogger(java.lang.String, java.util.ResourceBundle, java.lang.Class)
+         * localized logger}); can be {@code null}.
+         *
+         * @throws NullPointerException if {@code level} is {@code null}.
+         */
+        public default void log(Level level, String msg) {
+            log(level, (ResourceBundle) null, msg, (Object[]) null);
+        }
+
+        /**
+         * Logs a lazily supplied message.
+         * <p>
+         * If the logger is currently enabled for the given log message level
+         * then a message is logged that is the result produced by the
+         * given supplier function.  Otherwise, the supplier is not operated on.
+         *
+         * @implSpec When logging is enabled for the given level, the default
+         * implementation for this method calls
+         * {@code this.log(level, (ResourceBundle)null, msgSupplier.get(), (Object[])null);}
+         *
+         * @param level the log message level.
+         * @param msgSupplier a supplier function that produces a message.
+         *
+         * @throws NullPointerException if {@code level} is {@code null},
+         *         or {@code msgSupplier} is {@code null}.
+         */
+        public default void log(Level level, Supplier<String> msgSupplier) {
+            Objects.requireNonNull(msgSupplier);
+            if (isLoggable(Objects.requireNonNull(level))) {
+                log(level, (ResourceBundle) null, msgSupplier.get(), (Object[]) null);
+            }
+        }
+
+        /**
+         * Logs a message produced from the given object.
+         * <p>
+         * If the logger is currently enabled for the given log message level then
+         * a message is logged that, by default, is the result produced from
+         * calling  toString on the given object.
+         * Otherwise, the object is not operated on.
+         *
+         * @implSpec When logging is enabled for the given level, the default
+         * implementation for this method calls
+         * {@code this.log(level, (ResourceBundle)null, obj.toString(), (Object[])null);}
+         *
+         * @param level the log message level.
+         * @param obj the object to log.
+         *
+         * @throws NullPointerException if {@code level} is {@code null}, or
+         *         {@code obj} is {@code null}.
+         */
+        public default void log(Level level, Object obj) {
+            Objects.requireNonNull(obj);
+            if (isLoggable(Objects.requireNonNull(level))) {
+                this.log(level, (ResourceBundle) null, obj.toString(), (Object[]) null);
+            }
+        }
+
+        /**
+         * Logs a message associated with a given throwable.
+         *
+         * @implSpec The default implementation for this method calls
+         * {@code this.log(level, (ResourceBundle)null, msg, thrown);}
+         *
+         * @param level the log message level.
+         * @param msg the string message (or a key in the message catalog, if
+         * this logger is a {@link
+         * LoggerFinder#getLocalizedLogger(java.lang.String, java.util.ResourceBundle, java.lang.Class)
+         * localized logger}); can be {@code null}.
+         * @param thrown a {@code Throwable} associated with the log message;
+         *        can be {@code null}.
+         *
+         * @throws NullPointerException if {@code level} is {@code null}.
+         */
+        public default void log(Level level, String msg, Throwable thrown) {
+            this.log(level, null, msg, thrown);
+        }
+
+        /**
+         * Logs a lazily supplied message associated with a given throwable.
+         * <p>
+         * If the logger is currently enabled for the given log message level
+         * then a message is logged that is the result produced by the
+         * given supplier function.  Otherwise, the supplier is not operated on.
+         *
+         * @implSpec When logging is enabled for the given level, the default
+         * implementation for this method calls
+         * {@code this.log(level, (ResourceBundle)null, msgSupplier.get(), thrown);}
+         *
+         * @param level one of the log message level identifiers.
+         * @param msgSupplier a supplier function that produces a message.
+         * @param thrown a {@code Throwable} associated with log message;
+         *               can be {@code null}.
+         *
+         * @throws NullPointerException if {@code level} is {@code null}, or
+         *                               {@code msgSupplier} is {@code null}.
+         */
+        public default void log(Level level, Supplier<String> msgSupplier,
+                Throwable thrown) {
+            Objects.requireNonNull(msgSupplier);
+            if (isLoggable(Objects.requireNonNull(level))) {
+                this.log(level, null, msgSupplier.get(), thrown);
+            }
+        }
+
+        /**
+         * Logs a message with an optional list of parameters.
+         *
+         * @implSpec The default implementation for this method calls
+         * {@code this.log(level, (ResourceBundle)null, format, params);}
+         *
+         * @param level one of the log message level identifiers.
+         * @param format the string message format in {@link
+         * java.text.MessageFormat} format, (or a key in the message
+         * catalog, if this logger is a {@link
+         * LoggerFinder#getLocalizedLogger(java.lang.String, java.util.ResourceBundle, java.lang.Class)
+         * localized logger}); can be {@code null}.
+         * @param params an optional list of parameters to the message (may be
+         * none).
+         *
+         * @throws NullPointerException if {@code level} is {@code null}.
+         */
+        public default void log(Level level, String format, Object... params) {
+            this.log(level, null, format, params);
+        }
+
+        /**
+         * Logs a localized message associated with a given throwable.
+         * <p>
+         * If the given resource bundle is non-{@code null},  the {@code msg}
+         * string is localized using the given resource bundle.
+         * Otherwise the {@code msg} string is not localized.
+         *
+         * @param level the log message level.
+         * @param bundle a resource bundle to localize {@code msg}; can be
+         * {@code null}.
+         * @param msg the string message (or a key in the message catalog,
+         *            if {@code bundle} is not {@code null}); can be {@code null}.
+         * @param thrown a {@code Throwable} associated with the log message;
+         *        can be {@code null}.
+         *
+         * @throws NullPointerException if {@code level} is {@code null}.
+         */
+        public void log(Level level, ResourceBundle bundle, String msg,
+                Throwable thrown);
+
+        /**
+         * Logs a message with resource bundle and an optional list of
+         * parameters.
+         * <p>
+         * If the given resource bundle is non-{@code null},  the {@code format}
+         * string is localized using the given resource bundle.
+         * Otherwise the {@code format} string is not localized.
+         *
+         * @param level the log message level.
+         * @param bundle a resource bundle to localize {@code format}; can be
+         * {@code null}.
+         * @param format the string message format in {@link
+         * java.text.MessageFormat} format, (or a key in the message
+         * catalog if {@code bundle} is not {@code null}); can be {@code null}.
+         * @param params an optional list of parameters to the message (may be
+         * none).
+         *
+         * @throws NullPointerException if {@code level} is {@code null}.
+         */
+        public void log(Level level, ResourceBundle bundle, String format,
+                Object... params);
+
+
+    }
+
+    /**
+     * The {@code LoggerFinder} service is responsible for creating, managing,
+     * and configuring loggers to the underlying framework it uses.
+     * <p>
+     * A logger finder is a concrete implementation of this class that has a
+     * zero-argument constructor and implements the abstract methods defined
+     * by this class.
+     * The loggers returned from a logger finder are capable of routing log
+     * messages to the logging backend this provider supports.
+     * A given invocation of the Java Runtime maintains a single
+     * system-wide LoggerFinder instance that is loaded as follows:
+     * <ul>
+     *    <li>First it finds any custom {@code LoggerFinder} provider
+     *        using the {@link java.util.ServiceLoader} facility with the
+     *        {@linkplain ClassLoader#getSystemClassLoader() system class
+     *        loader}.</li>
+     *    <li>If no {@code LoggerFinder} provider is found, the system default
+     *        {@code LoggerFinder} implementation will be used.</li>
+     * </ul>
+     * <p>
+     * An application can replace the logging backend
+     * <i>even when the java.logging module is present</i>, by simply providing
+     * and declaring an implementation of the {@link LoggerFinder} service.
+     * <p>
+     * <b>Default Implementation</b>
+     * <p>
+     * The system default {@code LoggerFinder} implementation uses
+     * {@code java.util.logging} as the backend framework when the
+     * {@code java.logging} module is present.
+     * It returns a {@linkplain System.Logger logger} instance
+     * that will route log messages to a {@link java.util.logging.Logger
+     * java.util.logging.Logger}. Otherwise, if {@code java.logging} is not
+     * present, the default implementation will return a simple logger
+     * instance that will route log messages of {@code INFO} level and above to
+     * the console ({@code System.err}).
+     * <p>
+     * <b>Logging Configuration</b>
+     * <p>
+     * {@linkplain Logger Logger} instances obtained from the
+     * {@code LoggerFinder} factory methods are not directly configurable by
+     * the application. Configuration is the responsibility of the underlying
+     * logging backend, and usually requires using APIs specific to that backend.
+     * <p>For the default {@code LoggerFinder} implementation
+     * using {@code java.util.logging} as its backend, refer to
+     * {@link java.util.logging java.util.logging} for logging configuration.
+     * For the default {@code LoggerFinder} implementation returning simple loggers
+     * when the {@code java.logging} module is absent, the configuration
+     * is implementation dependent.
+     * <p>
+     * Usually an application that uses a logging framework will log messages
+     * through a logger facade defined (or supported) by that framework.
+     * Applications that wish to use an external framework should log
+     * through the facade associated with that framework.
+     * <p>
+     * A system class that needs to log messages will typically obtain
+     * a {@link System.Logger} instance to route messages to the logging
+     * framework selected by the application.
+     * <p>
+     * Libraries and classes that only need loggers to produce log messages
+     * should not attempt to configure loggers by themselves, as that
+     * would make them dependent from a specific implementation of the
+     * {@code LoggerFinder} service.
+     * <p>
+     * In addition, when a security manager is present, loggers provided to
+     * system classes should not be directly configurable through the logging
+     * backend without requiring permissions.
+     * <br>
+     * It is the responsibility of the provider of
+     * the concrete {@code LoggerFinder} implementation to ensure that
+     * these loggers are not configured by untrusted code without proper
+     * permission checks, as configuration performed on such loggers usually
+     * affects all applications in the same Java Runtime.
+     * <p>
+     * <b>Message Levels and Mapping to backend levels</b>
+     * <p>
+     * A logger finder is responsible for mapping from a {@code
+     * System.Logger.Level} to a level supported by the logging backend it uses.
+     * <br>The default LoggerFinder using {@code java.util.logging} as the backend
+     * maps {@code System.Logger} levels to
+     * {@linkplain java.util.logging.Level java.util.logging} levels
+     * of corresponding severity - as described in {@link Logger.Level
+     * Logger.Level}.
+     *
+     * @see java.lang.System
+     * @see java.lang.System.Logger
+     *
+     * @since 9
+     */
+    public static abstract class LoggerFinder {
+        /**
+         * The {@code RuntimePermission("loggerFinder")} is
+         * necessary to subclass and instantiate the {@code LoggerFinder} class,
+         * as well as to obtain loggers from an instance of that class.
+         */
+        static final RuntimePermission LOGGERFINDER_PERMISSION =
+                new RuntimePermission("loggerFinder");
+
+        /**
+         * Creates a new instance of {@code LoggerFinder}.
+         *
+         * @implNote It is recommended that a {@code LoggerFinder} service
+         *   implementation does not perform any heavy initialization in its
+         *   constructor, in order to avoid possible risks of deadlock or class
+         *   loading cycles during the instantiation of the service provider.
+         *
+         * @throws SecurityException if a security manager is present and its
+         *         {@code checkPermission} method doesn't allow the
+         *         {@code RuntimePermission("loggerFinder")}.
+         */
+        protected LoggerFinder() {
+            this(checkPermission());
+        }
+
+        private LoggerFinder(Void unused) {
+            // nothing to do.
+        }
+
+        private static Void checkPermission() {
+            final SecurityManager sm = System.getSecurityManager();
+            if (sm != null) {
+                sm.checkPermission(LOGGERFINDER_PERMISSION);
+            }
+            return null;
+        }
+
+        /**
+         * Returns an instance of {@link Logger Logger}
+         * for the given {@code caller}.
+         *
+         * @param name the name of the logger.
+         * @param caller the class for which the logger is being requested;
+         *               can be {@code null}.
+         *
+         * @return a {@link Logger logger} suitable for the given caller's
+         *         use.
+         * @throws NullPointerException if {@code name} is {@code null} or
+         *        {@code caller} is {@code null}.
+         * @throws SecurityException if a security manager is present and its
+         *         {@code checkPermission} method doesn't allow the
+         *         {@code RuntimePermission("loggerFinder")}.
+         */
+        public abstract Logger getLogger(String name, /* Module */ Class<?> caller);
+
+        /**
+         * Returns a localizable instance of {@link Logger Logger}
+         * for the given {@code caller}.
+         * The returned logger will use the provided resource bundle for
+         * message localization.
+         *
+         * @implSpec By default, this method calls {@link
+         * #getLogger(java.lang.String, java.lang.Class)
+         * this.getLogger(name, caller)} to obtain a logger, then wraps that
+         * logger in a {@link Logger} instance where all methods that do not
+         * take a {@link ResourceBundle} as parameter are redirected to one
+         * which does - passing the given {@code bundle} for
+         * localization. So for instance, a call to {@link
+         * Logger#log(Level, String) Logger.log(Level.INFO, msg)}
+         * will end up as a call to {@link
+         * Logger#log(Level, ResourceBundle, String, Object...)
+         * Logger.log(Level.INFO, bundle, msg, (Object[])null)} on the wrapped
+         * logger instance.
+         * Note however that by default, string messages returned by {@link
+         * java.util.function.Supplier Supplier&lt;String&gt;} will not be
+         * localized, as it is assumed that such strings are messages which are
+         * already constructed, rather than keys in a resource bundle.
+         * <p>
+         * An implementation of {@code LoggerFinder} may override this method,
+         * for example, when the underlying logging backend provides its own
+         * mechanism for localizing log messages, then such a
+         * {@code LoggerFinder} would be free to return a logger
+         * that makes direct use of the mechanism provided by the backend.
+         *
+         * @param name    the name of the logger.
+         * @param bundle  a resource bundle; can be {@code null}.
+         * @param caller the class for which the logger is being requested.
+         * @return an instance of {@link Logger Logger}  which will use the
+         * provided resource bundle for message localization.
+         *
+         * @throws NullPointerException if {@code name} is {@code null} or
+         *         {@code caller} is {@code null}.
+         * @throws SecurityException if a security manager is present and its
+         *         {@code checkPermission} method doesn't allow the
+         *         {@code RuntimePermission("loggerFinder")}.
+         */
+        public Logger getLocalizedLogger(String name, ResourceBundle bundle,
+                                          /* Module */ Class<?> caller) {
+            return new LocalizedLoggerWrapper<>(getLogger(name, caller), bundle);
+        }
+
+        /**
+         * Returns the {@code LoggerFinder} instance. There is one
+         * single system-wide {@code LoggerFinder} instance in
+         * the Java Runtime.  See the class specification of how the
+         * {@link LoggerFinder LoggerFinder} implementation is located and
+         * loaded.
+
+         * @return the {@link LoggerFinder LoggerFinder} instance.
+         * @throws SecurityException if a security manager is present and its
+         *         {@code checkPermission} method doesn't allow the
+         *         {@code RuntimePermission("loggerFinder")}.
+         */
+        public static LoggerFinder getLoggerFinder() {
+            final SecurityManager sm = System.getSecurityManager();
+            if (sm != null) {
+                sm.checkPermission(LOGGERFINDER_PERMISSION);
+            }
+            return accessProvider();
+        }
+
+
+        private static volatile LoggerFinder service;
+        static LoggerFinder accessProvider() {
+            // We do not need to synchronize: LoggerFinderLoader will
+            // always return the same instance, so if we don't have it,
+            // just fetch it again.
+            if (service == null) {
+                PrivilegedAction<LoggerFinder> pa =
+                        () -> LoggerFinderLoader.getLoggerFinder();
+                service = AccessController.doPrivileged(pa, null,
+                        LOGGERFINDER_PERMISSION);
+            }
+            return service;
+        }
+
+    }
+
+
+    /**
+     * Returns an instance of {@link Logger Logger} for the caller's
+     * use.
+     *
+     * @implSpec
+     * Instances returned by this method route messages to loggers
+     * obtained by calling {@link LoggerFinder#getLogger(java.lang.String, java.lang.Class)
+     * LoggerFinder.getLogger(name, caller)}.
+     *
+     * @apiNote
+     * This method may defer calling the {@link
+     * LoggerFinder#getLogger(java.lang.String, java.lang.Class)
+     * LoggerFinder.getLogger} method to create an actual logger supplied by
+     * the logging backend, for instance, to allow loggers to be obtained during
+     * the system initialization time.
+     *
+     * @param name the name of the logger.
+     * @return an instance of {@link Logger} that can be used by the calling
+     *         class.
+     * @throws NullPointerException if {@code name} is {@code null}.
+     */
+    @CallerSensitive
+    public static Logger getLogger(String name) {
+        Objects.requireNonNull(name);
+        final Class<?> caller = Reflection.getCallerClass();
+        return LazyLoggers.getLogger(name, caller);
+    }
+
+    /**
+     * Returns a localizable instance of {@link Logger
+     * Logger} for the caller's use.
+     * The returned logger will use the provided resource bundle for message
+     * localization.
+     *
+     * @implSpec
+     * The returned logger will perform message localization as specified
+     * by {@link LoggerFinder#getLocalizedLogger(java.lang.String,
+     * java.util.ResourceBundle, java.lang.Class)
+     * LoggerFinder.getLocalizedLogger(name, bundle, caller}.
+     *
+     * @apiNote
+     * This method is intended to be used after the system is fully initialized.
+     * This method may trigger the immediate loading and initialization
+     * of the {@link LoggerFinder} service, which may cause issues if the
+     * Java Runtime is not ready to initialize the concrete service
+     * implementation yet.
+     * System classes which may be loaded early in the boot sequence and
+     * need to log localized messages should create a logger using
+     * {@link #getLogger(java.lang.String)} and then use the log methods that
+     * take a resource bundle as parameter.
+     *
+     * @param name    the name of the logger.
+     * @param bundle  a resource bundle.
+     * @return an instance of {@link Logger} which will use the provided
+     * resource bundle for message localization.
+     * @throws NullPointerException if {@code name} is {@code null} or
+     *         {@code bundle} is {@code null}.
+     */
+    @CallerSensitive
+    public static Logger getLogger(String name, ResourceBundle bundle) {
+        final ResourceBundle rb = Objects.requireNonNull(bundle);
+        Objects.requireNonNull(name);
+        final Class<?> caller = Reflection.getCallerClass();
+        final SecurityManager sm = System.getSecurityManager();
+        // We don't use LazyLoggers if a resource bundle is specified.
+        // Bootstrap sensitive classes in the JDK do not use resource bundles
+        // when logging. This could be revisited later, if it needs to.
+        if (sm != null) {
+            return AccessController.doPrivileged((PrivilegedAction<Logger>)
+                    () -> LoggerFinder.accessProvider().getLocalizedLogger(name, rb, caller),
+                    null,
+                    LoggerFinder.LOGGERFINDER_PERMISSION);
+        }
+        return LoggerFinder.accessProvider().getLocalizedLogger(name, rb, caller);
+    }
+
+    /**
      * Terminates the currently running Java Virtual Machine. The
      * argument serves as a status code; by convention, a nonzero status
      * code indicates abnormal termination.
--- a/jdk/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java	Mon Nov 23 14:37:04 2015 -0500
@@ -224,12 +224,12 @@
         assert(names.length == nameCursor);
         if (doesAlloc) {
             // names = { argx,y,z,... new C, init method }
-            names[NEW_OBJ] = new Name(Lazy.NF_allocateInstance, names[DMH_THIS]);
-            names[GET_MEMBER] = new Name(Lazy.NF_constructorMethod, names[DMH_THIS]);
+            names[NEW_OBJ] = new Name(NF_allocateInstance, names[DMH_THIS]);
+            names[GET_MEMBER] = new Name(NF_constructorMethod, names[DMH_THIS]);
         } else if (needsInit) {
-            names[GET_MEMBER] = new Name(Lazy.NF_internalMemberNameEnsureInit, names[DMH_THIS]);
+            names[GET_MEMBER] = new Name(NF_internalMemberNameEnsureInit, names[DMH_THIS]);
         } else {
-            names[GET_MEMBER] = new Name(Lazy.NF_internalMemberName, names[DMH_THIS]);
+            names[GET_MEMBER] = new Name(NF_internalMemberName, names[DMH_THIS]);
         }
         assert(findDirectMethodHandle(names[GET_MEMBER]) == names[DMH_THIS]);
         Object[] outArgs = Arrays.copyOfRange(names, ARG_BASE, GET_MEMBER+1, Object[].class);
@@ -250,9 +250,9 @@
     }
 
     static Object findDirectMethodHandle(Name name) {
-        if (name.function == Lazy.NF_internalMemberName ||
-            name.function == Lazy.NF_internalMemberNameEnsureInit ||
-            name.function == Lazy.NF_constructorMethod) {
+        if (name.function == NF_internalMemberName ||
+            name.function == NF_internalMemberNameEnsureInit ||
+            name.function == NF_constructorMethod) {
             assert(name.arguments.length == 1);
             return name.arguments[0];
         }
@@ -613,18 +613,18 @@
         final int RESULT    = nameCursor-1;  // either the call or the cast
         Name[] names = arguments(nameCursor - ARG_LIMIT, mtype.invokerType());
         if (needsInit)
-            names[INIT_BAR] = new Name(Lazy.NF_ensureInitialized, names[DMH_THIS]);
+            names[INIT_BAR] = new Name(NF_ensureInitialized, names[DMH_THIS]);
         if (needsCast && !isGetter)
-            names[PRE_CAST] = new Name(Lazy.NF_checkCast, names[DMH_THIS], names[SET_VALUE]);
+            names[PRE_CAST] = new Name(NF_checkCast, names[DMH_THIS], names[SET_VALUE]);
         Object[] outArgs = new Object[1 + linkerType.parameterCount()];
         assert(outArgs.length == (isGetter ? 3 : 4));
         outArgs[0] = UNSAFE;
         if (isStatic) {
-            outArgs[1] = names[F_HOLDER]  = new Name(Lazy.NF_staticBase, names[DMH_THIS]);
-            outArgs[2] = names[F_OFFSET]  = new Name(Lazy.NF_staticOffset, names[DMH_THIS]);
+            outArgs[1] = names[F_HOLDER]  = new Name(NF_staticBase, names[DMH_THIS]);
+            outArgs[2] = names[F_OFFSET]  = new Name(NF_staticOffset, names[DMH_THIS]);
         } else {
-            outArgs[1] = names[OBJ_CHECK] = new Name(Lazy.NF_checkBase, names[OBJ_BASE]);
-            outArgs[2] = names[F_OFFSET]  = new Name(Lazy.NF_fieldOffset, names[DMH_THIS]);
+            outArgs[1] = names[OBJ_CHECK] = new Name(NF_checkBase, names[OBJ_BASE]);
+            outArgs[2] = names[F_OFFSET]  = new Name(NF_fieldOffset, names[DMH_THIS]);
         }
         if (!isGetter) {
             outArgs[3] = (needsCast ? names[PRE_CAST] : names[SET_VALUE]);
@@ -632,7 +632,7 @@
         for (Object a : outArgs)  assert(a != null);
         names[LINKER_CALL] = new Name(linker, outArgs);
         if (needsCast && isGetter)
-            names[POST_CAST] = new Name(Lazy.NF_checkCast, names[DMH_THIS], names[LINKER_CALL]);
+            names[POST_CAST] = new Name(NF_checkCast, names[DMH_THIS], names[LINKER_CALL]);
         for (Name n : names)  assert(n != null);
         String fieldOrStatic = (isStatic ? "Static" : "Field");
         String lambdaName = (linkerName + fieldOrStatic);  // significant only for debugging
@@ -645,50 +645,45 @@
      * Pre-initialized NamedFunctions for bootstrapping purposes.
      * Factored in an inner class to delay initialization until first usage.
      */
-    private static class Lazy {
-        static final NamedFunction
-                NF_internalMemberName,
-                NF_internalMemberNameEnsureInit,
-                NF_ensureInitialized,
-                NF_fieldOffset,
-                NF_checkBase,
-                NF_staticBase,
-                NF_staticOffset,
-                NF_checkCast,
-                NF_allocateInstance,
-                NF_constructorMethod;
-        static {
-            try {
-                NamedFunction nfs[] = {
-                        NF_internalMemberName = new NamedFunction(DirectMethodHandle.class
-                                .getDeclaredMethod("internalMemberName", Object.class)),
-                        NF_internalMemberNameEnsureInit = new NamedFunction(DirectMethodHandle.class
-                                .getDeclaredMethod("internalMemberNameEnsureInit", Object.class)),
-                        NF_ensureInitialized = new NamedFunction(DirectMethodHandle.class
-                                .getDeclaredMethod("ensureInitialized", Object.class)),
-                        NF_fieldOffset = new NamedFunction(DirectMethodHandle.class
-                                .getDeclaredMethod("fieldOffset", Object.class)),
-                        NF_checkBase = new NamedFunction(DirectMethodHandle.class
-                                .getDeclaredMethod("checkBase", Object.class)),
-                        NF_staticBase = new NamedFunction(DirectMethodHandle.class
-                                .getDeclaredMethod("staticBase", Object.class)),
-                        NF_staticOffset = new NamedFunction(DirectMethodHandle.class
-                                .getDeclaredMethod("staticOffset", Object.class)),
-                        NF_checkCast = new NamedFunction(DirectMethodHandle.class
-                                .getDeclaredMethod("checkCast", Object.class, Object.class)),
-                        NF_allocateInstance = new NamedFunction(DirectMethodHandle.class
-                                .getDeclaredMethod("allocateInstance", Object.class)),
-                        NF_constructorMethod = new NamedFunction(DirectMethodHandle.class
-                                .getDeclaredMethod("constructorMethod", Object.class))
-                };
-                for (NamedFunction nf : nfs) {
-                    // Each nf must be statically invocable or we get tied up in our bootstraps.
-                    assert(InvokerBytecodeGenerator.isStaticallyInvocable(nf.member)) : nf;
-                    nf.resolve();
-                }
-            } catch (ReflectiveOperationException ex) {
-                throw newInternalError(ex);
-            }
+    static final NamedFunction
+            NF_internalMemberName,
+            NF_internalMemberNameEnsureInit,
+            NF_ensureInitialized,
+            NF_fieldOffset,
+            NF_checkBase,
+            NF_staticBase,
+            NF_staticOffset,
+            NF_checkCast,
+            NF_allocateInstance,
+            NF_constructorMethod;
+    static {
+        try {
+            NamedFunction nfs[] = {
+                    NF_internalMemberName = new NamedFunction(DirectMethodHandle.class
+                            .getDeclaredMethod("internalMemberName", Object.class)),
+                    NF_internalMemberNameEnsureInit = new NamedFunction(DirectMethodHandle.class
+                            .getDeclaredMethod("internalMemberNameEnsureInit", Object.class)),
+                    NF_ensureInitialized = new NamedFunction(DirectMethodHandle.class
+                            .getDeclaredMethod("ensureInitialized", Object.class)),
+                    NF_fieldOffset = new NamedFunction(DirectMethodHandle.class
+                            .getDeclaredMethod("fieldOffset", Object.class)),
+                    NF_checkBase = new NamedFunction(DirectMethodHandle.class
+                            .getDeclaredMethod("checkBase", Object.class)),
+                    NF_staticBase = new NamedFunction(DirectMethodHandle.class
+                            .getDeclaredMethod("staticBase", Object.class)),
+                    NF_staticOffset = new NamedFunction(DirectMethodHandle.class
+                            .getDeclaredMethod("staticOffset", Object.class)),
+                    NF_checkCast = new NamedFunction(DirectMethodHandle.class
+                            .getDeclaredMethod("checkCast", Object.class, Object.class)),
+                    NF_allocateInstance = new NamedFunction(DirectMethodHandle.class
+                            .getDeclaredMethod("allocateInstance", Object.class)),
+                    NF_constructorMethod = new NamedFunction(DirectMethodHandle.class
+                            .getDeclaredMethod("constructorMethod", Object.class))
+            };
+            // Each nf must be statically invocable or we get tied up in our bootstraps.
+            assert(InvokerBytecodeGenerator.isStaticallyInvocable(nfs));
+        } catch (ReflectiveOperationException ex) {
+            throw newInternalError(ex);
         }
     }
 }
--- a/jdk/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java	Mon Nov 23 14:37:04 2015 -0500
@@ -750,7 +750,7 @@
         assert(!isLinkerMethodInvoke(name));  // should use the static path for these
         if (true) {
             // push receiver
-            MethodHandle target = name.function.resolvedHandle;
+            MethodHandle target = name.function.resolvedHandle();
             assert(target != null) : name.exprString();
             mv.visitLdcInsn(constantPlaceholder(target));
             emitReferenceCast(MethodHandle.class, target);
@@ -779,6 +779,15 @@
         //MethodHandle.class already covered
     };
 
+    static boolean isStaticallyInvocable(NamedFunction[] functions) {
+        for (NamedFunction nf : functions) {
+            if (!isStaticallyInvocable(nf.member())) {
+                return false;
+            }
+        }
+        return true;
+    }
+
     static boolean isStaticallyInvocable(Name name) {
         return isStaticallyInvocable(name.function.member());
     }
@@ -881,7 +890,7 @@
             // The array will be a constant.
             Object emptyArray;
             try {
-                emptyArray = name.function.resolvedHandle.invoke();
+                emptyArray = name.function.resolvedHandle().invoke();
             } catch (Throwable ex) {
                 throw newInternalError(ex);
             }
@@ -1085,8 +1094,8 @@
         Label L_handler = new Label();
         Label L_done = new Label();
 
-        Class<?> returnType = result.function.resolvedHandle.type().returnType();
-        MethodType type = args.function.resolvedHandle.type()
+        Class<?> returnType = result.function.resolvedHandle().type().returnType();
+        MethodType type = args.function.resolvedHandle().type()
                               .dropParameterTypes(0,1)
                               .changeReturnType(returnType);
 
--- a/jdk/src/java.base/share/classes/java/lang/invoke/Invokers.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/Invokers.java	Mon Nov 23 14:37:04 2015 -0500
@@ -429,11 +429,8 @@
                 NF_checkCustomized = new NamedFunction(Invokers.class
                         .getDeclaredMethod("checkCustomized", Object.class))
             };
-            for (NamedFunction nf : nfs) {
-                // Each nf must be statically invocable or we get tied up in our bootstraps.
-                assert(InvokerBytecodeGenerator.isStaticallyInvocable(nf.member)) : nf;
-                nf.resolve();
-            }
+            // Each nf must be statically invocable or we get tied up in our bootstraps.
+            assert(InvokerBytecodeGenerator.isStaticallyInvocable(nfs));
         } catch (ReflectiveOperationException ex) {
             throw newInternalError(ex);
         }
--- a/jdk/src/java.base/share/classes/java/lang/invoke/LambdaForm.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/LambdaForm.java	Mon Nov 23 14:37:04 2015 -0500
@@ -1024,7 +1024,7 @@
 
     static class NamedFunction {
         final MemberName member;
-        @Stable MethodHandle resolvedHandle;
+        private @Stable MethodHandle resolvedHandle;
         @Stable MethodHandle invoker;
 
         NamedFunction(MethodHandle resolvedHandle) {
@@ -1074,8 +1074,10 @@
             return resolvedHandle;
         }
 
-        void resolve() {
-            resolvedHandle = DirectMethodHandle.make(member);
+        synchronized void resolve() {
+            if (resolvedHandle == null) {
+                resolvedHandle = DirectMethodHandle.make(member);
+            }
         }
 
         @Override
@@ -1235,6 +1237,7 @@
                     traceInterpreter("| getInvoker", this);
                     invoker();
                 }
+                // resolvedHandle might be uninitialized, ok for tracing
                 if (resolvedHandle == null) {
                     traceInterpreter("| resolve", this);
                     resolvedHandle();
@@ -1704,88 +1707,112 @@
     private static final MemberName.Factory IMPL_NAMES = MemberName.getFactory();
 
     static LambdaForm identityForm(BasicType type) {
-        return LF_identityForm[type.ordinal()];
-    }
-    static LambdaForm zeroForm(BasicType type) {
-        return LF_zeroForm[type.ordinal()];
+        int ord = type.ordinal();
+        LambdaForm form = LF_identity[ord];
+        if (form != null) {
+            return form;
+        }
+        createFormsFor(type);
+        return LF_identity[ord];
     }
-    static NamedFunction identity(BasicType type) {
-        return NF_identity[type.ordinal()];
+
+    static LambdaForm zeroForm(BasicType type) {
+        int ord = type.ordinal();
+        LambdaForm form = LF_zero[ord];
+        if (form != null) {
+            return form;
+        }
+        createFormsFor(type);
+        return LF_zero[ord];
     }
-    static NamedFunction constantZero(BasicType type) {
-        return NF_zero[type.ordinal()];
+
+    static NamedFunction identity(BasicType type) {
+        int ord = type.ordinal();
+        NamedFunction function = NF_identity[ord];
+        if (function != null) {
+            return function;
+        }
+        createFormsFor(type);
+        return NF_identity[ord];
     }
-    private static final LambdaForm[] LF_identityForm = new LambdaForm[TYPE_LIMIT];
-    private static final LambdaForm[] LF_zeroForm = new LambdaForm[TYPE_LIMIT];
-    private static final NamedFunction[] NF_identity = new NamedFunction[TYPE_LIMIT];
-    private static final NamedFunction[] NF_zero = new NamedFunction[TYPE_LIMIT];
-    private static void createIdentityForms() {
-        for (BasicType type : BasicType.ALL_TYPES) {
-            int ord = type.ordinal();
-            char btChar = type.basicTypeChar();
-            boolean isVoid = (type == V_TYPE);
-            Class<?> btClass = type.btClass;
-            MethodType zeType = MethodType.methodType(btClass);
-            MethodType idType = isVoid ? zeType : zeType.appendParameterTypes(btClass);
+
+    static NamedFunction constantZero(BasicType type) {
+        int ord = type.ordinal();
+        NamedFunction function = NF_zero[ord];
+        if (function != null) {
+            return function;
+        }
+        createFormsFor(type);
+        return NF_zero[ord];
+    }
+
+    private static final @Stable LambdaForm[] LF_identity = new LambdaForm[TYPE_LIMIT];
+    private static final @Stable LambdaForm[] LF_zero = new LambdaForm[TYPE_LIMIT];
+    private static final @Stable NamedFunction[] NF_identity = new NamedFunction[TYPE_LIMIT];
+    private static final @Stable NamedFunction[] NF_zero = new NamedFunction[TYPE_LIMIT];
 
-            // Look up some symbolic names.  It might not be necessary to have these,
-            // but if we need to emit direct references to bytecodes, it helps.
-            // Zero is built from a call to an identity function with a constant zero input.
-            MemberName idMem = new MemberName(LambdaForm.class, "identity_"+btChar, idType, REF_invokeStatic);
-            MemberName zeMem = new MemberName(LambdaForm.class, "zero_"+btChar, zeType, REF_invokeStatic);
-            try {
+    private static synchronized void createFormsFor(BasicType type) {
+        final int ord = type.ordinal();
+        LambdaForm idForm = LF_identity[ord];
+        if (idForm != null) {
+            return;
+        }
+        char btChar = type.basicTypeChar();
+        boolean isVoid = (type == V_TYPE);
+        Class<?> btClass = type.btClass;
+        MethodType zeType = MethodType.methodType(btClass);
+        MethodType idType = (isVoid) ? zeType : zeType.appendParameterTypes(btClass);
+
+        // Look up symbolic names.  It might not be necessary to have these,
+        // but if we need to emit direct references to bytecodes, it helps.
+        // Zero is built from a call to an identity function with a constant zero input.
+        MemberName idMem = new MemberName(LambdaForm.class, "identity_"+btChar, idType, REF_invokeStatic);
+        MemberName zeMem = null;
+        try {
+            idMem = IMPL_NAMES.resolveOrFail(REF_invokeStatic, idMem, null, NoSuchMethodException.class);
+            if (!isVoid) {
+                zeMem = new MemberName(LambdaForm.class, "zero_"+btChar, zeType, REF_invokeStatic);
                 zeMem = IMPL_NAMES.resolveOrFail(REF_invokeStatic, zeMem, null, NoSuchMethodException.class);
-                idMem = IMPL_NAMES.resolveOrFail(REF_invokeStatic, idMem, null, NoSuchMethodException.class);
-            } catch (IllegalAccessException|NoSuchMethodException ex) {
-                throw newInternalError(ex);
             }
-
-            NamedFunction idFun = new NamedFunction(idMem);
-            LambdaForm idForm;
-            if (isVoid) {
-                Name[] idNames = new Name[] { argument(0, L_TYPE) };
-                idForm = new LambdaForm(idMem.getName(), 1, idNames, VOID_RESULT);
-            } else {
-                Name[] idNames = new Name[] { argument(0, L_TYPE), argument(1, type) };
-                idForm = new LambdaForm(idMem.getName(), 2, idNames, 1);
-            }
-            LF_identityForm[ord] = idForm;
-            NF_identity[ord] = idFun;
-
-            NamedFunction zeFun = new NamedFunction(zeMem);
-            LambdaForm zeForm;
-            if (isVoid) {
-                zeForm = idForm;
-            } else {
-                Object zeValue = Wrapper.forBasicType(btChar).zero();
-                Name[] zeNames = new Name[] { argument(0, L_TYPE), new Name(idFun, zeValue) };
-                zeForm = new LambdaForm(zeMem.getName(), 1, zeNames, 1);
-            }
-            LF_zeroForm[ord] = zeForm;
-            NF_zero[ord] = zeFun;
-
-            assert(idFun.isIdentity());
-            assert(zeFun.isConstantZero());
-            assert(new Name(zeFun).isConstantZero());
+        } catch (IllegalAccessException|NoSuchMethodException ex) {
+            throw newInternalError(ex);
         }
 
-        // Do this in a separate pass, so that SimpleMethodHandle.make can see the tables.
-        for (BasicType type : BasicType.ALL_TYPES) {
-            int ord = type.ordinal();
-            NamedFunction idFun = NF_identity[ord];
-            LambdaForm idForm = LF_identityForm[ord];
-            MemberName idMem = idFun.member;
-            idFun.resolvedHandle = SimpleMethodHandle.make(idMem.getInvocationType(), idForm);
+        NamedFunction idFun;
+        LambdaForm zeForm;
+        NamedFunction zeFun;
+
+        // Create the LFs and NamedFunctions. Precompiling LFs to byte code is needed to break circular
+        // bootstrap dependency on this method in case we're interpreting LFs
+        if (isVoid) {
+            Name[] idNames = new Name[] { argument(0, L_TYPE) };
+            idForm = new LambdaForm(idMem.getName(), 1, idNames, VOID_RESULT);
+            idForm.compileToBytecode();
+            idFun = new NamedFunction(idMem, SimpleMethodHandle.make(idMem.getInvocationType(), idForm));
 
-            NamedFunction zeFun = NF_zero[ord];
-            LambdaForm zeForm = LF_zeroForm[ord];
-            MemberName zeMem = zeFun.member;
-            zeFun.resolvedHandle = SimpleMethodHandle.make(zeMem.getInvocationType(), zeForm);
+            zeForm = idForm;
+            zeFun = idFun;
+        } else {
+            Name[] idNames = new Name[] { argument(0, L_TYPE), argument(1, type) };
+            idForm = new LambdaForm(idMem.getName(), 2, idNames, 1);
+            idForm.compileToBytecode();
+            idFun = new NamedFunction(idMem, SimpleMethodHandle.make(idMem.getInvocationType(), idForm));
 
-            assert(idFun.isIdentity());
-            assert(zeFun.isConstantZero());
-            assert(new Name(zeFun).isConstantZero());
+            Object zeValue = Wrapper.forBasicType(btChar).zero();
+            Name[] zeNames = new Name[] { argument(0, L_TYPE), new Name(idFun, zeValue) };
+            zeForm = new LambdaForm(zeMem.getName(), 1, zeNames, 1);
+            zeForm.compileToBytecode();
+            zeFun = new NamedFunction(zeMem, SimpleMethodHandle.make(zeMem.getInvocationType(), zeForm));
         }
+
+        LF_zero[ord] = zeForm;
+        NF_zero[ord] = zeFun;
+        LF_identity[ord] = idForm;
+        NF_identity[ord] = idFun;
+
+        assert(idFun.isIdentity());
+        assert(zeFun.isConstantZero());
+        assert(new Name(zeFun).isConstantZero());
     }
 
     // Avoid appealing to ValueConversions at bootstrap time:
@@ -1794,13 +1821,12 @@
     private static float identity_F(float x) { return x; }
     private static double identity_D(double x) { return x; }
     private static Object identity_L(Object x) { return x; }
-    private static void identity_V() { return; }  // same as zeroV, but that's OK
+    private static void identity_V() { return; }
     private static int zero_I() { return 0; }
     private static long zero_J() { return 0; }
     private static float zero_F() { return 0; }
     private static double zero_D() { return 0; }
     private static Object zero_L() { return null; }
-    private static void zero_V() { return; }
 
     /**
      * Internal marker for byte-compiled LambdaForms.
@@ -1830,7 +1856,6 @@
 
     // Put this last, so that previous static inits can run before.
     static {
-        createIdentityForms();
         if (USE_PREDEFINED_INTERPRET_METHODS)
             computeInitialPreparedForms();
         NamedFunction.initializeInvokers();
--- a/jdk/src/java.base/share/classes/java/lang/invoke/LambdaFormEditor.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/LambdaFormEditor.java	Mon Nov 23 14:37:04 2015 -0500
@@ -541,7 +541,7 @@
         assert(pos > 0);  // cannot spread the MH arg itself
 
         Name spreadParam = new Name(L_TYPE);
-        Name checkSpread = new Name(MethodHandleImpl.Lazy.NF_checkSpreadArgument, spreadParam, arrayLength);
+        Name checkSpread = new Name(MethodHandleImpl.NF_checkSpreadArgument, spreadParam, arrayLength);
 
         // insert the new expressions
         int exprPos = lambdaForm.arity();
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandle.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandle.java	Mon Nov 23 14:37:04 2015 -0500
@@ -872,13 +872,54 @@
      * @see #asCollector
      */
     public MethodHandle asSpreader(Class<?> arrayType, int arrayLength) {
-        MethodType postSpreadType = asSpreaderChecks(arrayType, arrayLength);
-        int arity = type().parameterCount();
-        int spreadArgPos = arity - arrayLength;
+        return asSpreader(type().parameterCount() - arrayLength, arrayType, arrayLength);
+    }
+
+    /**
+     * Makes an <em>array-spreading</em> method handle, which accepts an array argument at a given position and spreads
+     * its elements as positional arguments in place of the array. The new method handle adapts, as its <i>target</i>,
+     * the current method handle. The type of the adapter will be the same as the type of the target, except that the
+     * {@code arrayLength} parameters of the target's type, starting at the zero-based position {@code spreadArgPos},
+     * are replaced by a single array parameter of type {@code arrayType}.
+     * <p>
+     * This method behaves very much like {@link #asSpreader(Class, int)}, but accepts an additional {@code spreadArgPos}
+     * argument to indicate at which position in the parameter list the spreading should take place.
+     * <p>
+     * @apiNote Example:
+     * <blockquote><pre>{@code
+    MethodHandle compare = LOOKUP.findStatic(Objects.class, "compare", methodType(int.class, Object.class, Object.class, Comparator.class));
+    MethodHandle compare2FromArray = compare.asSpreader(0, Object[].class, 2);
+    Object[] ints = new Object[]{3, 9, 7, 7};
+    Comparator<Integer> cmp = (a, b) -> a - b;
+    assertTrue((int) compare2FromArray.invoke(Arrays.copyOfRange(ints, 0, 2), cmp) < 0);
+    assertTrue((int) compare2FromArray.invoke(Arrays.copyOfRange(ints, 1, 3), cmp) > 0);
+    assertTrue((int) compare2FromArray.invoke(Arrays.copyOfRange(ints, 2, 4), cmp) == 0);
+     * }</pre></blockquote>
+     * @param spreadArgPos the position (zero-based index) in the argument list at which spreading should start.
+     * @param arrayType usually {@code Object[]}, the type of the array argument from which to extract the spread arguments
+     * @param arrayLength the number of arguments to spread from an incoming array argument
+     * @return a new method handle which spreads an array argument at a given position,
+     *         before calling the original method handle
+     * @throws NullPointerException if {@code arrayType} is a null reference
+     * @throws IllegalArgumentException if {@code arrayType} is not an array type,
+     *         or if target does not have at least
+     *         {@code arrayLength} parameter types,
+     *         or if {@code arrayLength} is negative,
+     *         or if {@code spreadArgPos} has an illegal value (negative, or together with arrayLength exceeding the
+     *         number of arguments),
+     *         or if the resulting method handle's type would have
+     *         <a href="MethodHandle.html#maxarity">too many parameters</a>
+     * @throws WrongMethodTypeException if the implied {@code asType} call fails
+     *
+     * @see #asSpreader(Class, int)
+     * @since 9
+     */
+    public MethodHandle asSpreader(int spreadArgPos, Class<?> arrayType, int arrayLength) {
+        MethodType postSpreadType = asSpreaderChecks(arrayType, spreadArgPos, arrayLength);
         MethodHandle afterSpread = this.asType(postSpreadType);
         BoundMethodHandle mh = afterSpread.rebind();
         LambdaForm lform = mh.editor().spreadArgumentsForm(1 + spreadArgPos, arrayType, arrayLength);
-        MethodType preSpreadType = postSpreadType.replaceParameterTypes(spreadArgPos, arity, arrayType);
+        MethodType preSpreadType = postSpreadType.replaceParameterTypes(spreadArgPos, spreadArgPos + arrayLength, arrayType);
         return mh.copyWith(preSpreadType, lform);
     }
 
@@ -886,15 +927,18 @@
      * See if {@code asSpreader} can be validly called with the given arguments.
      * Return the type of the method handle call after spreading but before conversions.
      */
-    private MethodType asSpreaderChecks(Class<?> arrayType, int arrayLength) {
+    private MethodType asSpreaderChecks(Class<?> arrayType, int pos, int arrayLength) {
         spreadArrayChecks(arrayType, arrayLength);
         int nargs = type().parameterCount();
         if (nargs < arrayLength || arrayLength < 0)
             throw newIllegalArgumentException("bad spread array length");
+        if (pos < 0 || pos + arrayLength > nargs) {
+            throw newIllegalArgumentException("bad spread position");
+        }
         Class<?> arrayElement = arrayType.getComponentType();
         MethodType mtype = type();
         boolean match = true, fail = false;
-        for (int i = nargs - arrayLength; i < nargs; i++) {
+        for (int i = pos; i < arrayLength; i++) {
             Class<?> ptype = mtype.parameterType(i);
             if (ptype != arrayElement) {
                 match = false;
@@ -905,7 +949,7 @@
             }
         }
         if (match)  return mtype;
-        MethodType needType = mtype.asSpreaderType(arrayType, arrayLength);
+        MethodType needType = mtype.asSpreaderType(arrayType, pos, arrayLength);
         if (!fail)  return needType;
         // elicit an error:
         this.asType(needType);
@@ -998,10 +1042,53 @@
      * @see #asVarargsCollector
      */
     public MethodHandle asCollector(Class<?> arrayType, int arrayLength) {
-        asCollectorChecks(arrayType, arrayLength);
-        int collectArgPos = type().parameterCount() - 1;
+        return asCollector(type().parameterCount() - 1, arrayType, arrayLength);
+    }
+
+    /**
+     * Makes an <em>array-collecting</em> method handle, which accepts a given number of positional arguments starting
+     * at a given position, and collects them into an array argument. The new method handle adapts, as its
+     * <i>target</i>, the current method handle. The type of the adapter will be the same as the type of the target,
+     * except that the parameter at the position indicated by {@code collectArgPos} (usually of type {@code arrayType})
+     * is replaced by {@code arrayLength} parameters whose type is element type of {@code arrayType}.
+     * <p>
+     * This method behaves very much like {@link #asCollector(Class, int)}, but differs in that its {@code
+     * collectArgPos} argument indicates at which position in the parameter list arguments should be collected. This
+     * index is zero-based.
+     * <p>
+     * @apiNote Examples:
+     * <blockquote><pre>{@code
+    StringWriter swr = new StringWriter();
+    MethodHandle swWrite = LOOKUP.findVirtual(StringWriter.class, "write", methodType(void.class, char[].class, int.class, int.class)).bindTo(swr);
+    MethodHandle swWrite4 = swWrite.asCollector(0, char[].class, 4);
+    swWrite4.invoke('A', 'B', 'C', 'D', 1, 2);
+    assertEquals("BC", swr.toString());
+    swWrite4.invoke('P', 'Q', 'R', 'S', 0, 4);
+    assertEquals("BCPQRS", swr.toString());
+    swWrite4.invoke('W', 'X', 'Y', 'Z', 3, 1);
+    assertEquals("BCPQRSZ", swr.toString());
+     * }</pre></blockquote>
+     * @param collectArgPos the zero-based position in the parameter list at which to start collecting.
+     * @param arrayType often {@code Object[]}, the type of the array argument which will collect the arguments
+     * @param arrayLength the number of arguments to collect into a new array argument
+     * @return a new method handle which collects some arguments
+     *         into an array, before calling the original method handle
+     * @throws NullPointerException if {@code arrayType} is a null reference
+     * @throws IllegalArgumentException if {@code arrayType} is not an array type
+     *         or {@code arrayType} is not assignable to this method handle's array parameter type,
+     *         or {@code arrayLength} is not a legal array size,
+     *         or {@code collectArgPos} has an illegal value (negative, or greater than the number of arguments),
+     *         or the resulting method handle's type would have
+     *         <a href="MethodHandle.html#maxarity">too many parameters</a>
+     * @throws WrongMethodTypeException if the implied {@code asType} call fails
+     *
+     * @see #asCollector(Class, int)
+     * @since 9
+     */
+    public MethodHandle asCollector(int collectArgPos, Class<?> arrayType, int arrayLength) {
+        asCollectorChecks(arrayType, collectArgPos, arrayLength);
         BoundMethodHandle mh = rebind();
-        MethodType resultType = type().asCollectorType(arrayType, arrayLength);
+        MethodType resultType = type().asCollectorType(arrayType, collectArgPos, arrayLength);
         MethodHandle newArray = MethodHandleImpl.varargsArray(arrayType, arrayLength);
         LambdaForm lform = mh.editor().collectArgumentArrayForm(1 + collectArgPos, newArray);
         if (lform != null) {
@@ -1015,15 +1102,18 @@
      * See if {@code asCollector} can be validly called with the given arguments.
      * Return false if the last parameter is not an exact match to arrayType.
      */
-    /*non-public*/ boolean asCollectorChecks(Class<?> arrayType, int arrayLength) {
+    /*non-public*/ boolean asCollectorChecks(Class<?> arrayType, int pos, int arrayLength) {
         spreadArrayChecks(arrayType, arrayLength);
         int nargs = type().parameterCount();
+        if (pos < 0 || pos >= nargs) {
+            throw newIllegalArgumentException("bad collect position");
+        }
         if (nargs != 0) {
-            Class<?> lastParam = type().parameterType(nargs-1);
-            if (lastParam == arrayType)  return true;
-            if (lastParam.isAssignableFrom(arrayType))  return false;
+            Class<?> param = type().parameterType(pos);
+            if (param == arrayType)  return true;
+            if (param.isAssignableFrom(arrayType))  return false;
         }
-        throw newIllegalArgumentException("array type not assignable to trailing argument", this, arrayType);
+        throw newIllegalArgumentException("array type not assignable to argument", this, arrayType);
     }
 
     /**
@@ -1178,7 +1268,7 @@
      */
     public MethodHandle asVarargsCollector(Class<?> arrayType) {
         Objects.requireNonNull(arrayType);
-        boolean lastMatch = asCollectorChecks(arrayType, 0);
+        boolean lastMatch = asCollectorChecks(arrayType, type().parameterCount() - 1, 0);
         if (isVarargsCollector() && lastMatch)
             return this;
         return MethodHandleImpl.makeVarargsCollector(this, arrayType);
@@ -1341,7 +1431,6 @@
         // cannot be cracked into MethodHandleInfo.
         assert viewAsTypeChecks(newType, strict);
         BoundMethodHandle mh = rebind();
-        assert(!((MethodHandle)mh instanceof DirectMethodHandle));
         return mh.copyWith(newType, mh.form);
     }
 
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java	Mon Nov 23 14:37:04 2015 -0500
@@ -27,16 +27,17 @@
 
 import java.security.AccessController;
 import java.security.PrivilegedAction;
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
 import java.util.function.Function;
+import java.util.stream.Collectors;
 
 import sun.invoke.empty.Empty;
 import sun.invoke.util.ValueConversions;
 import sun.invoke.util.VerifyType;
 import sun.invoke.util.Wrapper;
-import jdk.internal.HotSpotIntrinsicCandidate;
 import sun.reflect.CallerSensitive;
 import sun.reflect.Reflection;
 import static java.lang.invoke.LambdaForm.*;
@@ -219,7 +220,7 @@
             if (convSpec == null)  continue;
             MethodHandle fn;
             if (convSpec instanceof Class) {
-                fn = Lazy.MH_cast.bindTo(convSpec);
+                fn = getConstantHandle(MH_cast).bindTo(convSpec);
             } else {
                 fn = (MethodHandle) convSpec;
             }
@@ -239,7 +240,7 @@
                 if (convSpec == void.class)
                     fn = null;
                 else
-                    fn = Lazy.MH_cast.bindTo(convSpec);
+                    fn = getConstantHandle(MH_cast).bindTo(convSpec);
             } else {
                 fn = (MethodHandle) convSpec;
             }
@@ -302,7 +303,7 @@
             Name conv;
             if (convSpec instanceof Class) {
                 Class<?> convClass = (Class<?>) convSpec;
-                conv = new Name(Lazy.MH_cast, convClass, names[INARG_BASE + i]);
+                conv = new Name(getConstantHandle(MH_cast), convClass, names[INARG_BASE + i]);
             } else {
                 MethodHandle fn = (MethodHandle) convSpec;
                 conv = new Name(fn, names[INARG_BASE + i]);
@@ -326,7 +327,7 @@
                 conv = new Name(LambdaForm.constantZero(BasicType.basicType(srcType.returnType())));
             } else if (convSpec instanceof Class) {
                 Class<?> convClass = (Class<?>) convSpec;
-                conv = new Name(Lazy.MH_cast, convClass, names[OUT_CALL]);
+                conv = new Name(getConstantHandle(MH_cast), convClass, names[OUT_CALL]);
             } else {
                 MethodHandle fn = (MethodHandle) convSpec;
                 if (fn.type().parameterCount() == 0)
@@ -529,7 +530,7 @@
                 // Spread the array.
                 MethodHandle aload = MethodHandles.arrayElementGetter(spreadArgType);
                 Name array = names[argIndex];
-                names[nameCursor++] = new Name(Lazy.NF_checkSpreadArgument, array, spreadArgCount);
+                names[nameCursor++] = new Name(NF_checkSpreadArgument, array, spreadArgCount);
                 for (int j = 0; j < spreadArgCount; i++, j++) {
                     indexes[i] = nameCursor;
                     names[nameCursor++] = new Name(aload, array, j);
@@ -566,66 +567,6 @@
         throw newIllegalArgumentException("array is not of length "+n);
     }
 
-    /**
-     * Pre-initialized NamedFunctions for bootstrapping purposes.
-     * Factored in an inner class to delay initialization until first usage.
-     */
-    static class Lazy {
-        private static final Class<?> MHI = MethodHandleImpl.class;
-        private static final Class<?> CLS = Class.class;
-
-        private static final MethodHandle[] ARRAYS;
-        private static final MethodHandle[] FILL_ARRAYS;
-
-        static final NamedFunction NF_checkSpreadArgument;
-        static final NamedFunction NF_guardWithCatch;
-        static final NamedFunction NF_throwException;
-        static final NamedFunction NF_profileBoolean;
-
-        static final MethodHandle MH_cast;
-        static final MethodHandle MH_selectAlternative;
-        static final MethodHandle MH_copyAsPrimitiveArray;
-        static final MethodHandle MH_fillNewTypedArray;
-        static final MethodHandle MH_fillNewArray;
-        static final MethodHandle MH_arrayIdentity;
-
-        static {
-            ARRAYS      = makeArrays();
-            FILL_ARRAYS = makeFillArrays();
-
-            try {
-                NF_checkSpreadArgument = new NamedFunction(MHI.getDeclaredMethod("checkSpreadArgument", Object.class, int.class));
-                NF_guardWithCatch      = new NamedFunction(MHI.getDeclaredMethod("guardWithCatch", MethodHandle.class, Class.class,
-                                                                                 MethodHandle.class, Object[].class));
-                NF_throwException      = new NamedFunction(MHI.getDeclaredMethod("throwException", Throwable.class));
-                NF_profileBoolean      = new NamedFunction(MHI.getDeclaredMethod("profileBoolean", boolean.class, int[].class));
-
-                NF_checkSpreadArgument.resolve();
-                NF_guardWithCatch.resolve();
-                NF_throwException.resolve();
-                NF_profileBoolean.resolve();
-
-                MH_cast                 = IMPL_LOOKUP.findVirtual(CLS, "cast",
-                                            MethodType.methodType(Object.class, Object.class));
-                MH_copyAsPrimitiveArray = IMPL_LOOKUP.findStatic(MHI, "copyAsPrimitiveArray",
-                                            MethodType.methodType(Object.class, Wrapper.class, Object[].class));
-                MH_arrayIdentity        = IMPL_LOOKUP.findStatic(MHI, "identity",
-                                            MethodType.methodType(Object[].class, Object[].class));
-                MH_fillNewArray         = IMPL_LOOKUP.findStatic(MHI, "fillNewArray",
-                                            MethodType.methodType(Object[].class, Integer.class, Object[].class));
-                MH_fillNewTypedArray    = IMPL_LOOKUP.findStatic(MHI, "fillNewTypedArray",
-                                            MethodType.methodType(Object[].class, Object[].class, Integer.class, Object[].class));
-
-                MH_selectAlternative    = makeIntrinsic(
-                        IMPL_LOOKUP.findStatic(MHI, "selectAlternative",
-                                MethodType.methodType(MethodHandle.class, boolean.class, MethodHandle.class, MethodHandle.class)),
-                        Intrinsic.SELECT_ALTERNATIVE);
-            } catch (ReflectiveOperationException ex) {
-                throw newInternalError(ex);
-            }
-        }
-    }
-
     /** Factory method:  Collect or filter selected argument(s). */
     static MethodHandle makeCollectArguments(MethodHandle target,
                 MethodHandle collector, int collectArgPos, boolean retainOriginalArgs) {
@@ -911,10 +852,10 @@
 
         // profile branch
         if (PROFILE != -1) {
-            names[PROFILE] = new Name(Lazy.NF_profileBoolean, names[CALL_TEST], names[GET_COUNTERS]);
+            names[PROFILE] = new Name(NF_profileBoolean, names[CALL_TEST], names[GET_COUNTERS]);
         }
         // call selectAlternative
-        names[SELECT_ALT] = new Name(Lazy.MH_selectAlternative, names[TEST], names[GET_TARGET], names[GET_FALLBACK]);
+        names[SELECT_ALT] = new Name(getConstantHandle(MH_selectAlternative), names[TEST], names[GET_TARGET], names[GET_FALLBACK]);
 
         // call target or fallback
         invokeArgs[0] = names[SELECT_ALT];
@@ -989,7 +930,7 @@
 
         // t_{i+1}:L=MethodHandleImpl.guardWithCatch(target:L,exType:L,catcher:L,t_{i}:L);
         Object[] gwcArgs = new Object[] {names[GET_TARGET], names[GET_CLASS], names[GET_CATCHER], names[BOXED_ARGS]};
-        names[TRY_CATCH] = new Name(Lazy.NF_guardWithCatch, gwcArgs);
+        names[TRY_CATCH] = new Name(NF_guardWithCatch, gwcArgs);
 
         // t_{i+2}:I=MethodHandle.invokeBasic(unbox:L,t_{i+1}:L);
         MethodHandle invokeBasicUnbox = MethodHandles.basicInvoker(MethodType.methodType(basicType.rtype(), Object.class));
@@ -1073,7 +1014,7 @@
             mh = MethodHandles.dropArguments(mh, 1, type.parameterList().subList(1, arity));
             return mh;
         }
-        return makePairwiseConvert(Lazy.NF_throwException.resolvedHandle(), type, false, true);
+        return makePairwiseConvert(NF_throwException.resolvedHandle(), type, false, true);
     }
 
     static <T extends Throwable> Empty throwException(T t) throws T { throw t; }
@@ -1357,7 +1298,7 @@
         @Override
         public MethodHandle asCollector(Class<?> arrayType, int arrayLength) {
             if (intrinsicName == Intrinsic.IDENTITY) {
-                MethodType resultType = type().asCollectorType(arrayType, arrayLength);
+                MethodType resultType = type().asCollectorType(arrayType, type().parameterCount() - 1, arrayLength);
                 MethodHandle newArray = MethodHandleImpl.varargsArray(arrayType, arrayLength);
                 return newArray.asType(resultType);
             }
@@ -1421,25 +1362,7 @@
                 { return makeArray(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); }
 
     private static final int ARRAYS_COUNT = 11;
-
-    private static MethodHandle[] makeArrays() {
-        MethodHandle[] mhs = new MethodHandle[MAX_ARITY + 1];
-        for (int i = 0; i < ARRAYS_COUNT; i++) {
-            MethodHandle mh = findCollector("array", i, Object[].class);
-            mh = makeIntrinsic(mh, Intrinsic.NEW_ARRAY);
-            mhs[i] = mh;
-        }
-        assert(assertArrayMethodCount(mhs));
-        return mhs;
-    }
-
-    private static boolean assertArrayMethodCount(MethodHandle[] mhs) {
-        assert(findCollector("array", ARRAYS_COUNT, Object[].class) == null);
-        for (int i = 0; i < ARRAYS_COUNT; i++) {
-            assert(mhs[i] != null);
-        }
-        return true;
-    }
+    private static final @Stable MethodHandle[] ARRAYS = new MethodHandle[MAX_ARITY + 1];
 
     // filling versions of the above:
     // using Integer len instead of int len and no varargs to avoid bootstrapping problems
@@ -1488,24 +1411,17 @@
                 { fillWithArguments(a, pos, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); return a; }
 
     private static final int FILL_ARRAYS_COUNT = 11; // current number of fillArray methods
+    private static final @Stable MethodHandle[] FILL_ARRAYS = new MethodHandle[FILL_ARRAYS_COUNT];
 
-    private static MethodHandle[] makeFillArrays() {
-        MethodHandle[] mhs = new MethodHandle[FILL_ARRAYS_COUNT];
-        mhs[0] = null;  // there is no empty fill; at least a0 is required
-        for (int i = 1; i < FILL_ARRAYS_COUNT; i++) {
-            MethodHandle mh = findCollector("fillArray", i, Object[].class, Integer.class, Object[].class);
-            mhs[i] = mh;
+    private static MethodHandle getFillArray(int count) {
+        assert (count > 0 && count < FILL_ARRAYS_COUNT);
+        MethodHandle mh = FILL_ARRAYS[count];
+        if (mh != null) {
+            return mh;
         }
-        assert(assertFillArrayMethodCount(mhs));
-        return mhs;
-    }
-
-    private static boolean assertFillArrayMethodCount(MethodHandle[] mhs) {
-        assert(findCollector("fillArray", FILL_ARRAYS_COUNT, Object[].class, Integer.class, Object[].class) == null);
-        for (int i = 1; i < FILL_ARRAYS_COUNT; i++) {
-            assert(mhs[i] != null);
-        }
-        return true;
+        mh = findCollector("fillArray", count, Object[].class, Integer.class, Object[].class);
+        FILL_ARRAYS[count] = mh;
+        return mh;
     }
 
     private static Object copyAsPrimitiveArray(Wrapper w, Object... boxes) {
@@ -1518,12 +1434,19 @@
      *  arguments and returns an Object array of them, as if for varargs.
      */
     static MethodHandle varargsArray(int nargs) {
-        MethodHandle mh = Lazy.ARRAYS[nargs];
-        if (mh != null)  return mh;
-        mh = buildVarargsArray(Lazy.MH_fillNewArray, Lazy.MH_arrayIdentity, nargs);
+        MethodHandle mh = ARRAYS[nargs];
+        if (mh != null) {
+            return mh;
+        }
+        if (nargs < ARRAYS_COUNT) {
+            mh = findCollector("array", nargs, Object[].class);
+        } else {
+            mh = buildVarargsArray(getConstantHandle(MH_fillNewArray),
+                    getConstantHandle(MH_arrayIdentity), nargs);
+        }
         assert(assertCorrectArity(mh, nargs));
         mh = makeIntrinsic(mh, Intrinsic.NEW_ARRAY);
-        return Lazy.ARRAYS[nargs] = mh;
+        return ARRAYS[nargs] = mh;
     }
 
     private static boolean assertCorrectArity(MethodHandle mh, int arity) {
@@ -1531,7 +1454,7 @@
         return true;
     }
 
-    // Array identity function (used as Lazy.MH_arrayIdentity).
+    // Array identity function (used as getConstantHandle(MH_arrayIdentity)).
     static <T> T[] identity(T[] x) {
         return x;
     }
@@ -1547,12 +1470,12 @@
         MethodHandle mh = finisher;
         if (rightLen > 0) {
             MethodHandle rightFiller = fillToRight(LEFT_ARGS + rightLen);
-            if (mh == Lazy.MH_arrayIdentity)
+            if (mh.equals(getConstantHandle(MH_arrayIdentity)))
                 mh = rightFiller;
             else
                 mh = MethodHandles.collectArguments(mh, 0, rightFiller);
         }
-        if (mh == Lazy.MH_arrayIdentity)
+        if (mh.equals(getConstantHandle(MH_arrayIdentity)))
             mh = leftCollector;
         else
             mh = MethodHandles.collectArguments(mh, 0, leftCollector);
@@ -1560,7 +1483,7 @@
     }
 
     private static final int LEFT_ARGS = FILL_ARRAYS_COUNT - 1;
-    private static final MethodHandle[] FILL_ARRAY_TO_RIGHT = new MethodHandle[MAX_ARITY+1];
+    private static final @Stable MethodHandle[] FILL_ARRAY_TO_RIGHT = new MethodHandle[MAX_ARITY+1];
     /** fill_array_to_right(N).invoke(a, argL..arg[N-1])
      *  fills a[L]..a[N-1] with corresponding arguments,
      *  and then returns a.  The value L is a global constant (LEFT_ARGS).
@@ -1574,7 +1497,7 @@
     }
     private static MethodHandle buildFiller(int nargs) {
         if (nargs <= LEFT_ARGS)
-            return Lazy.MH_arrayIdentity;  // no args to fill; return the array unchanged
+            return getConstantHandle(MH_arrayIdentity);  // no args to fill; return the array unchanged
         // we need room for both mh and a in mh.invoke(a, arg*[nargs])
         final int CHUNK = LEFT_ARGS;
         int rightLen = nargs % CHUNK;
@@ -1590,7 +1513,7 @@
         if (midLen < LEFT_ARGS) rightLen = nargs - (midLen = LEFT_ARGS);
         assert(rightLen > 0);
         MethodHandle midFill = fillToRight(midLen);  // recursive fill
-        MethodHandle rightFill = Lazy.FILL_ARRAYS[rightLen].bindTo(midLen);  // [midLen..nargs-1]
+        MethodHandle rightFill = getFillArray(rightLen).bindTo(midLen);  // [midLen..nargs-1]
         assert(midFill.type().parameterCount()   == 1 + midLen - LEFT_ARGS);
         assert(rightFill.type().parameterCount() == 1 + rightLen);
 
@@ -1641,14 +1564,14 @@
             Object example = java.lang.reflect.Array.newInstance(arrayType.getComponentType(), 0);
             mh = MethodHandles.constant(arrayType, example);
         } else if (elemType.isPrimitive()) {
-            MethodHandle builder = Lazy.MH_fillNewArray;
+            MethodHandle builder = getConstantHandle(MH_fillNewArray);
             MethodHandle producer = buildArrayProducer(arrayType);
             mh = buildVarargsArray(builder, producer, nargs);
         } else {
             Class<? extends Object[]> objArrayType = arrayType.asSubclass(Object[].class);
             Object[] example = Arrays.copyOf(NO_ARGS_ARRAY, 0, objArrayType);
-            MethodHandle builder = Lazy.MH_fillNewTypedArray.bindTo(example);
-            MethodHandle producer = Lazy.MH_arrayIdentity; // must be weakly typed
+            MethodHandle builder = getConstantHandle(MH_fillNewTypedArray).bindTo(example);
+            MethodHandle producer = getConstantHandle(MH_arrayIdentity); // must be weakly typed
             mh = buildVarargsArray(builder, producer, nargs);
         }
         mh = mh.asType(MethodType.methodType(arrayType, Collections.<Class<?>>nCopies(nargs, elemType)));
@@ -1662,7 +1585,7 @@
     private static MethodHandle buildArrayProducer(Class<?> arrayType) {
         Class<?> elemType = arrayType.getComponentType();
         assert(elemType.isPrimitive());
-        return Lazy.MH_copyAsPrimitiveArray.bindTo(Wrapper.forPrimitiveType(elemType));
+        return getConstantHandle(MH_copyAsPrimitiveArray).bindTo(Wrapper.forPrimitiveType(elemType));
     }
 
     /*non-public*/ static void assertSame(Object mh1, Object mh2) {
@@ -1673,4 +1596,346 @@
             throw newInternalError(msg);
         }
     }
+
+    // Local constant functions:
+    /*non-public*/ static final NamedFunction
+        NF_checkSpreadArgument,
+        NF_guardWithCatch,
+        NF_throwException,
+        NF_profileBoolean;
+
+    static {
+        try {
+            NF_checkSpreadArgument = new NamedFunction(MethodHandleImpl.class
+                    .getDeclaredMethod("checkSpreadArgument", Object.class, int.class));
+            NF_guardWithCatch = new NamedFunction(MethodHandleImpl.class
+                    .getDeclaredMethod("guardWithCatch", MethodHandle.class, Class.class,
+                            MethodHandle.class, Object[].class));
+            NF_throwException = new NamedFunction(MethodHandleImpl.class
+                    .getDeclaredMethod("throwException", Throwable.class));
+            NF_profileBoolean = new NamedFunction(MethodHandleImpl.class
+                    .getDeclaredMethod("profileBoolean", boolean.class, int[].class));
+        } catch (ReflectiveOperationException ex) {
+            throw newInternalError(ex);
+        }
+    }
+
+    /**
+     * Assembles a loop method handle from the given handles and type information. This works by binding and configuring
+     * the {@linkplain #looper(MethodHandle[], MethodHandle[], MethodHandle[], MethodHandle[], int, int, Object[]) "most
+     * generic loop"}.
+     *
+     * @param tloop the return type of the loop.
+     * @param targs types of the arguments to be passed to the loop.
+     * @param tvars types of loop-local variables.
+     * @param init sanitized array of initializers for loop-local variables.
+     * @param step sanitited array of loop bodies.
+     * @param pred sanitized array of predicates.
+     * @param fini sanitized array of loop finalizers.
+     *
+     * @return a handle that, when invoked, will execute the loop.
+     */
+    static MethodHandle makeLoop(Class<?> tloop, List<Class<?>> targs, List<Class<?>> tvars, List<MethodHandle> init,
+                                 List<MethodHandle> step, List<MethodHandle> pred, List<MethodHandle> fini) {
+        MethodHandle[] ainit = toArrayArgs(init);
+        MethodHandle[] astep = toArrayArgs(step);
+        MethodHandle[] apred = toArrayArgs(pred);
+        MethodHandle[] afini = toArrayArgs(fini);
+
+        MethodHandle l = getConstantHandle(MH_looper);
+
+        // Bind the statically known arguments.
+        l = MethodHandles.insertArguments(l, 0, ainit, astep, apred, afini, tvars.size(), targs.size());
+
+        // Turn the args array into an argument list.
+        l = l.asCollector(Object[].class, targs.size());
+
+        // Finally, make loop type.
+        MethodType loopType = MethodType.methodType(tloop, targs);
+        l = l.asType(loopType);
+
+        return l;
+    }
+
+    /**
+     * Converts all handles in the {@code hs} array to handles that accept an array of arguments.
+     *
+     * @param hs method handles to be converted.
+     *
+     * @return the {@code hs} array, with all method handles therein converted.
+     */
+    static MethodHandle[] toArrayArgs(List<MethodHandle> hs) {
+        return hs.stream().map(h -> h.asSpreader(Object[].class, h.type().parameterCount())).toArray(MethodHandle[]::new);
+    }
+
+    /**
+     * This method embodies the most generic loop for use by {@link MethodHandles#loop(MethodHandle[][])}. A handle on
+     * it will be transformed into a handle on a concrete loop instantiation by {@link #makeLoop}.
+     *
+     * @param init loop-local variable initializers.
+     * @param step bodies.
+     * @param pred predicates.
+     * @param fini finalizers.
+     * @param varSize number of loop-local variables.
+     * @param nArgs number of arguments passed to the loop.
+     * @param args arguments to the loop invocation.
+     *
+     * @return the result of executing the loop.
+     */
+    static Object looper(MethodHandle[] init, MethodHandle[] step, MethodHandle[] pred, MethodHandle[] fini,
+                         int varSize, int nArgs, Object[] args) throws Throwable {
+        Object[] varsAndArgs = new Object[varSize + nArgs];
+        for (int i = 0, v = 0; i < init.length; ++i) {
+            if (init[i].type().returnType() == void.class) {
+                init[i].invoke(args);
+            } else {
+                varsAndArgs[v++] = init[i].invoke(args);
+            }
+        }
+        System.arraycopy(args, 0, varsAndArgs, varSize, nArgs);
+        final int nSteps = step.length;
+        for (; ; ) {
+            for (int i = 0, v = 0; i < nSteps; ++i) {
+                MethodHandle p = pred[i];
+                MethodHandle s = step[i];
+                MethodHandle f = fini[i];
+                if (s.type().returnType() == void.class) {
+                    s.invoke(varsAndArgs);
+                } else {
+                    varsAndArgs[v++] = s.invoke(varsAndArgs);
+                }
+                if (!(boolean) p.invoke(varsAndArgs)) {
+                    return f.invoke(varsAndArgs);
+                }
+            }
+        }
+    }
+
+    /**
+     * This method is bound as the predicate in {@linkplain MethodHandles#countedLoop(MethodHandle, MethodHandle,
+     * MethodHandle) counting loops}.
+     *
+     * @param counter the counter parameter, passed in during loop execution.
+     * @param limit the upper bound of the parameter, statically bound at loop creation time.
+     *
+     * @return whether the counter has reached the limit.
+     */
+    static boolean countedLoopPredicate(int counter, int limit) {
+        return counter <= limit;
+    }
+
+    /**
+     * This method is bound as the step function in {@linkplain MethodHandles#countedLoop(MethodHandle, MethodHandle,
+     * MethodHandle) counting loops} to increment the counter.
+     *
+     * @param counter the loop counter.
+     *
+     * @return the loop counter incremented by 1.
+     */
+    static int countedLoopStep(int counter, int limit) {
+        return counter + 1;
+    }
+
+    /**
+     * This is bound to initialize the loop-local iterator in {@linkplain MethodHandles#iteratedLoop iterating loops}.
+     *
+     * @param it the {@link Iterable} over which the loop iterates.
+     *
+     * @return an {@link Iterator} over the argument's elements.
+     */
+    static Iterator<?> initIterator(Iterable<?> it) {
+        return it.iterator();
+    }
+
+    /**
+     * This method is bound as the predicate in {@linkplain MethodHandles#iteratedLoop iterating loops}.
+     *
+     * @param it the iterator to be checked.
+     *
+     * @return {@code true} iff there are more elements to iterate over.
+     */
+    static boolean iteratePredicate(Iterator<?> it) {
+        return it.hasNext();
+    }
+
+    /**
+     * This method is bound as the step for retrieving the current value from the iterator in {@linkplain
+     * MethodHandles#iteratedLoop iterating loops}.
+     *
+     * @param it the iterator.
+     *
+     * @return the next element from the iterator.
+     */
+    static Object iterateNext(Iterator<?> it) {
+        return it.next();
+    }
+
+    /**
+     * Makes a {@code try-finally} handle that conforms to the type constraints.
+     *
+     * @param target the target to execute in a {@code try-finally} block.
+     * @param cleanup the cleanup to execute in the {@code finally} block.
+     * @param type the result type of the entire construct.
+     * @param argTypes the types of the arguments.
+     *
+     * @return a handle on the constructed {@code try-finally} block.
+     */
+    static MethodHandle makeTryFinally(MethodHandle target, MethodHandle cleanup, Class<?> type, List<Class<?>> argTypes) {
+        MethodHandle tf = getConstantHandle(type == void.class ? MH_tryFinallyVoidExec : MH_tryFinallyExec);
+
+        // Bind the statically known arguments.
+        tf = MethodHandles.insertArguments(tf, 0, target, cleanup);
+
+        // Turn the args array into an argument list.
+        tf = tf.asCollector(Object[].class, argTypes.size());
+
+        // Finally, make try-finally type.
+        MethodType tfType = MethodType.methodType(type, argTypes);
+        tf = tf.asType(tfType);
+
+        return tf;
+    }
+
+    /**
+     * A method that will be bound during construction of a {@code try-finally} handle with non-{@code void} return type
+     * by {@link MethodHandles#tryFinally(MethodHandle, MethodHandle)}.
+     *
+     * @param target the handle to wrap in a {@code try-finally} block. This will be bound.
+     * @param cleanup the handle to run in any case before returning. This will be bound.
+     * @param args the arguments to the call. These will remain as the argument list.
+     *
+     * @return whatever the execution of the {@code target} returned (it may have been modified by the execution of
+     *         {@code cleanup}).
+     * @throws Throwable in case anything is thrown by the execution of {@code target}, the {@link Throwable} will be
+     *         passed to the {@code cleanup} handle, which may decide to throw any exception it sees fit.
+     */
+    static Object tryFinallyExecutor(MethodHandle target, MethodHandle cleanup, Object[] args) throws Throwable {
+        Throwable t = null;
+        Object r = null;
+        try {
+            r = target.invoke(args);
+        } catch (Throwable thrown) {
+            t = thrown;
+            throw t;
+        } finally {
+            r = cleanup.invoke(t, r, args);
+        }
+        return r;
+    }
+
+    /**
+     * A method that will be bound during construction of a {@code try-finally} handle with {@code void} return type by
+     * {@link MethodHandles#tryFinally(MethodHandle, MethodHandle)}.
+     *
+     * @param target the handle to wrap in a {@code try-finally} block. This will be bound.
+     * @param cleanup the handle to run in any case before returning. This will be bound.
+     * @param args the arguments to the call. These will remain as the argument list.
+     *
+     * @throws Throwable in case anything is thrown by the execution of {@code target}, the {@link Throwable} will be
+     *         passed to the {@code cleanup} handle, which may decide to throw any exception it sees fit.
+     */
+    static void tryFinallyVoidExecutor(MethodHandle target, MethodHandle cleanup, Object[] args) throws Throwable {
+        Throwable t = null;
+        try {
+            target.invoke(args);
+        } catch (Throwable thrown) {
+            t = thrown;
+            throw t;
+        } finally {
+            cleanup.invoke(t, args);
+        }
+    }
+
+    // Indexes into constant method handles:
+    static final int
+            MH_cast                  =  0,
+            MH_selectAlternative     =  1,
+            MH_copyAsPrimitiveArray  =  2,
+            MH_fillNewTypedArray     =  3,
+            MH_fillNewArray          =  4,
+            MH_arrayIdentity         =  5,
+            MH_looper                =  6,
+            MH_countedLoopPred       =  7,
+            MH_countedLoopStep       =  8,
+            MH_iteratePred           =  9,
+            MH_initIterator          = 10,
+            MH_iterateNext           = 11,
+            MH_tryFinallyExec        = 12,
+            MH_tryFinallyVoidExec    = 13,
+            MH_LIMIT                 = 14;
+
+    static MethodHandle getConstantHandle(int idx) {
+        MethodHandle handle = HANDLES[idx];
+        if (handle != null) {
+            return handle;
+        }
+        return setCachedHandle(idx, makeConstantHandle(idx));
+    }
+
+    private static synchronized MethodHandle setCachedHandle(int idx, final MethodHandle method) {
+        // Simulate a CAS, to avoid racy duplication of results.
+        MethodHandle prev = HANDLES[idx];
+        if (prev != null) {
+            return prev;
+        }
+        HANDLES[idx] = method;
+        return method;
+    }
+
+    // Local constant method handles:
+    private static final @Stable MethodHandle[] HANDLES = new MethodHandle[MH_LIMIT];
+
+    private static MethodHandle makeConstantHandle(int idx) {
+        try {
+            switch (idx) {
+                case MH_cast:
+                    return IMPL_LOOKUP.findVirtual(Class.class, "cast",
+                            MethodType.methodType(Object.class, Object.class));
+                case MH_copyAsPrimitiveArray:
+                    return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "copyAsPrimitiveArray",
+                            MethodType.methodType(Object.class, Wrapper.class, Object[].class));
+                case MH_arrayIdentity:
+                    return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "identity",
+                            MethodType.methodType(Object[].class, Object[].class));
+                case MH_fillNewArray:
+                    return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "fillNewArray",
+                            MethodType.methodType(Object[].class, Integer.class, Object[].class));
+                case MH_fillNewTypedArray:
+                    return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "fillNewTypedArray",
+                            MethodType.methodType(Object[].class, Object[].class, Integer.class, Object[].class));
+                case MH_selectAlternative:
+                    return makeIntrinsic(IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "selectAlternative",
+                            MethodType.methodType(MethodHandle.class, boolean.class, MethodHandle.class, MethodHandle.class)),
+                        Intrinsic.SELECT_ALTERNATIVE);
+                case MH_looper:
+                    return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "looper", MethodType.methodType(Object.class,
+                            MethodHandle[].class, MethodHandle[].class, MethodHandle[].class, MethodHandle[].class,
+                            int.class, int.class, Object[].class));
+                case MH_countedLoopPred:
+                    return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "countedLoopPredicate",
+                            MethodType.methodType(boolean.class, int.class, int.class));
+                case MH_countedLoopStep:
+                    return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "countedLoopStep",
+                            MethodType.methodType(int.class, int.class, int.class));
+                case MH_iteratePred:
+                    return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "iteratePredicate",
+                            MethodType.methodType(boolean.class, Iterator.class));
+                case MH_initIterator:
+                    return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "initIterator",
+                            MethodType.methodType(Iterator.class, Iterable.class));
+                case MH_iterateNext:
+                    return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "iterateNext",
+                            MethodType.methodType(Object.class, Iterator.class));
+                case MH_tryFinallyExec:
+                    return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "tryFinallyExecutor",
+                            MethodType.methodType(Object.class, MethodHandle.class, MethodHandle.class, Object[].class));
+                case MH_tryFinallyVoidExec:
+                    return IMPL_LOOKUP.findStatic(MethodHandleImpl.class, "tryFinallyVoidExecutor",
+                            MethodType.methodType(void.class, MethodHandle.class, MethodHandle.class, Object[].class));
+            }
+        } catch (ReflectiveOperationException ex) {
+            throw newInternalError(ex);
+        }
+        throw newInternalError("Unknown function index: " + idx);
+    }
 }
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandles.java	Mon Nov 23 14:37:04 2015 -0500
@@ -26,10 +26,7 @@
 package java.lang.invoke;
 
 import java.lang.reflect.*;
-import java.util.BitSet;
-import java.util.List;
-import java.util.Arrays;
-import java.util.Objects;
+import java.util.*;
 
 import sun.invoke.util.ValueConversions;
 import sun.invoke.util.VerifyAccess;
@@ -39,11 +36,13 @@
 import sun.reflect.misc.ReflectUtil;
 import sun.security.util.SecurityConstants;
 import java.lang.invoke.LambdaForm.BasicType;
-import static java.lang.invoke.LambdaForm.BasicType.*;
+
 import static java.lang.invoke.MethodHandleStatics.*;
 import static java.lang.invoke.MethodHandleImpl.Intrinsic;
 import static java.lang.invoke.MethodHandleNatives.Constants.*;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 /**
  * This class consists exclusively of static methods that operate on or return
@@ -176,7 +175,7 @@
      * equivalent of a particular <em>bytecode behavior</em>.
      * (Bytecode behaviors are described in section 5.4.3.5 of the Java Virtual Machine Specification.)
      * Here is a summary of the correspondence between these factory methods and
-     * the behavior the resulting method handles:
+     * the behavior of the resulting method handles:
      * <table border=1 cellpadding=5 summary="lookup method behaviors">
      * <tr>
      *     <th><a name="equiv"></a>lookup expression</th>
@@ -235,6 +234,10 @@
      *     <td>{@link java.lang.invoke.MethodHandles.Lookup#unreflect lookup.unreflect(aMethod)}</td>
      *     <td>({@code static})?<br>{@code T m(A*);}</td><td>{@code (T) aMethod.invoke(thisOrNull, arg*);}</td>
      * </tr>
+     * <tr>
+     *     <td>{@link java.lang.invoke.MethodHandles.Lookup#findClass lookup.findClass("C")}</td>
+     *     <td>{@code class C { ... }}</td><td>{@code C.class;}</td>
+     * </tr>
      * </table>
      *
      * Here, the type {@code C} is the class or interface being searched for a member,
@@ -255,6 +258,10 @@
      * The names {@code aMethod}, {@code aField}, and {@code aConstructor} stand
      * for reflective objects corresponding to the given members.
      * <p>
+     * The bytecode behavior for a {@code findClass} operation is a load of a constant class,
+     * as if by {@code ldc CONSTANT_Class}.
+     * The behavior is represented, not as a method handle, but directly as a {@code Class} constant.
+     * <p>
      * In cases where the given member is of variable arity (i.e., a method or constructor)
      * the returned method handle will also be of {@linkplain MethodHandle#asVarargsCollector variable arity}.
      * In all other cases, the returned method handle will be of fixed arity.
@@ -423,7 +430,7 @@
      * and the Core Reflection API
      * (as found on {@link java.lang.Class Class}).
      * <p>
-     * If a security manager is present, member lookups are subject to
+     * If a security manager is present, member and class lookups are subject to
      * additional checks.
      * From one to three calls are made to the security manager.
      * Any of these calls can refuse access by throwing a
@@ -433,6 +440,8 @@
      * {@code refc} as the containing class in which the member
      * is being sought, and {@code defc} as the class in which the
      * member is actually defined.
+     * (If a class or other type is being accessed,
+     * the {@code refc} and {@code defc} values are the class itself.)
      * The value {@code lookc} is defined as <em>not present</em>
      * if the current lookup object does not have
      * <a href="MethodHandles.Lookup.html#privacc">private access</a>.
@@ -444,11 +453,16 @@
      *     then {@link SecurityManager#checkPackageAccess
      *     smgr.checkPackageAccess(refcPkg)} is called,
      *     where {@code refcPkg} is the package of {@code refc}.
-     * <li><b>Step 2:</b>
+     * <li><b>Step 2a:</b>
      *     If the retrieved member is not public and
      *     {@code lookc} is not present, then
      *     {@link SecurityManager#checkPermission smgr.checkPermission}
      *     with {@code RuntimePermission("accessDeclaredMembers")} is called.
+     * <li><b>Step 2b:</b>
+     *     If the retrieved class has a {@code null} class loader,
+     *     and {@code lookc} is not present, then
+     *     {@link SecurityManager#checkPermission smgr.checkPermission}
+     *     with {@code RuntimePermission("getClassLoader")} is called.
      * <li><b>Step 3:</b>
      *     If the retrieved member is not public,
      *     and if {@code lookc} is not present,
@@ -458,9 +472,9 @@
      *     where {@code defcPkg} is the package of {@code defc}.
      * </ul>
      * Security checks are performed after other access checks have passed.
-     * Therefore, the above rules presuppose a member that is public,
+     * Therefore, the above rules presuppose a member or class that is public,
      * or else that is being accessed from a lookup class that has
-     * rights to access the member.
+     * rights to access the member or class.
      *
      * <h1><a name="callsens"></a>Caller sensitive methods</h1>
      * A small number of Java methods have a special property called caller sensitivity.
@@ -922,6 +936,49 @@
         }
 
         /**
+         * Looks up a class by name from the lookup context defined by this {@code Lookup} object. The static
+         * initializer of the class is not run.
+         *
+         * @param targetName the fully qualified name of the class to be looked up.
+         * @return the requested class.
+         * @exception SecurityException if a security manager is present and it
+         *            <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
+         * @throws LinkageError if the linkage fails
+         * @throws ClassNotFoundException if the class does not exist.
+         * @throws IllegalAccessException if the class is not accessible, using the allowed access
+         * modes.
+         * @exception SecurityException if a security manager is present and it
+         *                              <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
+         * @since 9
+         */
+        public Class<?> findClass(String targetName) throws ClassNotFoundException, IllegalAccessException {
+            Class<?> targetClass = Class.forName(targetName, false, lookupClass.getClassLoader());
+            return accessClass(targetClass);
+        }
+
+        /**
+         * Determines if a class can be accessed from the lookup context defined by this {@code Lookup} object. The
+         * static initializer of the class is not run.
+         *
+         * @param targetClass the class to be access-checked
+         *
+         * @return the class that has been access-checked
+         *
+         * @throws IllegalAccessException if the class is not accessible from the lookup class, using the allowed access
+         * modes.
+         * @exception SecurityException if a security manager is present and it
+         *                              <a href="MethodHandles.Lookup.html#secmgr">refuses access</a>
+         * @since 9
+         */
+        public Class<?> accessClass(Class<?> targetClass) throws IllegalAccessException {
+            if (!VerifyAccess.isClassAccessible(targetClass, lookupClass, allowedModes)) {
+                throw new MemberName(targetClass).makeAccessException("access violation", this);
+            }
+            checkSecurityManager(targetClass, null);
+            return targetClass;
+        }
+
+        /**
          * Produces an early-bound method handle for a virtual method.
          * It will bypass checks for overriding methods on the receiver,
          * <a href="MethodHandles.Lookup.html#equiv">as if called</a> from an {@code invokespecial}
@@ -995,7 +1052,7 @@
          */
         public MethodHandle findSpecial(Class<?> refc, String name, MethodType type,
                                         Class<?> specialCaller) throws NoSuchMethodException, IllegalAccessException {
-            checkSpecialCaller(specialCaller);
+            checkSpecialCaller(specialCaller, refc);
             Lookup specialLookup = this.in(specialCaller);
             MemberName method = specialLookup.resolveOrFail(REF_invokeSpecial, refc, name, type);
             return specialLookup.getDirectMethod(REF_invokeSpecial, refc, method, findBoundCallerClass(method));
@@ -1224,7 +1281,7 @@
          * @throws NullPointerException if any argument is null
          */
         public MethodHandle unreflectSpecial(Method m, Class<?> specialCaller) throws IllegalAccessException {
-            checkSpecialCaller(specialCaller);
+            checkSpecialCaller(specialCaller, null);
             Lookup specialLookup = this.in(specialCaller);
             MemberName method = new MemberName(m, true);
             assert(method.isMethod());
@@ -1444,7 +1501,15 @@
                 ReflectUtil.checkPackageAccess(refc);
             }
 
-            // Step 2:
+            if (m == null) {  // findClass or accessClass
+                // Step 2b:
+                if (!fullPowerLookup) {
+                    smgr.checkPermission(SecurityConstants.GET_CLASSLOADER_PERMISSION);
+                }
+                return;
+            }
+
+            // Step 2a:
             if (m.isPublic()) return;
             if (!fullPowerLookup) {
                 smgr.checkPermission(SecurityConstants.CHECK_MEMBER_ACCESS_PERMISSION);
@@ -1557,11 +1622,13 @@
 
         private static final boolean ALLOW_NESTMATE_ACCESS = false;
 
-        private void checkSpecialCaller(Class<?> specialCaller) throws IllegalAccessException {
+        private void checkSpecialCaller(Class<?> specialCaller, Class<?> refc) throws IllegalAccessException {
             int allowedModes = this.allowedModes;
             if (allowedModes == TRUSTED)  return;
             if (!hasPrivateAccess()
                 || (specialCaller != lookupClass()
+                       // ensure non-abstract methods in superinterfaces can be special-invoked
+                    && !(refc != null && refc.isInterface() && refc.isAssignableFrom(specialCaller))
                     && !(ALLOW_NESTMATE_ACCESS &&
                          VerifyAccess.isSamePackageMember(specialCaller, lookupClass()))))
                 throw new MemberName(specialCaller).
@@ -1888,7 +1955,7 @@
     MethodHandle spreadInvoker(MethodType type, int leadingArgCount) {
         if (leadingArgCount < 0 || leadingArgCount > type.parameterCount())
             throw newIllegalArgumentException("bad argument count", leadingArgCount);
-        type = type.asSpreaderType(Object[].class, type.parameterCount() - leadingArgCount);
+        type = type.asSpreaderType(Object[].class, leadingArgCount, type.parameterCount() - leadingArgCount);
         return type.invokers().spreadInvoker(leadingArgCount);
     }
 
@@ -2924,19 +2991,7 @@
      */
     public static
     MethodHandle foldArguments(MethodHandle target, MethodHandle combiner) {
-        int foldPos = 0;
-        MethodType targetType = target.type();
-        MethodType combinerType = combiner.type();
-        Class<?> rtype = foldArgumentChecks(foldPos, targetType, combinerType);
-        BoundMethodHandle result = target.rebind();
-        boolean dropResult = (rtype == void.class);
-        // Note:  This may cache too many distinct LFs. Consider backing off to varargs code.
-        LambdaForm lform = result.editor().foldArgumentsForm(1 + foldPos, dropResult, combinerType.basicType());
-        MethodType newType = targetType;
-        if (!dropResult)
-            newType = newType.dropParameterTypes(foldPos, foldPos + 1);
-        result = result.copyWithExtendL(newType, lform, combiner);
-        return result;
+        return foldArguments(target, 0, combiner);
     }
 
     private static Class<?> foldArgumentChecks(int foldPos, MethodType targetType, MethodType combinerType) {
@@ -2949,7 +3004,7 @@
                     .equals(targetType.parameterList().subList(afterInsertPos,
                                                                afterInsertPos + foldArgs))))
             ok = false;
-        if (ok && foldVals != 0 && combinerType.returnType() != targetType.parameterType(0))
+        if (ok && foldVals != 0 && combinerType.returnType() != targetType.parameterType(foldPos))
             ok = false;
         if (!ok)
             throw misMatchedTypes("target and combiner types", targetType, combinerType);
@@ -3011,7 +3066,7 @@
         return MethodHandleImpl.makeGuardWithTest(test, target, fallback);
     }
 
-    static RuntimeException misMatchedTypes(String what, MethodType t1, MethodType t2) {
+    static <T> RuntimeException misMatchedTypes(String what, T t1, T t2) {
         return newIllegalArgumentException(what + " must match: " + t1 + " != " + t2);
     }
 
@@ -3057,6 +3112,7 @@
      *          the given exception type, or if the method handle types do
      *          not match in their return types and their
      *          corresponding parameters
+     * @see MethodHandles#tryFinally(MethodHandle, MethodHandle)
      */
     public static
     MethodHandle catchException(MethodHandle target,
@@ -3100,4 +3156,913 @@
             throw new ClassCastException(exType.getName());
         return MethodHandleImpl.throwException(MethodType.methodType(returnType, exType));
     }
+
+    /**
+     * Constructs a method handle representing a loop with several loop variables that are updated and checked upon each
+     * iteration. Upon termination of the loop due to one of the predicates, a corresponding finalizer is run and
+     * delivers the loop's result, which is the return value of the resulting handle.
+     * <p>
+     * Intuitively, every loop is formed by one or more "clauses", each specifying a local iteration value and/or a loop
+     * exit. Each iteration of the loop executes each clause in order. A clause can optionally update its iteration
+     * variable; it can also optionally perform a test and conditional loop exit. In order to express this logic in
+     * terms of method handles, each clause will determine four actions:<ul>
+     * <li>Before the loop executes, the initialization of an iteration variable or loop invariant local.
+     * <li>When a clause executes, an update step for the iteration variable.
+     * <li>When a clause executes, a predicate execution to test for loop exit.
+     * <li>If a clause causes a loop exit, a finalizer execution to compute the loop's return value.
+     * </ul>
+     * <p>
+     * Some of these clause parts may be omitted according to certain rules, and useful default behavior is provided in
+     * this case. See below for a detailed description.
+     * <p>
+     * Each clause function, with the exception of clause initializers, is able to observe the entire loop state,
+     * because it will be passed <em>all</em> current iteration variable values, as well as all incoming loop
+     * parameters. Most clause functions will not need all of this information, but they will be formally connected as
+     * if by {@link #dropArguments}.
+     * <p>
+     * Given a set of clauses, there is a number of checks and adjustments performed to connect all the parts of the
+     * loop. They are spelled out in detail in the steps below. In these steps, every occurrence of the word "must"
+     * corresponds to a place where {@link IllegalArgumentException} may be thrown if the required constraint is not met
+     * by the inputs to the loop combinator. The term "effectively identical", applied to parameter type lists, means
+     * that they must be identical, or else one list must be a proper prefix of the other.
+     * <p>
+     * <em>Step 0: Determine clause structure.</em><ol type="a">
+     * <li>The clause array (of type {@code MethodHandle[][]} must be non-{@code null} and contain at least one element.
+     * <li>The clause array may not contain {@code null}s or sub-arrays longer than four elements.
+     * <li>Clauses shorter than four elements are treated as if they were padded by {@code null} elements to length
+     * four. Padding takes place by appending elements to the array.
+     * <li>Clauses with all {@code null}s are disregarded.
+     * <li>Each clause is treated as a four-tuple of functions, called "init", "step", "pred", and "fini".
+     * </ol>
+     * <p>
+     * <em>Step 1A: Determine iteration variables.</em><ol type="a">
+     * <li>Examine init and step function return types, pairwise, to determine each clause's iteration variable type.
+     * <li>If both functions are omitted, use {@code void}; else if one is omitted, use the other's return type; else
+     * use the common return type (they must be identical).
+     * <li>Form the list of return types (in clause order), omitting all occurrences of {@code void}.
+     * <li>This list of types is called the "common prefix".
+     * </ol>
+     * <p>
+     * <em>Step 1B: Determine loop parameters.</em><ol type="a">
+     * <li>Examine init function parameter lists.
+     * <li>Omitted init functions are deemed to have {@code null} parameter lists.
+     * <li>All init function parameter lists must be effectively identical.
+     * <li>The longest parameter list (which is necessarily unique) is called the "common suffix".
+     * </ol>
+     * <p>
+     * <em>Step 1C: Determine loop return type.</em><ol type="a">
+     * <li>Examine fini function return types, disregarding omitted fini functions.
+     * <li>If there are no fini functions, use {@code void} as the loop return type.
+     * <li>Otherwise, use the common return type of the fini functions; they must all be identical.
+     * </ol>
+     * <p>
+     * <em>Step 1D: Check other types.</em><ol type="a">
+     * <li>There must be at least one non-omitted pred function.
+     * <li>Every non-omitted pred function must have a {@code boolean} return type.
+     * </ol>
+     * <p>
+     * (Implementation Note: Steps 1A, 1B, 1C, 1D are logically independent of each other, and may be performed in any
+     * order.)
+     * <p>
+     * <em>Step 2: Determine parameter lists.</em><ol type="a">
+     * <li>The parameter list for the resulting loop handle will be the "common suffix".
+     * <li>The parameter list for init functions will be adjusted to the "common suffix". (Note that their parameter
+     * lists are already effectively identical to the common suffix.)
+     * <li>The parameter list for non-init (step, pred, and fini) functions will be adjusted to the common prefix
+     * followed by the common suffix, called the "common parameter sequence".
+     * <li>Every non-init, non-omitted function parameter list must be effectively identical to the common parameter
+     * sequence.
+     * </ol>
+     * <p>
+     * <em>Step 3: Fill in omitted functions.</em><ol type="a">
+     * <li>If an init function is omitted, use a {@linkplain #constant constant function} of the appropriate
+     * {@code null}/zero/{@code false}/{@code void} type. (For this purpose, a constant {@code void} is simply a
+     * function which does nothing and returns {@code void}; it can be obtained from another constant function by
+     * {@linkplain MethodHandle#asType type conversion}.)
+     * <li>If a step function is omitted, use an {@linkplain #identity identity function} of the clause's iteration
+     * variable type; insert dropped argument parameters before the identity function parameter for the non-{@code void}
+     * iteration variables of preceding clauses. (This will turn the loop variable into a local loop invariant.)
+     * <li>If a pred function is omitted, the corresponding fini function must also be omitted.
+     * <li>If a pred function is omitted, use a constant {@code true} function. (This will keep the loop going, as far
+     * as this clause is concerned.)
+     * <li>If a fini function is omitted, use a constant {@code null}/zero/{@code false}/{@code void} function of the
+     * loop return type.
+     * </ol>
+     * <p>
+     * <em>Step 4: Fill in missing parameter types.</em><ol type="a">
+     * <li>At this point, every init function parameter list is effectively identical to the common suffix, but some
+     * lists may be shorter. For every init function with a short parameter list, pad out the end of the list by
+     * {@linkplain #dropArguments dropping arguments}.
+     * <li>At this point, every non-init function parameter list is effectively identical to the common parameter
+     * sequence, but some lists may be shorter. For every non-init function with a short parameter list, pad out the end
+     * of the list by {@linkplain #dropArguments dropping arguments}.
+     * </ol>
+     * <p>
+     * <em>Final observations.</em><ol type="a">
+     * <li>After these steps, all clauses have been adjusted by supplying omitted functions and arguments.
+     * <li>All init functions have a common parameter type list, which the final loop handle will also have.
+     * <li>All fini functions have a common return type, which the final loop handle will also have.
+     * <li>All non-init functions have a common parameter type list, which is the common parameter sequence, of
+     * (non-{@code void}) iteration variables followed by loop parameters.
+     * <li>Each pair of init and step functions agrees in their return types.
+     * <li>Each non-init function will be able to observe the current values of all iteration variables, by means of the
+     * common prefix.
+     * </ol>
+     * <p>
+     * <em>Loop execution.</em><ol type="a">
+     * <li>When the loop is called, the loop input values are saved in locals, to be passed (as the common suffix) to
+     * every clause function. These locals are loop invariant.
+     * <li>Each init function is executed in clause order (passing the common suffix) and the non-{@code void} values
+     * are saved (as the common prefix) into locals. These locals are loop varying (unless their steps are identity
+     * functions, as noted above).
+     * <li>All function executions (except init functions) will be passed the common parameter sequence, consisting of
+     * the non-{@code void} iteration values (in clause order) and then the loop inputs (in argument order).
+     * <li>The step and pred functions are then executed, in clause order (step before pred), until a pred function
+     * returns {@code false}.
+     * <li>The non-{@code void} result from a step function call is used to update the corresponding loop variable. The
+     * updated value is immediately visible to all subsequent function calls.
+     * <li>If a pred function returns {@code false}, the corresponding fini function is called, and the resulting value
+     * is returned from the loop as a whole.
+     * </ol>
+     * <p>
+     * Here is pseudocode for the resulting loop handle. In the code, {@code V}/{@code v} represent the types / values
+     * of loop variables; {@code A}/{@code a}, those of arguments passed to the resulting loop; and {@code R}, the
+     * result types of finalizers as well as of the resulting loop.
+     * <blockquote><pre>{@code
+     * V... init...(A...);
+     * boolean pred...(V..., A...);
+     * V... step...(V..., A...);
+     * R fini...(V..., A...);
+     * R loop(A... a) {
+     *   V... v... = init...(a...);
+     *   for (;;) {
+     *     for ((v, p, s, f) in (v..., pred..., step..., fini...)) {
+     *       v = s(v..., a...);
+     *       if (!p(v..., a...)) {
+     *         return f(v..., a...);
+     *       }
+     *     }
+     *   }
+     * }
+     * }</pre></blockquote>
+     * <p>
+     * @apiNote Example:
+     * <blockquote><pre>{@code
+     * // iterative implementation of the factorial function as a loop handle
+     * static int one(int k) { return 1; }
+     * int inc(int i, int acc, int k) { return i + 1; }
+     * int mult(int i, int acc, int k) { return i * acc; }
+     * boolean pred(int i, int acc, int k) { return i < k; }
+     * int fin(int i, int acc, int k) { return acc; }
+     * // assume MH_one, MH_inc, MH_mult, MH_pred, and MH_fin are handles to the above methods
+     * // null initializer for counter, should initialize to 0
+     * MethodHandle[] counterClause = new MethodHandle[]{null, MH_inc};
+     * MethodHandle[] accumulatorClause = new MethodHandle[]{MH_one, MH_mult, MH_pred, MH_fin};
+     * MethodHandle loop = MethodHandles.loop(counterClause, accumulatorClause);
+     * assertEquals(120, loop.invoke(5));
+     * }</pre></blockquote>
+     *
+     * @param clauses an array of arrays (4-tuples) of {@link MethodHandle}s adhering to the rules described above.
+     *
+     * @return a method handle embodying the looping behavior as defined by the arguments.
+     *
+     * @throws IllegalArgumentException in case any of the constraints described above is violated.
+     *
+     * @see MethodHandles#whileLoop(MethodHandle, MethodHandle, MethodHandle)
+     * @see MethodHandles#doWhileLoop(MethodHandle, MethodHandle, MethodHandle)
+     * @see MethodHandles#countedLoop(MethodHandle, MethodHandle, MethodHandle)
+     * @see MethodHandles#iteratedLoop(MethodHandle, MethodHandle, MethodHandle)
+     * @since 9
+     */
+    public static MethodHandle loop(MethodHandle[]... clauses) {
+        // Step 0: determine clause structure.
+        checkLoop0(clauses);
+
+        List<MethodHandle> init = new ArrayList<>();
+        List<MethodHandle> step = new ArrayList<>();
+        List<MethodHandle> pred = new ArrayList<>();
+        List<MethodHandle> fini = new ArrayList<>();
+
+        Stream.of(clauses).filter(c -> Stream.of(c).anyMatch(Objects::nonNull)).forEach(clause -> {
+            init.add(clause[0]); // all clauses have at least length 1
+            step.add(clause.length <= 1 ? null : clause[1]);
+            pred.add(clause.length <= 2 ? null : clause[2]);
+            fini.add(clause.length <= 3 ? null : clause[3]);
+        });
+
+        assert Stream.of(init, step, pred, fini).map(List::size).distinct().count() == 1;
+        final int nclauses = init.size();
+
+        // Step 1A: determine iteration variables.
+        final List<Class<?>> iterationVariableTypes = new ArrayList<>();
+        for (int i = 0; i < nclauses; ++i) {
+            MethodHandle in = init.get(i);
+            MethodHandle st = step.get(i);
+            if (in == null && st == null) {
+                iterationVariableTypes.add(void.class);
+            } else if (in != null && st != null) {
+                checkLoop1a(i, in, st);
+                iterationVariableTypes.add(in.type().returnType());
+            } else {
+                iterationVariableTypes.add(in == null ? st.type().returnType() : in.type().returnType());
+            }
+        }
+        final List<Class<?>> commonPrefix = iterationVariableTypes.stream().filter(t -> t != void.class).
+                collect(Collectors.toList());
+
+        // Step 1B: determine loop parameters.
+        final List<Class<?>> empty = new ArrayList<>();
+        final List<Class<?>> commonSuffix = init.stream().filter(Objects::nonNull).map(MethodHandle::type).
+                map(MethodType::parameterList).reduce((p, q) -> p.size() >= q.size() ? p : q).orElse(empty);
+        checkLoop1b(init, commonSuffix);
+
+        // Step 1C: determine loop return type.
+        // Step 1D: check other types.
+        final Class<?> loopReturnType = fini.stream().filter(Objects::nonNull).map(MethodHandle::type).
+                map(MethodType::returnType).findFirst().orElse(void.class);
+        checkLoop1cd(pred, fini, loopReturnType);
+
+        // Step 2: determine parameter lists.
+        final List<Class<?>> commonParameterSequence = new ArrayList<>(commonPrefix);
+        commonParameterSequence.addAll(commonSuffix);
+        checkLoop2(step, pred, fini, commonParameterSequence);
+
+        // Step 3: fill in omitted functions.
+        for (int i = 0; i < nclauses; ++i) {
+            Class<?> t = iterationVariableTypes.get(i);
+            if (init.get(i) == null) {
+                init.set(i, zeroHandle(t));
+            }
+            if (step.get(i) == null) {
+                step.set(i, dropArguments(t == void.class ? zeroHandle(t) : identity(t), 0, commonPrefix.subList(0, i)));
+            }
+            if (pred.get(i) == null) {
+                pred.set(i, constant(boolean.class, true));
+            }
+            if (fini.get(i) == null) {
+                fini.set(i, zeroHandle(t));
+            }
+        }
+
+        // Step 4: fill in missing parameter types.
+        List<MethodHandle> finit = fillParameterTypes(init, commonSuffix);
+        List<MethodHandle> fstep = fillParameterTypes(step, commonParameterSequence);
+        List<MethodHandle> fpred = fillParameterTypes(pred, commonParameterSequence);
+        List<MethodHandle> ffini = fillParameterTypes(fini, commonParameterSequence);
+
+        assert finit.stream().map(MethodHandle::type).map(MethodType::parameterList).
+                allMatch(pl -> pl.equals(commonSuffix));
+        assert Stream.of(fstep, fpred, ffini).flatMap(List::stream).map(MethodHandle::type).map(MethodType::parameterList).
+                allMatch(pl -> pl.equals(commonParameterSequence));
+
+        return MethodHandleImpl.makeLoop(loopReturnType, commonSuffix, commonPrefix, finit, fstep, fpred, ffini);
+    }
+
+    private static List<MethodHandle> fillParameterTypes(List<MethodHandle> hs, final List<Class<?>> targetParams) {
+        return hs.stream().map(h -> {
+            int pc = h.type().parameterCount();
+            int tpsize = targetParams.size();
+            return pc < tpsize ? dropArguments(h, pc, targetParams.subList(pc, tpsize)) : h;
+        }).collect(Collectors.toList());
+    }
+
+    /**
+     * Constructs a {@code while} loop from an initializer, a body, and a predicate. This is a convenience wrapper for
+     * the {@linkplain #loop(MethodHandle[][]) generic loop combinator}.
+     * <p>
+     * The loop handle's result type is the same as the sole loop variable's, i.e., the result type of {@code init}.
+     * The parameter type list of {@code init} also determines that of the resulting handle. The {@code pred} handle
+     * must have an additional leading parameter of the same type as {@code init}'s result, and so must the {@code
+     * body}. These constraints follow directly from those described for the {@linkplain MethodHandles#loop(MethodHandle[][])
+     * generic loop combinator}.
+     * <p>
+     * Here is pseudocode for the resulting loop handle. In the code, {@code V}/{@code v} represent the type / value of
+     * the sole loop variable as well as the result type of the loop; and {@code A}/{@code a}, that of the argument
+     * passed to the loop.
+     * <blockquote><pre>{@code
+     * V init(A);
+     * boolean pred(V, A);
+     * V body(V, A);
+     * V whileLoop(A a) {
+     *   V v = init(a);
+     *   while (pred(v, a)) {
+     *     v = body(v, a);
+     *   }
+     *   return v;
+     * }
+     * }</pre></blockquote>
+     * <p>
+     * @apiNote Example:
+     * <blockquote><pre>{@code
+     * // implement the zip function for lists as a loop handle
+     * List<String> initZip(Iterator<String> a, Iterator<String> b) { return new ArrayList<>(); }
+     * boolean zipPred(List<String> zip, Iterator<String> a, Iterator<String> b) { return a.hasNext() && b.hasNext(); }
+     * List<String> zipStep(List<String> zip, Iterator<String> a, Iterator<String> b) {
+     *   zip.add(a.next());
+     *   zip.add(b.next());
+     *   return zip;
+     * }
+     * // assume MH_initZip, MH_zipPred, and MH_zipStep are handles to the above methods
+     * MethodHandle loop = MethodHandles.doWhileLoop(MH_initZip, MH_zipPred, MH_zipStep);
+     * List<String> a = Arrays.asList("a", "b", "c", "d");
+     * List<String> b = Arrays.asList("e", "f", "g", "h");
+     * List<String> zipped = Arrays.asList("a", "e", "b", "f", "c", "g", "d", "h");
+     * assertEquals(zipped, (List<String>) loop.invoke(a.iterator(), b.iterator()));
+     * }</pre></blockquote>
+     *
+     * <p>
+     * @implSpec The implementation of this method is equivalent to:
+     * <blockquote><pre>{@code
+     * MethodHandle whileLoop(MethodHandle init, MethodHandle pred, MethodHandle body) {
+     *     MethodHandle[]
+     *         checkExit = {null, null, pred, identity(init.type().returnType())},
+     *         varBody = {init, body};
+     *     return loop(checkExit, varBody);
+     * }
+     * }</pre></blockquote>
+     *
+     * @param init initializer: it should provide the initial value of the loop variable. This controls the loop's
+     *             result type. Passing {@code null} or a {@code void} init function will make the loop's result type
+     *             {@code void}.
+     * @param pred condition for the loop, which may not be {@code null}.
+     * @param body body of the loop, which may not be {@code null}.
+     *
+     * @return the value of the loop variable as the loop terminates.
+     * @throws IllegalArgumentException if any argument has a type inconsistent with the loop structure
+     *
+     * @see MethodHandles#loop(MethodHandle[][])
+     * @since 9
+     */
+    public static MethodHandle whileLoop(MethodHandle init, MethodHandle pred, MethodHandle body) {
+        MethodHandle fin = init == null ? zeroHandle(void.class) : identity(init.type().returnType());
+        MethodHandle[] checkExit = {null, null, pred, fin};
+        MethodHandle[] varBody = {init, body};
+        return loop(checkExit, varBody);
+    }
+
+    /**
+     * Constructs a {@code do-while} loop from an initializer, a body, and a predicate. This is a convenience wrapper
+     * for the {@linkplain MethodHandles#loop(MethodHandle[][]) generic loop combinator}.
+     * <p>
+     * The loop handle's result type is the same as the sole loop variable's, i.e., the result type of {@code init}.
+     * The parameter type list of {@code init} also determines that of the resulting handle. The {@code pred} handle
+     * must have an additional leading parameter of the same type as {@code init}'s result, and so must the {@code
+     * body}. These constraints follow directly from those described for the {@linkplain MethodHandles#loop(MethodHandle[][])
+     * generic loop combinator}.
+     * <p>
+     * Here is pseudocode for the resulting loop handle. In the code, {@code V}/{@code v} represent the type / value of
+     * the sole loop variable as well as the result type of the loop; and {@code A}/{@code a}, that of the argument
+     * passed to the loop.
+     * <blockquote><pre>{@code
+     * V init(A);
+     * boolean pred(V, A);
+     * V body(V, A);
+     * V doWhileLoop(A a) {
+     *   V v = init(a);
+     *   do {
+     *     v = body(v, a);
+     *   } while (pred(v, a));
+     *   return v;
+     * }
+     * }</pre></blockquote>
+     * <p>
+     * @apiNote Example:
+     * <blockquote><pre>{@code
+     * // int i = 0; while (i < limit) { ++i; } return i; => limit
+     * int zero(int limit) { return 0; }
+     * int step(int i, int limit) { return i + 1; }
+     * boolean pred(int i, int limit) { return i < limit; }
+     * // assume MH_zero, MH_step, and MH_pred are handles to the above methods
+     * MethodHandle loop = MethodHandles.doWhileLoop(MH_zero, MH_step, MH_pred);
+     * assertEquals(23, loop.invoke(23));
+     * }</pre></blockquote>
+     *
+     * <p>
+     * @implSpec The implementation of this method is equivalent to:
+     * <blockquote><pre>{@code
+     * MethodHandle doWhileLoop(MethodHandle init, MethodHandle body, MethodHandle pred) {
+     *     MethodHandle[] clause = { init, body, pred, identity(init.type().returnType()) };
+     *     return loop(clause);
+     * }
+     * }</pre></blockquote>
+     *
+     *
+     * @param init initializer: it should provide the initial value of the loop variable. This controls the loop's
+     *             result type. Passing {@code null} or a {@code void} init function will make the loop's result type
+     *             {@code void}.
+     * @param pred condition for the loop, which may not be {@code null}.
+     * @param body body of the loop, which may not be {@code null}.
+     *
+     * @return the value of the loop variable as the loop terminates.
+     * @throws IllegalArgumentException if any argument has a type inconsistent with the loop structure
+     *
+     * @see MethodHandles#loop(MethodHandle[][])
+     * @since 9
+     */
+    public static MethodHandle doWhileLoop(MethodHandle init, MethodHandle body, MethodHandle pred) {
+        MethodHandle fin = init == null ? zeroHandle(void.class) : identity(init.type().returnType());
+        MethodHandle[] clause = {init, body, pred, fin};
+        return loop(clause);
+    }
+
+    /**
+     * Constructs a loop that runs a given number of iterations. The loop counter is an {@code int} initialized from the
+     * {@code iterations} handle evaluation result. The counter is passed to the {@code body} function, so that must
+     * accept an initial {@code int} argument. The result of the loop execution is the final value of the additional
+     * local state. This is a convenience wrapper for the {@linkplain MethodHandles#loop(MethodHandle[][]) generic loop
+     * combinator}.
+     * <p>
+     * The result type and parameter type list of {@code init} determine those of the resulting handle. The {@code
+     * iterations} handle must accept the same parameter types as {@code init} but return an {@code int}. The {@code
+     * body} handle must accept the same parameter types as well, preceded by an {@code int} parameter for the counter,
+     * and a parameter of the same type as {@code init}'s result. These constraints follow directly from those described
+     * for the {@linkplain MethodHandles#loop(MethodHandle[][]) generic loop combinator}.
+     * <p>
+     * Here is pseudocode for the resulting loop handle. In the code, {@code V}/{@code v} represent the type / value of
+     * the sole loop variable as well as the result type of the loop; and {@code A}/{@code a}, that of the argument
+     * passed to the loop.
+     * <blockquote><pre>{@code
+     * int iterations(A);
+     * V init(A);
+     * V body(int, V, A);
+     * V countedLoop(A a) {
+     *   int end = iterations(a);
+     *   V v = init(a);
+     *   for (int i = 0; i < end; ++i) {
+     *     v = body(i, v, a);
+     *   }
+     *   return v;
+     * }
+     * }</pre></blockquote>
+     * <p>
+     * @apiNote Example:
+     * <blockquote><pre>{@code
+     * // String s = "Lambdaman!"; for (int i = 0; i < 13; ++i) { s = "na " + s; } return s;
+     * // => a variation on a well known theme
+     * String start(String arg) { return arg; }
+     * String step(int counter, String v, String arg) { return "na " + v; }
+     * // assume MH_start and MH_step are handles to the two methods above
+     * MethodHandle loop = MethodHandles.countedLoop(13, MH_start, MH_step);
+     * assertEquals("na na na na na na na na na na na na na Lambdaman!", loop.invoke("Lambdaman!"));
+     * }</pre></blockquote>
+     *
+     * <p>
+     * @implSpec The implementation of this method is equivalent to:
+     * <blockquote><pre>{@code
+     * MethodHandle countedLoop(MethodHandle iterations, MethodHandle init, MethodHandle body) {
+     *     return countedLoop(null, iterations, init, body);  // null => constant zero
+     * }
+     * }</pre></blockquote>
+     *
+     * @param iterations a handle to return the number of iterations this loop should run.
+     * @param init initializer for additional loop state. This determines the loop's result type.
+     *             Passing {@code null} or a {@code void} init function will make the loop's result type
+     *             {@code void}.
+     * @param body the body of the loop, which must not be {@code null}.
+     *             It must accept an initial {@code int} parameter (for the counter), and then any
+     *             additional loop-local variable plus loop parameters.
+     *
+     * @return a method handle representing the loop.
+     * @throws IllegalArgumentException if any argument has a type inconsistent with the loop structure
+     *
+     * @since 9
+     */
+    public static MethodHandle countedLoop(MethodHandle iterations, MethodHandle init, MethodHandle body) {
+        return countedLoop(null, iterations, init, body);
+    }
+
+    /**
+     * Constructs a loop that counts over a range of numbers. The loop counter is an {@code int} that will be
+     * initialized to the {@code int} value returned from the evaluation of the {@code start} handle and run to the
+     * value returned from {@code end} (exclusively) with a step width of 1. The counter value is passed to the {@code
+     * body} function in each iteration; it has to accept an initial {@code int} parameter
+     * for that. The result of the loop execution is the final value of the additional local state
+     * obtained by running {@code init}.
+     * This is a
+     * convenience wrapper for the {@linkplain MethodHandles#loop(MethodHandle[][]) generic loop combinator}.
+     * <p>
+     * The constraints for the {@code init} and {@code body} handles are the same as for {@link
+     * #countedLoop(MethodHandle, MethodHandle, MethodHandle)}. Additionally, the {@code start} and {@code end} handles
+     * must return an {@code int} and accept the same parameters as {@code init}.
+     * <p>
+     * Here is pseudocode for the resulting loop handle. In the code, {@code V}/{@code v} represent the type / value of
+     * the sole loop variable as well as the result type of the loop; and {@code A}/{@code a}, that of the argument
+     * passed to the loop.
+     * <blockquote><pre>{@code
+     * int start(A);
+     * int end(A);
+     * V init(A);
+     * V body(int, V, A);
+     * V countedLoop(A a) {
+     *   int s = start(a);
+     *   int e = end(a);
+     *   V v = init(a);
+     *   for (int i = s; i < e; ++i) {
+     *     v = body(i, v, a);
+     *   }
+     *   return v;
+     * }
+     * }</pre></blockquote>
+     *
+     * <p>
+     * @implSpec The implementation of this method is equivalent to:
+     * <blockquote><pre>{@code
+     * MethodHandle countedLoop(MethodHandle start, MethodHandle end, MethodHandle init, MethodHandle body) {
+     *     MethodHandle returnVar = dropArguments(identity(init.type().returnType()), 0, int.class, int.class);
+     *     // assume MH_increment and MH_lessThan are handles to x+1 and x<y of type int
+     *     MethodHandle[]
+     *         indexVar = {start, MH_increment}, // i = start; i = i+1
+     *         loopLimit = {end, null, MH_lessThan, returnVar }, // i<end
+     *         bodyClause = {init, dropArguments(body, 1, int.class)};  // v = body(i, v);
+     *     return loop(indexVar, loopLimit, bodyClause);
+     * }
+     * }</pre></blockquote>
+     *
+     * @param start a handle to return the start value of the loop counter.
+     *              If it is {@code null}, a constant zero is assumed.
+     * @param end a non-{@code null} handle to return the end value of the loop counter (the loop will run to {@code end-1}).
+     * @param init initializer for additional loop state. This determines the loop's result type.
+     *             Passing {@code null} or a {@code void} init function will make the loop's result type
+     *             {@code void}.
+     * @param body the body of the loop, which must not be {@code null}.
+     *             It must accept an initial {@code int} parameter (for the counter), and then any
+     *             additional loop-local variable plus loop parameters.
+     *
+     * @return a method handle representing the loop.
+     * @throws IllegalArgumentException if any argument has a type inconsistent with the loop structure
+     *
+     * @since 9
+     */
+    public static MethodHandle countedLoop(MethodHandle start, MethodHandle end, MethodHandle init, MethodHandle body) {
+        MethodHandle returnVar = dropArguments(init == null ? zeroHandle(void.class) : identity(init.type().returnType()),
+                0, int.class, int.class);
+        MethodHandle[] indexVar = {start, MethodHandleImpl.getConstantHandle(MethodHandleImpl.MH_countedLoopStep)};
+        MethodHandle[] loopLimit = {end, null, MethodHandleImpl.getConstantHandle(MethodHandleImpl.MH_countedLoopPred), returnVar};
+        MethodHandle[] bodyClause = {init, dropArguments(body, 1, int.class)};
+        return loop(indexVar, loopLimit, bodyClause);
+    }
+
+    /**
+     * Constructs a loop that ranges over the elements produced by an {@code Iterator<T>}.
+     * The iterator will be produced by the evaluation of the {@code iterator} handle.
+     * If this handle is passed as {@code null} the method {@link Iterable#iterator} will be used instead,
+     * and will be applied to a leading argument of the loop handle.
+     * Each value produced by the iterator is passed to the {@code body}, which must accept an initial {@code T} parameter.
+     * The result of the loop execution is the final value of the additional local state
+     * obtained by running {@code init}.
+     * <p>
+     * This is a convenience wrapper for the
+     * {@linkplain MethodHandles#loop(MethodHandle[][]) generic loop combinator}, and the constraints imposed on the {@code body}
+     * handle follow directly from those described for the latter.
+     * <p>
+     * Here is pseudocode for the resulting loop handle. In the code, {@code V}/{@code v} represent the type / value of
+     * the loop variable as well as the result type of the loop; {@code T}/{@code t}, that of the elements of the
+     * structure the loop iterates over, and {@code A}/{@code a}, that of the argument passed to the loop.
+     * <blockquote><pre>{@code
+     * Iterator<T> iterator(A);  // defaults to Iterable::iterator
+     * V init(A);
+     * V body(T,V,A);
+     * V iteratedLoop(A a) {
+     *   Iterator<T> it = iterator(a);
+     *   V v = init(a);
+     *   for (T t : it) {
+     *     v = body(t, v, a);
+     *   }
+     *   return v;
+     * }
+     * }</pre></blockquote>
+     * <p>
+     * The type {@code T} may be either a primitive or reference.
+     * Since type {@code Iterator<T>} is erased in the method handle representation to the raw type
+     * {@code Iterator}, the {@code iteratedLoop} combinator adjusts the leading argument type for {@code body}
+     * to {@code Object} as if by the {@link MethodHandle#asType asType} conversion method.
+     * Therefore, if an iterator of the wrong type appears as the loop is executed,
+     * runtime exceptions may occur as the result of dynamic conversions performed by {@code asType}.
+     * <p>
+     * @apiNote Example:
+     * <blockquote><pre>{@code
+     * // reverse a list
+     * List<String> reverseStep(String e, List<String> r) {
+     *   r.add(0, e);
+     *   return r;
+     * }
+     * List<String> newArrayList() { return new ArrayList<>(); }
+     * // assume MH_reverseStep, MH_newArrayList are handles to the above methods
+     * MethodHandle loop = MethodHandles.iteratedLoop(null, MH_newArrayList, MH_reverseStep);
+     * List<String> list = Arrays.asList("a", "b", "c", "d", "e");
+     * List<String> reversedList = Arrays.asList("e", "d", "c", "b", "a");
+     * assertEquals(reversedList, (List<String>) loop.invoke(list));
+     * }</pre></blockquote>
+     * <p>
+     * @implSpec The implementation of this method is equivalent to:
+     * <blockquote><pre>{@code
+     * MethodHandle iteratedLoop(MethodHandle iterator, MethodHandle init, MethodHandle body) {
+     *     // assume MH_next and MH_hasNext are handles to methods of Iterator
+     *     Class<?> itype = iterator.type().returnType();
+     *     Class<?> ttype = body.type().parameterType(0);
+     *     MethodHandle returnVar = dropArguments(identity(init.type().returnType()), 0, itype);
+     *     MethodHandle nextVal = MH_next.asType(MH_next.type().changeReturnType(ttype));
+     *     MethodHandle[]
+     *         iterVar = {iterator, null, MH_hasNext, returnVar}, // it = iterator(); while (it.hasNext)
+     *         bodyClause = {init, filterArgument(body, 0, nextVal)};  // v = body(t, v, a);
+     *     return loop(iterVar, bodyClause);
+     * }
+     * }</pre></blockquote>
+     *
+     * @param iterator a handle to return the iterator to start the loop.
+     *             Passing {@code null} will make the loop call {@link Iterable#iterator()} on the first
+     *             incoming value.
+     * @param init initializer for additional loop state. This determines the loop's result type.
+     *             Passing {@code null} or a {@code void} init function will make the loop's result type
+     *             {@code void}.
+     * @param body the body of the loop, which must not be {@code null}.
+     *             It must accept an initial {@code T} parameter (for the iterated values), and then any
+     *             additional loop-local variable plus loop parameters.
+     *
+     * @return a method handle embodying the iteration loop functionality.
+     * @throws IllegalArgumentException if any argument has a type inconsistent with the loop structure
+     *
+     * @since 9
+     */
+    public static MethodHandle iteratedLoop(MethodHandle iterator, MethodHandle init, MethodHandle body) {
+        checkIteratedLoop(body);
+
+        MethodHandle initit = MethodHandleImpl.getConstantHandle(MethodHandleImpl.MH_initIterator);
+        MethodHandle initIterator = iterator == null ?
+                initit.asType(initit.type().changeParameterType(0, body.type().parameterType(init == null ? 1 : 2))) :
+                iterator;
+        Class<?> itype = initIterator.type().returnType();
+        Class<?> ttype = body.type().parameterType(0);
+
+        MethodHandle returnVar =
+                dropArguments(init == null ? zeroHandle(void.class) : identity(init.type().returnType()), 0, itype);
+        MethodHandle initnx = MethodHandleImpl.getConstantHandle(MethodHandleImpl.MH_iterateNext);
+        MethodHandle nextVal = initnx.asType(initnx.type().changeReturnType(ttype));
+
+        MethodHandle[] iterVar = {initIterator, null, MethodHandleImpl.getConstantHandle(MethodHandleImpl.MH_iteratePred), returnVar};
+        MethodHandle[] bodyClause = {init, filterArgument(body, 0, nextVal)};
+
+        return loop(iterVar, bodyClause);
+    }
+
+    /**
+     * Makes a method handle that adapts a {@code target} method handle by wrapping it in a {@code try-finally} block.
+     * Another method handle, {@code cleanup}, represents the functionality of the {@code finally} block. Any exception
+     * thrown during the execution of the {@code target} handle will be passed to the {@code cleanup} handle. The
+     * exception will be rethrown, unless {@code cleanup} handle throws an exception first.  The
+     * value returned from the {@code cleanup} handle's execution will be the result of the execution of the
+     * {@code try-finally} handle.
+     * <p>
+     * The {@code cleanup} handle will be passed one or two additional leading arguments.
+     * The first is the exception thrown during the
+     * execution of the {@code target} handle, or {@code null} if no exception was thrown.
+     * The second is the result of the execution of the {@code target} handle, or, if it throws an exception,
+     * a {@code null}, zero, or {@code false} value of the required type is supplied as a placeholder.
+     * The second argument is not present if the {@code target} handle has a {@code void} return type.
+     * (Note that, except for argument type conversions, combinators represent {@code void} values in parameter lists
+     * by omitting the corresponding paradoxical arguments, not by inserting {@code null} or zero values.)
+     * <p>
+     * The {@code target} and {@code cleanup} handles' return types must be the same. Their parameter type lists also
+     * must be the same, but the {@code cleanup} handle must accept one or two more leading parameters:<ul>
+     * <li>a {@code Throwable}, which will carry the exception thrown by the {@code target} handle (if any); and
+     * <li>a parameter of the same type as the return type of both {@code target} and {@code cleanup}, which will carry
+     * the result from the execution of the {@code target} handle.
+     * This parameter is not present if the {@code target} returns {@code void}.
+     * </ul>
+     * <p>
+     * The pseudocode for the resulting adapter looks as follows. In the code, {@code V} represents the result type of
+     * the {@code try/finally} construct; {@code A}/{@code a}, the types and values of arguments to the resulting
+     * handle consumed by the cleanup; and {@code B}/{@code b}, those of arguments to the resulting handle discarded by
+     * the cleanup.
+     * <blockquote><pre>{@code
+     * V target(A..., B...);
+     * V cleanup(Throwable, V, A...);
+     * V adapter(A... a, B... b) {
+     *   V result = (zero value for V);
+     *   Throwable throwable = null;
+     *   try {
+     *     result = target(a..., b...);
+     *   } catch (Throwable t) {
+     *     throwable = t;
+     *     throw t;
+     *   } finally {
+     *     result = cleanup(throwable, result, a...);
+     *   }
+     *   return result;
+     * }
+     * }</pre></blockquote>
+     * <p>
+     * Note that the saved arguments ({@code a...} in the pseudocode) cannot
+     * be modified by execution of the target, and so are passed unchanged
+     * from the caller to the cleanup, if it is invoked.
+     * <p>
+     * The target and cleanup must return the same type, even if the cleanup
+     * always throws.
+     * To create such a throwing cleanup, compose the cleanup logic
+     * with {@link #throwException throwException},
+     * in order to create a method handle of the correct return type.
+     * <p>
+     * Note that {@code tryFinally} never converts exceptions into normal returns.
+     * In rare cases where exceptions must be converted in that way, first wrap
+     * the target with {@link #catchException(MethodHandle, Class, MethodHandle)}
+     * to capture an outgoing exception, and then wrap with {@code tryFinally}.
+     *
+     * @param target the handle whose execution is to be wrapped in a {@code try} block.
+     * @param cleanup the handle that is invoked in the finally block.
+     *
+     * @return a method handle embodying the {@code try-finally} block composed of the two arguments.
+     * @throws NullPointerException if any argument is null
+     * @throws IllegalArgumentException if {@code cleanup} does not accept
+     *          the required leading arguments, or if the method handle types do
+     *          not match in their return types and their
+     *          corresponding trailing parameters
+     *
+     * @see MethodHandles#catchException(MethodHandle, Class, MethodHandle)
+     * @since 9
+     */
+    public static MethodHandle tryFinally(MethodHandle target, MethodHandle cleanup) {
+        List<Class<?>> targetParamTypes = target.type().parameterList();
+        List<Class<?>> cleanupParamTypes = cleanup.type().parameterList();
+        Class<?> rtype = target.type().returnType();
+
+        checkTryFinally(target, cleanup);
+
+        // Match parameter lists: if the cleanup has a shorter parameter list than the target, add ignored arguments.
+        int tpSize = targetParamTypes.size();
+        int cpPrefixLength = rtype == void.class ? 1 : 2;
+        int cpSize = cleanupParamTypes.size();
+        MethodHandle aCleanup = cpSize - cpPrefixLength < tpSize ?
+                dropArguments(cleanup, cpSize, targetParamTypes.subList(tpSize - (cpSize - cpPrefixLength), tpSize)) :
+                cleanup;
+
+        MethodHandle aTarget = target.asSpreader(Object[].class, target.type().parameterCount());
+        aCleanup = aCleanup.asSpreader(Object[].class, tpSize);
+
+        return MethodHandleImpl.makeTryFinally(aTarget, aCleanup, rtype, targetParamTypes);
+    }
+
+    /**
+     * Adapts a target method handle by pre-processing some of its arguments, starting at a given position, and then
+     * calling the target with the result of the pre-processing, inserted into the original sequence of arguments just
+     * before the folded arguments.
+     * <p>
+     * This method is closely related to {@link #foldArguments(MethodHandle, MethodHandle)}, but allows to control the
+     * position in the parameter list at which folding takes place. The argument controlling this, {@code pos}, is a
+     * zero-based index. The aforementioned method {@link #foldArguments(MethodHandle, MethodHandle)} assumes position
+     * 0.
+     * <p>
+     * @apiNote Example:
+     * <blockquote><pre>{@code
+    import static java.lang.invoke.MethodHandles.*;
+    import static java.lang.invoke.MethodType.*;
+    ...
+    MethodHandle trace = publicLookup().findVirtual(java.io.PrintStream.class,
+    "println", methodType(void.class, String.class))
+    .bindTo(System.out);
+    MethodHandle cat = lookup().findVirtual(String.class,
+    "concat", methodType(String.class, String.class));
+    assertEquals("boojum", (String) cat.invokeExact("boo", "jum"));
+    MethodHandle catTrace = foldArguments(cat, 1, trace);
+    // also prints "jum":
+    assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum"));
+     * }</pre></blockquote>
+     * <p> Here is pseudocode for the resulting adapter:
+     * <blockquote><pre>{@code
+     * // there are N arguments in A...
+     * T target(Z..., V, A[N]..., B...);
+     * V combiner(A...);
+     * T adapter(Z... z, A... a, B... b) {
+     *   V v = combiner(a...);
+     *   return target(z..., v, a..., b...);
+     * }
+     * // and if the combiner has a void return:
+     * T target2(Z..., A[N]..., B...);
+     * void combiner2(A...);
+     * T adapter2(Z... z, A... a, B... b) {
+     *   combiner2(a...);
+     *   return target2(z..., a..., b...);
+     * }
+     * }</pre></blockquote>
+     *
+     * @param target the method handle to invoke after arguments are combined
+     * @param pos the position at which to start folding and at which to insert the folding result; if this is {@code
+     *            0}, the effect is the same as for {@link #foldArguments(MethodHandle, MethodHandle)}.
+     * @param combiner method handle to call initially on the incoming arguments
+     * @return method handle which incorporates the specified argument folding logic
+     * @throws NullPointerException if either argument is null
+     * @throws IllegalArgumentException if {@code combiner}'s return type
+     *          is non-void and not the same as the argument type at position {@code pos} of
+     *          the target signature, or if the {@code N} argument types at position {@code pos}
+     *          of the target signature
+     *          (skipping one matching the {@code combiner}'s return type)
+     *          are not identical with the argument types of {@code combiner}
+     *
+     * @see #foldArguments(MethodHandle, MethodHandle)
+     * @since 9
+     */
+    public static MethodHandle foldArguments(MethodHandle target, int pos, MethodHandle combiner) {
+        MethodType targetType = target.type();
+        MethodType combinerType = combiner.type();
+        Class<?> rtype = foldArgumentChecks(pos, targetType, combinerType);
+        BoundMethodHandle result = target.rebind();
+        boolean dropResult = rtype == void.class;
+        LambdaForm lform = result.editor().foldArgumentsForm(1 + pos, dropResult, combinerType.basicType());
+        MethodType newType = targetType;
+        if (!dropResult) {
+            newType = newType.dropParameterTypes(pos, pos + 1);
+        }
+        result = result.copyWithExtendL(newType, lform, combiner);
+        return result;
+    }
+
+    /**
+     * Wrap creation of a proper zero handle for a given type.
+     *
+     * @param type the type.
+     *
+     * @return a zero value for the given type.
+     */
+    static MethodHandle zeroHandle(Class<?> type) {
+        return type.isPrimitive() ?  zero(Wrapper.forPrimitiveType(type), type) : zero(Wrapper.OBJECT, type);
+    }
+
+    private static void checkLoop0(MethodHandle[][] clauses) {
+        if (clauses == null || clauses.length == 0) {
+            throw newIllegalArgumentException("null or no clauses passed");
+        }
+        if (Stream.of(clauses).anyMatch(Objects::isNull)) {
+            throw newIllegalArgumentException("null clauses are not allowed");
+        }
+        if (Stream.of(clauses).anyMatch(c -> c.length > 4)) {
+            throw newIllegalArgumentException("All loop clauses must be represented as MethodHandle arrays with at most 4 elements.");
+        }
+    }
+
+    private static void checkLoop1a(int i, MethodHandle in, MethodHandle st) {
+        if (in.type().returnType() != st.type().returnType()) {
+            throw misMatchedTypes("clause " + i + ": init and step return types", in.type().returnType(),
+                    st.type().returnType());
+        }
+    }
+
+    private static void checkLoop1b(List<MethodHandle> init, List<Class<?>> commonSuffix) {
+        if (init.stream().filter(Objects::nonNull).map(MethodHandle::type).map(MethodType::parameterList).
+                anyMatch(pl -> !pl.equals(commonSuffix.subList(0, pl.size())))) {
+            throw newIllegalArgumentException("found non-effectively identical init parameter type lists: " + init +
+                    " (common suffix: " + commonSuffix + ")");
+        }
+    }
+
+    private static void checkLoop1cd(List<MethodHandle> pred, List<MethodHandle> fini, Class<?> loopReturnType) {
+        if (fini.stream().filter(Objects::nonNull).map(MethodHandle::type).map(MethodType::returnType).
+                anyMatch(t -> t != loopReturnType)) {
+            throw newIllegalArgumentException("found non-identical finalizer return types: " + fini + " (return type: " +
+                    loopReturnType + ")");
+        }
+
+        if (!pred.stream().filter(Objects::nonNull).findFirst().isPresent()) {
+            throw newIllegalArgumentException("no predicate found", pred);
+        }
+        if (pred.stream().filter(Objects::nonNull).map(MethodHandle::type).map(MethodType::returnType).
+                anyMatch(t -> t != boolean.class)) {
+            throw newIllegalArgumentException("predicates must have boolean return type", pred);
+        }
+    }
+
+    private static void checkLoop2(List<MethodHandle> step, List<MethodHandle> pred, List<MethodHandle> fini, List<Class<?>> commonParameterSequence) {
+        if (Stream.of(step, pred, fini).flatMap(List::stream).filter(Objects::nonNull).map(MethodHandle::type).
+                map(MethodType::parameterList).anyMatch(pl -> !pl.equals(commonParameterSequence.subList(0, pl.size())))) {
+            throw newIllegalArgumentException("found non-effectively identical parameter type lists:\nstep: " + step +
+                    "\npred: " + pred + "\nfini: " + fini + " (common parameter sequence: " + commonParameterSequence + ")");
+        }
+    }
+
+    private static void checkIteratedLoop(MethodHandle body) {
+        if (null == body) {
+            throw newIllegalArgumentException("iterated loop body must not be null");
+        }
+    }
+
+    private static void checkTryFinally(MethodHandle target, MethodHandle cleanup) {
+        Class<?> rtype = target.type().returnType();
+        if (rtype != cleanup.type().returnType()) {
+            throw misMatchedTypes("target and return types", cleanup.type().returnType(), rtype);
+        }
+        List<Class<?>> cleanupParamTypes = cleanup.type().parameterList();
+        if (!Throwable.class.isAssignableFrom(cleanupParamTypes.get(0))) {
+            throw misMatchedTypes("cleanup first argument and Throwable", cleanup.type(), Throwable.class);
+        }
+        if (rtype != void.class && cleanupParamTypes.get(1) != rtype) {
+            throw misMatchedTypes("cleanup second argument and target return type", cleanup.type(), rtype);
+        }
+        // The cleanup parameter list (minus the leading Throwable and result parameters) must be a sublist of the
+        // target parameter list.
+        int cleanupArgIndex = rtype == void.class ? 1 : 2;
+        if (!cleanupParamTypes.subList(cleanupArgIndex, cleanupParamTypes.size()).
+                equals(target.type().parameterList().subList(0, cleanupParamTypes.size() - cleanupArgIndex))) {
+            throw misMatchedTypes("cleanup parameters after (Throwable,result) and target parameter list prefix",
+                    cleanup.type(), target.type());
+        }
+    }
+
 }
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodType.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodType.java	Mon Nov 23 14:37:04 2015 -0500
@@ -469,12 +469,13 @@
 
     /** Replace the last arrayLength parameter types with the component type of arrayType.
      * @param arrayType any array type
+     * @param pos position at which to spread
      * @param arrayLength the number of parameter types to change
      * @return the resulting type
      */
-    /*non-public*/ MethodType asSpreaderType(Class<?> arrayType, int arrayLength) {
+    /*non-public*/ MethodType asSpreaderType(Class<?> arrayType, int pos, int arrayLength) {
         assert(parameterCount() >= arrayLength);
-        int spreadPos = ptypes.length - arrayLength;
+        int spreadPos = pos;
         if (arrayLength == 0)  return this;  // nothing to change
         if (arrayType == Object[].class) {
             if (isGeneric())  return this;  // nothing to change
@@ -489,10 +490,10 @@
         }
         Class<?> elemType = arrayType.getComponentType();
         assert(elemType != null);
-        for (int i = spreadPos; i < ptypes.length; i++) {
+        for (int i = spreadPos; i < spreadPos + arrayLength; i++) {
             if (ptypes[i] != elemType) {
                 Class<?>[] fixedPtypes = ptypes.clone();
-                Arrays.fill(fixedPtypes, i, ptypes.length, elemType);
+                Arrays.fill(fixedPtypes, i, spreadPos + arrayLength, elemType);
                 return methodType(rtype, fixedPtypes);
             }
         }
@@ -512,12 +513,14 @@
 
     /** Delete the last parameter type and replace it with arrayLength copies of the component type of arrayType.
      * @param arrayType any array type
+     * @param pos position at which to insert parameters
      * @param arrayLength the number of parameter types to insert
      * @return the resulting type
      */
-    /*non-public*/ MethodType asCollectorType(Class<?> arrayType, int arrayLength) {
+    /*non-public*/ MethodType asCollectorType(Class<?> arrayType, int pos, int arrayLength) {
         assert(parameterCount() >= 1);
-        assert(lastParameterType().isAssignableFrom(arrayType));
+        assert(pos < ptypes.length);
+        assert(ptypes[pos].isAssignableFrom(arrayType));
         MethodType res;
         if (arrayType == Object[].class) {
             res = genericMethodType(arrayLength);
@@ -532,7 +535,11 @@
         if (ptypes.length == 1) {
             return res;
         } else {
-            return res.insertParameterTypes(0, parameterList().subList(0, ptypes.length-1));
+            // insert after (if need be), then before
+            if (pos < parameterList().size() - 1) {
+                res = res.insertParameterTypes(arrayLength, parameterList().subList(pos + 1, parameterList().size()));
+            }
+            return res.insertParameterTypes(0, parameterList().subList(0, pos));
         }
     }
 
--- a/jdk/src/java.base/share/classes/java/time/Clock.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/src/java.base/share/classes/java/time/Clock.java	Mon Nov 23 14:37:04 2015 -0500
@@ -65,7 +65,7 @@
 import java.io.ObjectInputStream;
 import static java.time.LocalTime.NANOS_PER_MINUTE;
 import static java.time.LocalTime.NANOS_PER_SECOND;
-
+import static java.time.LocalTime.NANOS_PER_MILLI;
 import java.io.Serializable;
 import java.util.Objects;
 import java.util.TimeZone;
@@ -182,7 +182,7 @@
     }
 
     /**
-     * Obtains a clock that returns the current instant using best available
+     * Obtains a clock that returns the current instant using the best available
      * system clock.
      * <p>
      * This clock is based on the best available system clock.
@@ -206,8 +206,32 @@
 
     //-------------------------------------------------------------------------
     /**
+     * Obtains a clock that returns the current instant ticking in whole milliseconds
+     * using the best available system clock.
+     * <p>
+     * This clock will always have the nano-of-second field truncated to milliseconds.
+     * This ensures that the visible time ticks in whole milliseconds.
+     * The underlying clock is the best available system clock, equivalent to
+     * using {@link #system(ZoneId)}.
+     * <p>
+     * Implementations may use a caching strategy for performance reasons.
+     * As such, it is possible that the start of the millisecond observed via this
+     * clock will be later than that observed directly via the underlying clock.
+     * <p>
+     * The returned implementation is immutable, thread-safe and {@code Serializable}.
+     * It is equivalent to {@code tick(system(zone), Duration.ofMillis(1))}.
+     *
+     * @param zone  the time-zone to use to convert the instant to date-time, not null
+     * @return a clock that ticks in whole milliseconds using the specified zone, not null
+     */
+    public static Clock tickMillis(ZoneId zone) {
+        return new TickClock(system(zone), NANOS_PER_MILLI);
+    }
+
+    //-------------------------------------------------------------------------
+    /**
      * Obtains a clock that returns the current instant ticking in whole seconds
-     * using best available system clock.
+     * using the best available system clock.
      * <p>
      * This clock will always have the nano-of-second field set to zero.
      * This ensures that the visible time ticks in whole seconds.
@@ -230,7 +254,7 @@
 
     /**
      * Obtains a clock that returns the current instant ticking in whole minutes
-     * using best available system clock.
+     * using the best available system clock.
      * <p>
      * This clock will always have the nano-of-second and second-of-minute fields set to zero.
      * This ensures that the visible time ticks in whole minutes.
--- a/jdk/src/java.base/share/classes/java/time/LocalDate.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/src/java.base/share/classes/java/time/LocalDate.java	Mon Nov 23 14:37:04 2015 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -81,7 +81,7 @@
 import java.io.ObjectInputStream;
 import java.io.Serializable;
 import java.time.chrono.ChronoLocalDate;
-import java.time.chrono.Era;
+import java.time.chrono.IsoEra;
 import java.time.chrono.IsoChronology;
 import java.time.format.DateTimeFormatter;
 import java.time.format.DateTimeParseException;
@@ -220,12 +220,8 @@
      */
     public static LocalDate now(Clock clock) {
         Objects.requireNonNull(clock, "clock");
-        // inline to avoid creating object and Instant checks
         final Instant now = clock.instant();  // called once
-        ZoneOffset offset = clock.getZone().getRules().getOffset(now);
-        long epochSec = now.getEpochSecond() + offset.getTotalSeconds();  // overflow caught later
-        long epochDay = Math.floorDiv(epochSec, SECONDS_PER_DAY);
-        return LocalDate.ofEpochDay(epochDay);
+        return ofInstant(now, clock.getZone());
     }
 
     //-----------------------------------------------------------------------
@@ -300,6 +296,30 @@
 
     //-----------------------------------------------------------------------
     /**
+     * Obtains an instance of {@code LocalDate} from an {@code Instant} and zone ID.
+     * <p>
+     * This creates a local date based on the specified instant.
+     * First, the offset from UTC/Greenwich is obtained using the zone ID and instant,
+     * which is simple as there is only one valid offset for each instant.
+     * Then, the instant and offset are used to calculate the local date.
+     *
+     * @param instant  the instant to create the date from, not null
+     * @param zone  the time-zone, which may be an offset, not null
+     * @return the local date, not null
+     * @throws DateTimeException if the result exceeds the supported range
+     */
+    public static LocalDate ofInstant(Instant instant, ZoneId zone) {
+        Objects.requireNonNull(instant, "instant");
+        Objects.requireNonNull(zone, "zone");
+        ZoneRules rules = zone.getRules();
+        ZoneOffset offset = rules.getOffset(instant);
+        long localSecond = instant.getEpochSecond() + offset.getTotalSeconds();
+        long localEpochDay = Math.floorDiv(localSecond, SECONDS_PER_DAY);
+        return ofEpochDay(localEpochDay);
+    }
+
+    //-----------------------------------------------------------------------
+    /**
      * Obtains an instance of {@code LocalDate} from the epoch day count.
      * <p>
      * This returns a {@code LocalDate} with the specified epoch-day.
@@ -712,15 +732,12 @@
      * Users of this class should typically ignore this method as it exists primarily
      * to fulfill the {@link ChronoLocalDate} contract where it is necessary to support
      * the Japanese calendar system.
-     * <p>
-     * The returned era will be a singleton capable of being compared with the constants
-     * in {@link IsoChronology} using the {@code ==} operator.
      *
-     * @return the {@code IsoChronology} era constant applicable at this date, not null
+     * @return the IsoEra applicable at this date, not null
      */
     @Override // override for Javadoc
-    public Era getEra() {
-        return ChronoLocalDate.super.getEra();
+    public IsoEra getEra() {
+        return (getYear() >= 1 ? IsoEra.CE : IsoEra.BCE);
     }
 
     /**
--- a/jdk/src/java.base/share/classes/java/time/LocalTime.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/src/java.base/share/classes/java/time/LocalTime.java	Mon Nov 23 14:37:04 2015 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -190,9 +190,13 @@
      */
     static final long MICROS_PER_DAY = SECONDS_PER_DAY * 1000_000L;
     /**
+     * Nanos per millisecond.
+     */
+    static final long NANOS_PER_MILLI = 1000_000L;
+    /**
      * Nanos per second.
      */
-    static final long NANOS_PER_SECOND = 1000_000_000L;
+    static final long NANOS_PER_SECOND =  1000_000_000L;
     /**
      * Nanos per minute.
      */
@@ -272,12 +276,8 @@
      */
     public static LocalTime now(Clock clock) {
         Objects.requireNonNull(clock, "clock");
-        // inline OffsetTime factory to avoid creating object and InstantProvider checks
         final Instant now = clock.instant();  // called once
-        ZoneOffset offset = clock.getZone().getRules().getOffset(now);
-        long localSecond = now.getEpochSecond() + offset.getTotalSeconds();  // overflow caught later
-        int secsOfDay = (int) Math.floorMod(localSecond, SECONDS_PER_DAY);
-        return ofNanoOfDay(secsOfDay * NANOS_PER_SECOND + now.getNano());
+        return ofInstant(now, clock.getZone());
     }
 
     //-----------------------------------------------------------------------
@@ -343,6 +343,27 @@
         return create(hour, minute, second, nanoOfSecond);
     }
 
+    /**
+     * Obtains an instance of {@code LocalTime} from an {@code Instant} and zone ID.
+     * <p>
+     * This creates a local time based on the specified instant.
+     * First, the offset from UTC/Greenwich is obtained using the zone ID and instant,
+     * which is simple as there is only one valid offset for each instant.
+     * Then, the instant and offset are used to calculate the local time.
+     *
+     * @param instant  the instant to create the time from, not null
+     * @param zone  the time-zone, which may be an offset, not null
+     * @return the local time, not null
+     */
+     public static LocalTime ofInstant(Instant instant, ZoneId zone) {
+         Objects.requireNonNull(instant, "instant");
+         Objects.requireNonNull(zone, "zone");
+         ZoneOffset offset = zone.getRules().getOffset(instant);
+         long localSecond = instant.getEpochSecond() + offset.getTotalSeconds();
+         int secsOfDay = (int) Math.floorMod(localSecond, SECONDS_PER_DAY);
+         return ofNanoOfDay(secsOfDay * NANOS_PER_SECOND + instant.getNano());
+     }
+
     //-----------------------------------------------------------------------
     /**
      * Obtains an instance of {@code LocalTime} from a second-of-day value.
--- a/jdk/src/java.base/share/classes/java/util/Arrays.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/src/java.base/share/classes/java/util/Arrays.java	Mon Nov 23 14:37:04 2015 -0500
@@ -3305,6 +3305,103 @@
         return true;
     }
 
+    /**
+     * Returns {@code true} if the two specified arrays of Objects are
+     * <i>equal</i> to one another.
+     *
+     * <p>Two arrays are considered equal if both arrays contain the same number
+     * of elements, and all corresponding pairs of elements in the two arrays
+     * are equal.  In other words, the two arrays are equal if they contain the
+     * same elements in the same order.  Also, two array references are
+     * considered equal if both are {@code null}.
+     *
+     * <p>Two objects {@code e1} and {@code e2} are considered <i>equal</i> if,
+     * given the specified comparator, {@code cmp.compare(e1, e2) == 0}.
+     *
+     * @param a one array to be tested for equality
+     * @param a2 the other array to be tested for equality
+     * @param cmp the comparator to compare array elements
+     * @param <T> the type of array elements
+     * @return {@code true} if the two arrays are equal
+     * @throws NullPointerException if the comparator is {@code null}
+     * @since 9
+     */
+    public static <T> boolean equals(T[] a, T[] a2, Comparator<? super T> cmp) {
+        Objects.requireNonNull(cmp);
+        if (a==a2)
+            return true;
+        if (a==null || a2==null)
+            return false;
+
+        int length = a.length;
+        if (a2.length != length)
+            return false;
+
+        for (int i=0; i<length; i++) {
+            if (cmp.compare(a[i], a2[i]) != 0)
+                return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Returns true if the two specified arrays of Objects, over the specified
+     * ranges, are <i>equal</i> to one another.
+     *
+     * <p>Two arrays are considered equal if the number of elements covered by
+     * each range is the same, and all corresponding pairs of elements over the
+     * specified ranges in the two arrays are equal.  In other words, two arrays
+     * are equal if they contain, over the specified ranges, the same elements
+     * in the same order.
+     *
+     * <p>Two objects {@code e1} and {@code e2} are considered <i>equal</i> if,
+     * given the specified comparator, {@code cmp.compare(e1, e2) == 0}.
+     *
+     * @param a the first array to be tested for equality
+     * @param aFromIndex the index (inclusive) of the first element in the
+     *                   first array to be tested
+     * @param aToIndex the index (exclusive) of the last element in the
+     *                 first array to be tested
+     * @param b the second array to be tested fro equality
+     * @param bFromIndex the index (inclusive) of the first element in the
+     *                   second array to be tested
+     * @param bToIndex the index (exclusive) of the last element in the
+     *                 second array to be tested
+     * @param cmp the comparator to compare array elements
+     * @param <T> the type of array elements
+     * @return {@code true} if the two arrays, over the specified ranges, are
+     *         equal
+     * @throws IllegalArgumentException
+     *         if {@code aFromIndex > aToIndex} or
+     *         if {@code bFromIndex > bToIndex}
+     * @throws ArrayIndexOutOfBoundsException
+     *         if {@code aFromIndex < 0 or aToIndex > a.length} or
+     *         if {@code bFromIndex < 0 or bToIndex > b.length}
+     * @throws NullPointerException
+     *         if either array or the comparator is {@code null}
+     * @since 9
+     */
+    public static <T> boolean equals(T[] a, int aFromIndex, int aToIndex,
+                                     T[] b, int bFromIndex, int bToIndex,
+                                     Comparator<? super T> cmp) {
+        Objects.requireNonNull(cmp);
+        rangeCheck(a.length, aFromIndex, aToIndex);
+        rangeCheck(b.length, bFromIndex, bToIndex);
+
+        int aLength = aToIndex - aFromIndex;
+        int bLength = bToIndex - bFromIndex;
+        if (aLength != bLength)
+            return false;
+
+        for (int i = 0; i < aLength; i++) {
+            if (cmp.compare(a[aFromIndex++], b[bFromIndex++]) != 0)
+                return false;
+        }
+
+        return true;
+    }
+
     // Filling
 
     /**
@@ -8744,9 +8841,7 @@
      * <pre>{@code
      *     pl >= 0 &&
      *     pl < Math.min(a.length, b.length) &&
-     *     IntStream.range(0, pl).
-     *         map(i -> cmp.compare(a[i], b[i])).
-     *         allMatch(c -> c == 0) &&
+     *     Arrays.equals(a, 0, pl, b, 0, pl, cmp)
      *     cmp.compare(a[pl], b[pl]) != 0
      * }</pre>
      * Note that a common prefix length of {@code 0} indicates that the first
@@ -8756,9 +8851,9 @@
      * prefix if the following expression is true:
      * <pre>{@code
      *     a.length != b.length &&
-     *     IntStream.range(0, Math.min(a.length, b.length)).
-     *         map(i -> cmp.compare(a[i], b[i])).
-     *         allMatch(c -> c == 0) &&
+     *     Arrays.equals(a, 0, Math.min(a.length, b.length),
+     *                   b, 0, Math.min(a.length, b.length),
+     *                   cmp)
      * }</pre>
      *
      * @param a the first array to be tested for a mismatch
@@ -8815,9 +8910,7 @@
      * <pre>{@code
      *     pl >= 0 &&
      *     pl < Math.min(aToIndex - aFromIndex, bToIndex - bFromIndex) &&
-     *     IntStream.range(0, pl).
-     *         map(i -> cmp.compare(a[aFromIndex + i], b[bFromIndex + i])).
-     *         allMatch(c -> c == 0) &&
+     *     Arrays.equals(a, aFromIndex, aFromIndex + pl, b, bFromIndex, bFromIndex + pl, cmp) &&
      *     cmp.compare(a[aFromIndex + pl], b[bFromIndex + pl]) != 0
      * }</pre>
      * Note that a common prefix length of {@code 0} indicates that the first
@@ -8829,9 +8922,9 @@
      * if the following expression is true:
      * <pre>{@code
      *     (aToIndex - aFromIndex) != (bToIndex - bFromIndex) &&
-     *     IntStream.range(0, Math.min(aToIndex - aFromIndex, bToIndex - bFromIndex)).
-     *         map(i -> cmp.compare(a[aFromIndex + i], b[bFromIndex + i])).
-     *         allMatch(c -> c == 0)
+     *     Arrays.equals(a, 0, Math.min(aToIndex - aFromIndex, bToIndex - bFromIndex),
+     *                   b, 0, Math.min(aToIndex - aFromIndex, bToIndex - bFromIndex),
+     *                   cmp)
      * }</pre>
      *
      * @param a the first array to be tested for a mismatch
--- a/jdk/src/java.base/share/classes/java/util/Objects.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/src/java.base/share/classes/java/util/Objects.java	Mon Nov 23 14:37:04 2015 -0500
@@ -352,15 +352,16 @@
      * @param b the second out of bound value
      * @param oobe the exception mapping function that when applied with out of
      *        bounds arguments returns a runtime exception.  If {@code null}
-     *        then, it's as if an exception mapping function was supplied that
+     *        then, it is as if an exception mapping function was supplied that
      *        returns {@link IndexOutOfBoundsException} for any given arguments.
      * @return the runtime exception
      */
     private static RuntimeException outOfBounds(
             int a, int b, BiFunction<Integer, Integer, ? extends RuntimeException> oobe) {
-        return oobe == null
-               ? new IndexOutOfBoundsException(a, b)
-               : oobe.apply(a, b);
+        RuntimeException e = oobe == null
+                             ? null : oobe.apply(a, b);
+        return e == null
+               ? new IndexOutOfBoundsException(a, b) : e;
     }
 
     /**
@@ -408,8 +409,10 @@
      * @param length the upper-bound (exclusive) of the range
      * @param oobe the exception mapping function that when applied with out
      *        of bounds arguments returns a runtime exception.  If {@code null}
-     *        then, it's as if an exception mapping function was supplied that
-     *        returns {@link IndexOutOfBoundsException} for any given arguments.
+     *        or returns {@code null} then, it is as if an exception mapping
+     *        function was supplied that returns
+     *        {@link IndexOutOfBoundsException} for any given arguments.
+     *        Exceptions thrown by the function are relayed to the caller.
      * @return {@code index} if it is within bounds of the range
      * @throws T if the {@code index} is out of bounds, then a runtime exception
      *         is thrown that is the result of applying the out of bounds
@@ -484,8 +487,10 @@
      * @param length the upper-bound (exclusive) the range
      * @param oobe the exception mapping function that when applied with out
      *        of bounds arguments returns a runtime exception.  If {@code null}
-     *        then, it's as if an exception mapping function was supplied that
-     *        returns {@link IndexOutOfBoundsException} for any given arguments.
+     *        or returns {@code null} then, it is as if an exception mapping
+     *        function was supplied that returns
+     *        {@link IndexOutOfBoundsException} for any given arguments.
+     *        Exceptions thrown by the function are relayed to the caller.
      * @return {@code fromIndex} if the sub-range within bounds of the range
      * @throws T if the sub-range is out of bounds, then a runtime exception is
      *         thrown that is the result of applying the out of bounds arguments
@@ -553,8 +558,10 @@
      * @param length the upper-bound (exclusive) of the range
      * @param oobe the exception mapping function that when applied with out
      *        of bounds arguments returns a runtime exception.  If {@code null}
-     *        then, it's as if an exception mapping function was supplied that
-     *        returns {@link IndexOutOfBoundsException} for any given arguments.
+     *        or returns {@code null} then, it is as if an exception mapping
+     *        function was supplied that returns
+     *        {@link IndexOutOfBoundsException} for any given arguments.
+     *        Exceptions thrown by the function are relayed to the caller.
      * @return {@code fromIndex} if the sub-range within bounds of the range
      * @throws T if the sub-range is out of bounds, then a runtime exception is
      *         thrown that is the result of applying the out of bounds arguments
--- a/jdk/src/java.base/share/classes/java/util/TreeMap.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/src/java.base/share/classes/java/util/TreeMap.java	Mon Nov 23 14:37:04 2015 -0500
@@ -2581,19 +2581,17 @@
     }
 
     /**
-     * Find the level down to which to assign all nodes BLACK.  This is the
-     * last `full' level of the complete binary tree produced by
-     * buildTree. The remaining nodes are colored RED. (This makes a `nice'
-     * set of color assignments wrt future insertions.) This level number is
+     * Finds the level down to which to assign all nodes BLACK.  This is the
+     * last `full' level of the complete binary tree produced by buildTree.
+     * The remaining nodes are colored RED. (This makes a `nice' set of
+     * color assignments wrt future insertions.) This level number is
      * computed by finding the number of splits needed to reach the zeroeth
-     * node.  (The answer is ~lg(N), but in any case must be computed by same
-     * quick O(lg(N)) loop.)
+     * node.
+     *
+     * @param size the (non-negative) number of keys in the tree to be built
      */
-    private static int computeRedLevel(int sz) {
-        int level = 0;
-        for (int m = sz - 1; m >= 0; m = m / 2 - 1)
-            level++;
-        return level;
+    private static int computeRedLevel(int size) {
+        return 31 - Integer.numberOfLeadingZeros(size + 1);
     }
 
     /**
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/jdk/internal/logger/AbstractLoggerWrapper.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,380 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.internal.logger;
+
+import java.util.ResourceBundle;
+import java.util.function.Supplier;
+import java.lang.System.Logger;
+import java.lang.System.Logger.Level;
+import sun.util.logging.PlatformLogger;
+
+/**
+ * An implementation of {@link System.Logger System.Logger}
+ * that redirects all calls to a wrapped instance of {@link
+ * System.Logger System.Logger}
+ *
+ * @param <L> Type of the wrapped Logger: {@code Logger} or
+ *        an extension of that interface.
+ *
+ */
+abstract class AbstractLoggerWrapper<L extends Logger>
+    implements Logger, PlatformLogger.Bridge, PlatformLogger.ConfigurableBridge {
+
+    AbstractLoggerWrapper() { }
+
+    abstract L wrapped();
+
+    abstract PlatformLogger.Bridge platformProxy();
+
+    L getWrapped() {
+        return wrapped();
+    }
+
+    @Override
+    public final String getName() {
+        return wrapped().getName();
+    }
+
+    // -----------------------------------------------------------------
+    // Generic methods taking a Level as parameter
+    // -----------------------------------------------------------------
+
+
+    @Override
+    public boolean isLoggable(Level level) {
+        return wrapped().isLoggable(level);
+    }
+
+    @Override
+    public void log(Level level, String msg) {
+        wrapped().log(level, msg);
+    }
+
+    @Override
+    public void log(Level level,
+                    Supplier<String> msgSupplier) {
+        wrapped().log(level, msgSupplier);
+    }
+
+    @Override
+    public void log(Level level, Object obj) {
+        wrapped().log(level, obj);
+    }
+
+    @Override
+    public void log(Level level,
+                   String msg, Throwable thrown) {
+        wrapped().log(level, msg, thrown);
+    }
+
+    @Override
+    public void log(Level level, Supplier<String> msgSupplier, Throwable thrown) {
+        wrapped().log(level, msgSupplier, thrown);
+    }
+
+    @Override
+    public void log(Level level,
+                    String format, Object... params) {
+        wrapped().log(level, format, params);
+    }
+
+    @Override
+    public void log(Level level, ResourceBundle bundle,
+                    String key, Throwable thrown) {
+        wrapped().log(level, bundle, key, thrown);
+    }
+
+    @Override
+    public void log(Level level, ResourceBundle bundle,
+                    String format, Object... params) {
+        wrapped().log(level, bundle, format, params);
+    }
+
+    // ---------------------------------------------------------
+    // Methods from PlatformLogger.Bridge
+    // ---------------------------------------------------------
+
+    @Override
+    public boolean isLoggable(PlatformLogger.Level level) {
+        final PlatformLogger.Bridge platformProxy = platformProxy();
+        if (platformProxy == null) return isLoggable(level.systemLevel());
+        else return platformProxy.isLoggable(level);
+    }
+
+    @Override
+    public boolean isEnabled() {
+        final PlatformLogger.Bridge platformProxy = platformProxy();
+        return platformProxy == null || platformProxy.isEnabled();
+    }
+
+    @Override
+    public void log(PlatformLogger.Level level, String msg) {
+        final PlatformLogger.Bridge platformProxy = platformProxy();
+        if (platformProxy == null)  {
+            wrapped().log(level.systemLevel(), msg);
+        } else {
+            platformProxy.log(level, msg);
+        }
+    }
+
+    @Override
+    public void log(PlatformLogger.Level level, String msg, Throwable thrown) {
+        final PlatformLogger.Bridge platformProxy = platformProxy();
+        if (platformProxy == null)  {
+            wrapped().log(level.systemLevel(), msg, thrown);
+        } else {
+            platformProxy.log(level, msg, thrown);
+        }
+    }
+
+    @Override
+    public void log(PlatformLogger.Level level, String msg, Object... params) {
+        final PlatformLogger.Bridge platformProxy = platformProxy();
+        if (platformProxy == null)  {
+            wrapped().log(level.systemLevel(), msg, params);
+        } else {
+            platformProxy.log(level, msg, params);
+        }
+    }
+
+    @Override
+    public void log(PlatformLogger.Level level, Supplier<String> msgSupplier) {
+        final PlatformLogger.Bridge platformProxy = platformProxy();
+        if (platformProxy == null)  {
+            wrapped().log(level.systemLevel(),msgSupplier);
+        } else {
+            platformProxy.log(level,msgSupplier);
+        }
+    }
+
+    @Override
+    public void log(PlatformLogger.Level level, Throwable thrown,
+                    Supplier<String> msgSupplier) {
+        final PlatformLogger.Bridge platformProxy = platformProxy();
+        if (platformProxy == null)  {
+            wrapped().log(level.systemLevel(), msgSupplier, thrown);
+        } else {
+            platformProxy.log(level, thrown, msgSupplier);
+        }
+    }
+
+    @Override
+    public void logp(PlatformLogger.Level level, String sourceClass,
+                     String sourceMethod, String msg) {
+        final PlatformLogger.Bridge platformProxy = platformProxy();
+        if (platformProxy == null)  {
+            if (sourceClass == null && sourceMethod == null) { // best effort
+                wrapped().log(level.systemLevel(), msg);
+            } else {
+                Level systemLevel = level.systemLevel();
+                Logger wrapped = wrapped();
+                if (wrapped.isLoggable(systemLevel)) {
+                    sourceClass  = sourceClass  == null ? "" : sourceClass;
+                    sourceMethod = sourceMethod == null ? "" : sourceMethod;
+                    msg = msg == null ? "" : msg;
+                    wrapped.log(systemLevel, String.format("[%s %s] %s",
+                            sourceClass, sourceMethod, msg));
+                }
+            }
+        } else {
+            platformProxy.logp(level, sourceClass, sourceMethod, msg);
+        }
+    }
+
+    @Override
+    public void logp(PlatformLogger.Level level, String sourceClass,
+                     String sourceMethod, Supplier<String> msgSupplier) {
+        final PlatformLogger.Bridge platformProxy = platformProxy();
+        if (platformProxy == null) { // best effort
+            if (sourceClass == null && sourceMethod == null) {
+                wrapped().log(level.systemLevel(), msgSupplier);
+            } else {
+                Level systemLevel = level.systemLevel();
+                Logger wrapped = wrapped();
+                if (wrapped.isLoggable(systemLevel)) {
+                    final String sClass  = sourceClass  == null ? "" : sourceClass;
+                    final String sMethod = sourceMethod == null ? "" : sourceMethod;
+                    wrapped.log(systemLevel, () -> String.format("[%s %s] %s",
+                            sClass, sMethod, msgSupplier.get()));
+                }
+            }
+        } else {
+            platformProxy.logp(level, sourceClass, sourceMethod, msgSupplier);
+        }
+    }
+
+    @Override
+    public void logp(PlatformLogger.Level level, String sourceClass,
+                     String sourceMethod, String msg, Object... params) {
+        final PlatformLogger.Bridge platformProxy = platformProxy();
+        if (platformProxy == null) { // best effort
+            if (sourceClass == null && sourceMethod == null) {
+                wrapped().log(level.systemLevel(), msg, params);
+            } else {
+                Level systemLevel = level.systemLevel();
+                Logger wrapped = wrapped();
+                if (wrapped.isLoggable(systemLevel)) {
+                    sourceClass  = sourceClass  == null ? "" : sourceClass;
+                    sourceMethod = sourceMethod == null ? "" : sourceMethod;
+                    msg = msg == null ? "" : msg;
+                    wrapped.log(systemLevel, String.format("[%s %s] %s",
+                            sourceClass, sourceMethod, msg), params);
+                }
+            }
+        } else {
+            platformProxy.logp(level, sourceClass, sourceMethod, msg, params);
+        }
+    }
+
+    @Override
+    public void logp(PlatformLogger.Level level, String sourceClass,
+                     String sourceMethod, String msg, Throwable thrown) {
+        final PlatformLogger.Bridge platformProxy = platformProxy();
+        if (platformProxy == null) { // best effort
+            if (sourceClass == null && sourceMethod == null) {
+                wrapped().log(level.systemLevel(), msg, thrown);
+            } else {
+                Level systemLevel = level.systemLevel();
+                Logger wrapped = wrapped();
+                if (wrapped.isLoggable(systemLevel)) {
+                    sourceClass  = sourceClass  == null ? "" : sourceClass;
+                    sourceMethod = sourceMethod == null ? "" : sourceMethod;
+                    msg = msg == null ? "" : msg;
+                    wrapped.log(systemLevel, String.format("[%s %s] %s",
+                            sourceClass, sourceMethod, msg), thrown);
+                }
+            }
+        } else {
+            platformProxy.logp(level, sourceClass, sourceMethod, msg, thrown);
+        }
+    }
+
+    @Override
+    public void logp(PlatformLogger.Level level, String sourceClass,
+                     String sourceMethod, Throwable thrown,
+                     Supplier<String> msgSupplier) {
+        final PlatformLogger.Bridge platformProxy = platformProxy();
+        if (platformProxy == null)  { // best effort
+            if (sourceClass == null && sourceMethod == null) {
+                wrapped().log(level.systemLevel(), msgSupplier, thrown);
+            } else {
+                Level systemLevel = level.systemLevel();
+                Logger wrapped = wrapped();
+                if (wrapped.isLoggable(systemLevel)) {
+                    final String sClass  = sourceClass  == null ? "" : sourceClass;
+                    final String sMethod = sourceMethod == null ? "" : sourceMethod;
+                    wrapped.log(systemLevel,  () -> String.format("[%s %s] %s",
+                            sClass, sMethod, msgSupplier.get()), thrown);
+                }
+            }
+        } else {
+            platformProxy.logp(level, sourceClass, sourceMethod,
+                               thrown, msgSupplier);
+        }
+    }
+
+    @Override
+    public void logrb(PlatformLogger.Level level, String sourceClass,
+                      String sourceMethod, ResourceBundle bundle,
+                      String msg, Object... params) {
+        final PlatformLogger.Bridge platformProxy = platformProxy();
+        if (platformProxy == null)  { // best effort
+            if (bundle != null || sourceClass == null && sourceMethod == null) {
+                wrapped().log(level.systemLevel(), bundle, msg, params);
+            } else {
+                Level systemLevel = level.systemLevel();
+                Logger wrapped = wrapped();
+                if (wrapped.isLoggable(systemLevel)) {
+                    sourceClass  = sourceClass  == null ? "" : sourceClass;
+                    sourceMethod = sourceMethod == null ? "" : sourceMethod;
+                    msg = msg == null ? "" : msg;
+                    wrapped.log(systemLevel, bundle, String.format("[%s %s] %s",
+                            sourceClass, sourceMethod, msg), params);
+                }
+            }
+        } else {
+            platformProxy.logrb(level, sourceClass, sourceMethod,
+                    bundle, msg, params);
+        }
+    }
+
+    @Override
+    public void logrb(PlatformLogger.Level level, String sourceClass,
+                      String sourceMethod, ResourceBundle bundle, String msg,
+                      Throwable thrown) {
+        final PlatformLogger.Bridge platformProxy = platformProxy();
+        if (platformProxy == null)  { // best effort
+            if (bundle != null || sourceClass == null && sourceMethod == null) {
+                wrapped().log(level.systemLevel(), bundle, msg, thrown);
+            } else {
+                Level systemLevel = level.systemLevel();
+                Logger wrapped = wrapped();
+                if (wrapped.isLoggable(systemLevel)) {
+                    sourceClass  = sourceClass  == null ? "" : sourceClass;
+                    sourceMethod = sourceMethod == null ? "" : sourceMethod;
+                    msg = msg == null ? "" : msg;
+                    wrapped.log(systemLevel, bundle, String.format("[%s %s] %s",
+                            sourceClass, sourceMethod, msg), thrown);
+                }
+            }
+        } else {
+            platformProxy.logrb(level, sourceClass, sourceMethod, bundle,
+                                msg, thrown);
+        }
+    }
+
+    @Override
+    public void logrb(PlatformLogger.Level level, ResourceBundle bundle,
+                      String msg, Throwable thrown) {
+        final PlatformLogger.Bridge platformProxy = platformProxy();
+        if (platformProxy == null)  {
+            wrapped().log(level.systemLevel(), bundle, msg, thrown);
+        } else {
+            platformProxy.logrb(level, bundle, msg, thrown);
+        }
+    }
+
+    @Override
+    public void logrb(PlatformLogger.Level level, ResourceBundle bundle,
+                      String msg, Object... params) {
+        final PlatformLogger.Bridge platformProxy = platformProxy();
+        if (platformProxy == null)  {
+            wrapped().log(level.systemLevel(), bundle, msg, params);
+        } else {
+            platformProxy.logrb(level, bundle, msg, params);
+        }
+    }
+
+
+    @Override
+    public LoggerConfiguration getLoggerConfiguration() {
+        final PlatformLogger.Bridge platformProxy = platformProxy();
+        return platformProxy == null ? null
+               : PlatformLogger.ConfigurableBridge
+                       .getLoggerConfiguration(platformProxy);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/jdk/internal/logger/BootstrapLogger.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,1074 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.internal.logger;
+
+import java.security.AccessControlContext;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.ResourceBundle;
+import java.util.ServiceLoader;
+import java.util.function.BooleanSupplier;
+import java.util.function.Function;
+import java.util.function.Supplier;
+import java.lang.System.LoggerFinder;
+import java.lang.System.Logger;
+import java.lang.System.Logger.Level;
+import java.lang.ref.WeakReference;
+import java.util.Objects;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import sun.misc.InnocuousThread;
+import sun.misc.VM;
+import sun.util.logging.PlatformLogger;
+import jdk.internal.logger.LazyLoggers.LazyLoggerAccessor;
+
+/**
+ * The BootstrapLogger class handles all the logic needed by Lazy Loggers
+ * to delay the creation of System.Logger instances until the VM is booted.
+ * By extension - it also contains the logic that will delay the creation
+ * of JUL Loggers until the LogManager is initialized by the application, in
+ * the common case where JUL is the default and there is no custom JUL
+ * configuration.
+ *
+ * A BootstrapLogger instance is both a Logger and a
+ * PlatformLogger.Bridge instance, which will put all Log messages in a queue
+ * until the VM is booted.
+ * Once the VM is booted, it obtain the real System.Logger instance from the
+ * LoggerFinder and flushes the message to the queue.
+ *
+ * There are a few caveat:
+ *  - the queue may not be flush until the next message is logged after
+ *    the VM is booted
+ *  - while the BootstrapLogger is active, the default implementation
+ *    for all convenience methods is used
+ *  - PlatformLogger.setLevel calls are ignored
+ *
+ *
+ */
+public final class BootstrapLogger implements Logger, PlatformLogger.Bridge,
+        PlatformLogger.ConfigurableBridge {
+
+    // We use the BootstrapExecutors class to submit delayed messages
+    // to an independent InnocuousThread which will ensure that
+    // delayed log events will be clearly identified as messages that have
+    // been delayed during the boot sequence.
+    private static class BootstrapExecutors implements ThreadFactory {
+
+        // Maybe that should be made configurable with system properties.
+        static final long KEEP_EXECUTOR_ALIVE_SECONDS = 30;
+
+        // The BootstrapMessageLoggerTask is a Runnable which keeps
+        // a hard ref to the ExecutorService that owns it.
+        // This ensure that the ExecutorService is not gc'ed until the thread
+        // has stopped running.
+        private static class BootstrapMessageLoggerTask implements Runnable {
+            ExecutorService owner;
+            Runnable run;
+            public BootstrapMessageLoggerTask(ExecutorService owner, Runnable r) {
+                this.owner = owner;
+                this.run = r;
+            }
+            @Override
+            public void run() {
+                try {
+                    run.run();
+                } finally {
+                    owner = null; // allow the ExecutorService to be gced.
+                }
+            }
+        }
+
+        private static volatile WeakReference<ExecutorService> executorRef;
+        private static ExecutorService getExecutor() {
+            WeakReference<ExecutorService> ref = executorRef;
+            ExecutorService executor = ref == null ? null : ref.get();
+            if (executor != null) return executor;
+            synchronized (BootstrapExecutors.class) {
+                ref = executorRef;
+                executor = ref == null ? null : ref.get();
+                if (executor == null) {
+                    executor = new ThreadPoolExecutor(0, 1,
+                            KEEP_EXECUTOR_ALIVE_SECONDS, TimeUnit.SECONDS,
+                            new LinkedBlockingQueue<>(), new BootstrapExecutors());
+                }
+                // The executor service will be elligible for gc
+                // KEEP_EXECUTOR_ALIVE_SECONDS seconds (30s)
+                // after the execution of its last pending task.
+                executorRef = new WeakReference<>(executor);
+                return executorRef.get();
+            }
+        }
+
+        @Override
+        public Thread newThread(Runnable r) {
+            ExecutorService owner = getExecutor();
+            Thread thread = AccessController.doPrivileged(new PrivilegedAction<Thread>() {
+                @Override
+                public Thread run() {
+                    Thread t = new InnocuousThread(new BootstrapMessageLoggerTask(owner, r));
+                    t.setName("BootstrapMessageLoggerTask-"+t.getName());
+                    return t;
+                }
+            }, null, new RuntimePermission("enableContextClassLoaderOverride"));
+            thread.setDaemon(true);
+            return thread;
+        }
+
+        static void submit(Runnable r) {
+            getExecutor().execute(r);
+        }
+
+        // This is used by tests.
+        static void join(Runnable r) {
+            try {
+                getExecutor().submit(r).get();
+            } catch (InterruptedException | ExecutionException ex) {
+                // should not happen
+                throw new RuntimeException(ex);
+            }
+        }
+
+        // This is used by tests.
+        static void awaitPendingTasks() {
+            WeakReference<ExecutorService> ref = executorRef;
+            ExecutorService executor = ref == null ? null : ref.get();
+            if (ref == null) {
+                synchronized(BootstrapExecutors.class) {
+                    ref = executorRef;
+                    executor = ref == null ? null : ref.get();
+                }
+            }
+            if (executor != null) {
+                // since our executor uses a FIFO and has a single thread
+                // then awaiting the execution of its pending tasks can be done
+                // simply by registering a new task and waiting until it
+                // completes. This of course would not work if we were using
+                // several threads, but we don't.
+                join(()->{});
+            }
+        }
+
+        // This is used by tests.
+        static boolean isAlive() {
+            WeakReference<ExecutorService> ref = executorRef;
+            ExecutorService executor = ref == null ? null : ref.get();
+            if (executor != null) return true;
+            synchronized (BootstrapExecutors.class) {
+                ref = executorRef;
+                executor = ref == null ? null : ref.get();
+                return executor != null;
+            }
+        }
+
+        // The pending log event queue. The first event is the head, and
+        // new events are added at the tail
+        static LogEvent head, tail;
+
+        static void enqueue(LogEvent event) {
+            if (event.next != null) return;
+            synchronized (BootstrapExecutors.class) {
+                if (event.next != null) return;
+                event.next = event;
+                if (tail == null) {
+                    head = tail = event;
+                } else {
+                    tail.next = event;
+                    tail = event;
+                }
+            }
+        }
+
+        static void flush() {
+            LogEvent event;
+            // drain the whole queue
+            synchronized(BootstrapExecutors.class) {
+                event = head;
+                head = tail = null;
+            }
+            while(event != null) {
+                LogEvent.log(event);
+                synchronized(BootstrapExecutors.class) {
+                    LogEvent prev = event;
+                    event = (event.next == event ? null : event.next);
+                    prev.next = null;
+                }
+            }
+        }
+    }
+
+    // The accessor in which this logger is temporarily set.
+    final LazyLoggerAccessor holder;
+
+    BootstrapLogger(LazyLoggerAccessor holder) {
+        this.holder = holder;
+    }
+
+    // Temporary data object storing log events
+    // It would be nice to use a Consumer<Logger> instead of a LogEvent.
+    // This way we could simply do things like:
+    //    push((logger) -> logger.log(level, msg));
+    // Unfortunately, if we come to here it means we are in the bootsraping
+    // phase where using lambdas is not safe yet - so we have to use a
+    // a data object instead...
+    //
+    static final class LogEvent {
+        // only one of these two levels should be non null
+        final Level level;
+        final PlatformLogger.Level platformLevel;
+        final BootstrapLogger bootstrap;
+
+        final ResourceBundle bundle;
+        final String msg;
+        final Throwable thrown;
+        final Object[] params;
+        final Supplier<String> msgSupplier;
+        final String sourceClass;
+        final String sourceMethod;
+        final long timeMillis;
+        final long nanoAdjustment;
+
+        // because logging a message may entail calling toString() on
+        // the parameters etc... we need to store the context of the
+        // caller who logged the message - so that we can reuse it when
+        // we finally log the message.
+        final AccessControlContext acc;
+
+        // The next event in the queue
+        LogEvent next;
+
+        private LogEvent(BootstrapLogger bootstrap, Level level,
+                ResourceBundle bundle, String msg,
+                Throwable thrown, Object[] params) {
+            this.acc = AccessController.getContext();
+            this.timeMillis = System.currentTimeMillis();
+            this.nanoAdjustment = VM.getNanoTimeAdjustment(timeMillis);
+            this.level = level;
+            this.platformLevel = null;
+            this.bundle = bundle;
+            this.msg = msg;
+            this.msgSupplier = null;
+            this.thrown = thrown;
+            this.params = params;
+            this.sourceClass = null;
+            this.sourceMethod = null;
+            this.bootstrap = bootstrap;
+        }
+
+        private LogEvent(BootstrapLogger bootstrap, Level level,
+                Supplier<String> msgSupplier,
+                Throwable thrown, Object[] params) {
+            this.acc = AccessController.getContext();
+            this.timeMillis = System.currentTimeMillis();
+            this.nanoAdjustment = VM.getNanoTimeAdjustment(timeMillis);
+            this.level = level;
+            this.platformLevel = null;
+            this.bundle = null;
+            this.msg = null;
+            this.msgSupplier = msgSupplier;
+            this.thrown = thrown;
+            this.params = params;
+            this.sourceClass = null;
+            this.sourceMethod = null;
+            this.bootstrap = bootstrap;
+        }
+
+        private LogEvent(BootstrapLogger bootstrap,
+                PlatformLogger.Level platformLevel,
+                String sourceClass, String sourceMethod,
+                ResourceBundle bundle, String msg,
+                Throwable thrown, Object[] params) {
+            this.acc = AccessController.getContext();
+            this.timeMillis = System.currentTimeMillis();
+            this.nanoAdjustment = VM.getNanoTimeAdjustment(timeMillis);
+            this.level = null;
+            this.platformLevel = platformLevel;
+            this.bundle = bundle;
+            this.msg = msg;
+            this.msgSupplier = null;
+            this.thrown = thrown;
+            this.params = params;
+            this.sourceClass = sourceClass;
+            this.sourceMethod = sourceMethod;
+            this.bootstrap = bootstrap;
+        }
+
+        private LogEvent(BootstrapLogger bootstrap,
+                PlatformLogger.Level platformLevel,
+                String sourceClass, String sourceMethod,
+                Supplier<String> msgSupplier,
+                Throwable thrown, Object[] params) {
+            this.acc = AccessController.getContext();
+            this.timeMillis = System.currentTimeMillis();
+            this.nanoAdjustment = VM.getNanoTimeAdjustment(timeMillis);
+            this.level = null;
+            this.platformLevel = platformLevel;
+            this.bundle = null;
+            this.msg = null;
+            this.msgSupplier = msgSupplier;
+            this.thrown = thrown;
+            this.params = params;
+            this.sourceClass = sourceClass;
+            this.sourceMethod = sourceMethod;
+            this.bootstrap = bootstrap;
+        }
+
+        // Log this message in the given logger. Do not call directly.
+        // Use LogEvent.log(LogEvent, logger) instead.
+        private void log(Logger logger) {
+            assert platformLevel == null && level != null;
+            //new Exception("logging delayed message").printStackTrace();
+            if (msgSupplier != null) {
+                if (thrown != null) {
+                    logger.log(level, msgSupplier, thrown);
+                } else {
+                    logger.log(level, msgSupplier);
+                }
+            } else {
+                // BootstrapLoggers are never localized so we can safely
+                // use the method that takes a ResourceBundle parameter
+                // even when that resource bundle is null.
+                if (thrown != null) {
+                    logger.log(level, bundle, msg, thrown);
+                } else {
+                    logger.log(level, bundle, msg, params);
+                }
+            }
+        }
+
+        // Log this message in the given logger.  Do not call directly.
+        // Use LogEvent.doLog(LogEvent, logger) instead.
+        private void log(PlatformLogger.Bridge logger) {
+            assert platformLevel != null && level == null;
+            if (sourceClass == null) {
+                if (msgSupplier != null) {
+                    if (thrown != null) {
+                        logger.log(platformLevel, thrown, msgSupplier);
+                    } else {
+                        logger.log(platformLevel, msgSupplier);
+                    }
+                } else {
+                    // BootstrapLoggers are never localized so we can safely
+                    // use the method that takes a ResourceBundle parameter
+                    // even when that resource bundle is null.
+                    if (thrown != null) {
+                        logger.logrb(platformLevel, bundle, msg, thrown);
+                    } else {
+                        logger.logrb(platformLevel, bundle, msg, params);
+                    }
+                }
+            } else {
+                if (msgSupplier != null) {
+                    if (thrown != null) {
+                        logger.log(platformLevel, sourceClass, sourceMethod, thrown, msgSupplier);
+                    } else {
+                        logger.log(platformLevel,sourceClass, sourceMethod,  msgSupplier);
+                    }
+                } else {
+                    // BootstrapLoggers are never localized so we can safely
+                    // use the method that takes a ResourceBundle parameter
+                    // even when that resource bundle is null.
+                    if (thrown != null) {
+                        logger.logrb(platformLevel, sourceClass, sourceMethod, bundle, msg, thrown);
+                    } else {
+                        logger.logrb(platformLevel, sourceClass, sourceMethod, bundle, msg, params);
+                    }
+                }
+            }
+        }
+
+        // non default methods from Logger interface
+        static LogEvent valueOf(BootstrapLogger bootstrap, Level level,
+                ResourceBundle bundle, String key, Throwable thrown) {
+            return new LogEvent(Objects.requireNonNull(bootstrap),
+                                Objects.requireNonNull(level), bundle, key,
+                                thrown, null);
+        }
+        static LogEvent valueOf(BootstrapLogger bootstrap, Level level,
+                ResourceBundle bundle, String format, Object[] params) {
+            return new LogEvent(Objects.requireNonNull(bootstrap),
+                                Objects.requireNonNull(level), bundle, format,
+                                null, params);
+        }
+        static LogEvent valueOf(BootstrapLogger bootstrap, Level level,
+                                Supplier<String> msgSupplier, Throwable thrown) {
+            return new LogEvent(Objects.requireNonNull(bootstrap),
+                    Objects.requireNonNull(level),
+                    Objects.requireNonNull(msgSupplier), thrown, null);
+        }
+        static LogEvent valueOf(BootstrapLogger bootstrap, Level level,
+                                Supplier<String> msgSupplier) {
+            return new LogEvent(Objects.requireNonNull(bootstrap),
+                                Objects.requireNonNull(level),
+                                Objects.requireNonNull(msgSupplier), null, null);
+        }
+        static void log(LogEvent log, Logger logger) {
+            final SecurityManager sm = System.getSecurityManager();
+            // not sure we can actually use lambda here. We may need to create
+            // an anonymous class. Although if we reach here, then it means
+            // the VM is booted.
+            if (sm == null || log.acc == null) {
+                BootstrapExecutors.submit(() -> log.log(logger));
+            } else {
+                BootstrapExecutors.submit(() ->
+                    AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
+                        log.log(logger); return null;
+                    }, log.acc));
+            }
+        }
+
+        // non default methods from PlatformLogger.Bridge interface
+        static LogEvent valueOf(BootstrapLogger bootstrap,
+                                PlatformLogger.Level level, String msg) {
+            return new LogEvent(Objects.requireNonNull(bootstrap),
+                                Objects.requireNonNull(level), null, null, null,
+                                msg, null, null);
+        }
+        static LogEvent valueOf(BootstrapLogger bootstrap, PlatformLogger.Level level,
+                                String msg, Throwable thrown) {
+            return new LogEvent(Objects.requireNonNull(bootstrap),
+                    Objects.requireNonNull(level), null, null, null, msg, thrown, null);
+        }
+        static LogEvent valueOf(BootstrapLogger bootstrap, PlatformLogger.Level level,
+                                String msg, Object[] params) {
+            return new LogEvent(Objects.requireNonNull(bootstrap),
+                    Objects.requireNonNull(level), null, null, null, msg, null, params);
+        }
+        static LogEvent valueOf(BootstrapLogger bootstrap, PlatformLogger.Level level,
+                                Supplier<String> msgSupplier) {
+            return new LogEvent(Objects.requireNonNull(bootstrap),
+                    Objects.requireNonNull(level), null, null, msgSupplier, null, null);
+        }
+        static LogEvent vaueOf(BootstrapLogger bootstrap, PlatformLogger.Level level,
+                               Supplier<String> msgSupplier,
+                               Throwable thrown) {
+            return new LogEvent(Objects.requireNonNull(bootstrap),
+                                Objects.requireNonNull(level), null, null,
+                                msgSupplier, thrown, null);
+        }
+        static LogEvent valueOf(BootstrapLogger bootstrap, PlatformLogger.Level level,
+                                String sourceClass, String sourceMethod,
+                                ResourceBundle bundle, String msg, Object[] params) {
+            return new LogEvent(Objects.requireNonNull(bootstrap),
+                                Objects.requireNonNull(level), sourceClass,
+                                sourceMethod, bundle, msg, null, params);
+        }
+        static LogEvent valueOf(BootstrapLogger bootstrap, PlatformLogger.Level level,
+                                String sourceClass, String sourceMethod,
+                                ResourceBundle bundle, String msg, Throwable thrown) {
+            return new LogEvent(Objects.requireNonNull(bootstrap),
+                                Objects.requireNonNull(level), sourceClass,
+                                sourceMethod, bundle, msg, thrown, null);
+        }
+        static LogEvent valueOf(BootstrapLogger bootstrap, PlatformLogger.Level level,
+                                String sourceClass, String sourceMethod,
+                                Supplier<String> msgSupplier, Throwable thrown) {
+            return new LogEvent(Objects.requireNonNull(bootstrap),
+                                Objects.requireNonNull(level), sourceClass,
+                                sourceMethod, msgSupplier, thrown, null);
+        }
+        static void log(LogEvent log, PlatformLogger.Bridge logger) {
+            final SecurityManager sm = System.getSecurityManager();
+            if (sm == null || log.acc == null) {
+                log.log(logger);
+            } else {
+                // not sure we can actually use lambda here. We may need to create
+                // an anonymous class. Although if we reach here, then it means
+                // the VM is booted.
+                AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
+                    log.log(logger); return null;
+                }, log.acc);
+            }
+        }
+
+        static void log(LogEvent event) {
+            event.bootstrap.flush(event);
+        }
+
+    }
+
+    // Push a log event at the end of the pending LogEvent queue.
+    void push(LogEvent log) {
+        BootstrapExecutors.enqueue(log);
+        // if the queue has been flushed just before we entered
+        // the synchronized block we need to flush it again.
+        checkBootstrapping();
+    }
+
+    // Flushes the queue of pending LogEvents to the logger.
+    void flush(LogEvent event) {
+        assert event.bootstrap == this;
+        if (event.platformLevel != null) {
+            PlatformLogger.Bridge concrete = holder.getConcretePlatformLogger(this);
+            LogEvent.log(event, concrete);
+        } else {
+            Logger concrete = holder.getConcreteLogger(this);
+            LogEvent.log(event, concrete);
+        }
+    }
+
+    /**
+     * The name of this logger. This is the name of the actual logger for which
+     * this logger acts as a temporary proxy.
+     * @return The logger name.
+     */
+    @Override
+    public String getName() {
+        return holder.name;
+    }
+
+    /**
+     * Check whether the VM is still bootstrapping, and if not, arranges
+     * for this logger's holder to create the real logger and flush the
+     * pending event queue.
+     * @return true if the VM is still bootstrapping.
+     */
+    boolean checkBootstrapping() {
+        if (isBooted()) {
+            BootstrapExecutors.flush();
+            return false;
+        }
+        return true;
+    }
+
+    // ----------------------------------
+    // Methods from Logger
+    // ----------------------------------
+
+    @Override
+    public boolean isLoggable(Level level) {
+        if (checkBootstrapping()) {
+            return level.getSeverity() >= Level.INFO.getSeverity();
+        } else {
+            final Logger spi = holder.wrapped();
+            return spi.isLoggable(level);
+        }
+    }
+
+    @Override
+    public void log(Level level, ResourceBundle bundle, String key, Throwable thrown) {
+        if (checkBootstrapping()) {
+            push(LogEvent.valueOf(this, level, bundle, key, thrown));
+        } else {
+            final Logger spi = holder.wrapped();
+            spi.log(level, bundle, key, thrown);
+        }
+    }
+
+    @Override
+    public void log(Level level, ResourceBundle bundle, String format, Object... params) {
+        if (checkBootstrapping()) {
+            push(LogEvent.valueOf(this, level, bundle, format, params));
+        } else {
+            final Logger spi = holder.wrapped();
+            spi.log(level, bundle, format, params);
+        }
+    }
+
+    @Override
+    public void log(Level level, String msg, Throwable thrown) {
+        if (checkBootstrapping()) {
+            push(LogEvent.valueOf(this, level, null, msg, thrown));
+        } else {
+            final Logger spi = holder.wrapped();
+            spi.log(level, msg, thrown);
+        }
+    }
+
+    @Override
+    public void log(Level level, String format, Object... params) {
+        if (checkBootstrapping()) {
+            push(LogEvent.valueOf(this, level, null, format, params));
+        } else {
+            final Logger spi = holder.wrapped();
+            spi.log(level, format, params);
+        }
+    }
+
+    @Override
+    public void log(Level level, Supplier<String> msgSupplier) {
+        if (checkBootstrapping()) {
+            push(LogEvent.valueOf(this, level, msgSupplier));
+        } else {
+            final Logger spi = holder.wrapped();
+            spi.log(level, msgSupplier);
+        }
+    }
+
+    @Override
+    public void log(Level level, Object obj) {
+        if (checkBootstrapping()) {
+            Logger.super.log(level, obj);
+        } else {
+            final Logger spi = holder.wrapped();
+            spi.log(level, obj);
+        }
+    }
+
+    @Override
+    public void log(Level level, String msg) {
+        if (checkBootstrapping()) {
+            push(LogEvent.valueOf(this, level, null, msg, (Object[])null));
+        } else {
+            final Logger spi = holder.wrapped();
+            spi.log(level, msg);
+        }
+    }
+
+    @Override
+    public void log(Level level, Supplier<String> msgSupplier, Throwable thrown) {
+        if (checkBootstrapping()) {
+            push(LogEvent.valueOf(this, level, msgSupplier, thrown));
+        } else {
+            final Logger spi = holder.wrapped();
+            spi.log(level, msgSupplier, thrown);
+        }
+    }
+
+    // ----------------------------------
+    // Methods from PlatformLogger.Bridge
+    // ----------------------------------
+
+    @Override
+    public boolean isLoggable(PlatformLogger.Level level) {
+        if (checkBootstrapping()) {
+            return level.intValue() >= PlatformLogger.Level.INFO.intValue();
+        } else {
+            final PlatformLogger.Bridge spi = holder.platform();
+            return spi.isLoggable(level);
+        }
+    }
+
+    @Override
+    public boolean isEnabled() {
+        if (checkBootstrapping()) {
+            return true;
+        } else {
+            final PlatformLogger.Bridge spi = holder.platform();
+            return spi.isEnabled();
+        }
+    }
+
+    @Override
+    public void log(PlatformLogger.Level level, String msg) {
+        if (checkBootstrapping()) {
+            push(LogEvent.valueOf(this, level, msg));
+        } else {
+            final PlatformLogger.Bridge spi = holder.platform();
+            spi.log(level, msg);
+        }
+    }
+
+    @Override
+    public void log(PlatformLogger.Level level, String msg, Throwable thrown) {
+        if (checkBootstrapping()) {
+            push(LogEvent.valueOf(this, level, msg, thrown));
+        } else {
+            final PlatformLogger.Bridge spi = holder.platform();
+            spi.log(level, msg, thrown);
+        }
+    }
+
+    @Override
+    public void log(PlatformLogger.Level level, String msg, Object... params) {
+        if (checkBootstrapping()) {
+            push(LogEvent.valueOf(this, level, msg, params));
+        } else {
+            final PlatformLogger.Bridge spi = holder.platform();
+            spi.log(level, msg, params);
+        }
+    }
+
+    @Override
+    public void log(PlatformLogger.Level level, Supplier<String> msgSupplier) {
+        if (checkBootstrapping()) {
+            push(LogEvent.valueOf(this, level, msgSupplier));
+        } else {
+            final PlatformLogger.Bridge spi = holder.platform();
+            spi.log(level, msgSupplier);
+        }
+    }
+
+    @Override
+    public void log(PlatformLogger.Level level, Throwable thrown,
+            Supplier<String> msgSupplier) {
+        if (checkBootstrapping()) {
+            push(LogEvent.vaueOf(this, level, msgSupplier, thrown));
+        } else {
+            final PlatformLogger.Bridge spi = holder.platform();
+            spi.log(level, thrown, msgSupplier);
+        }
+    }
+
+    @Override
+    public void logp(PlatformLogger.Level level, String sourceClass,
+            String sourceMethod, String msg) {
+        if (checkBootstrapping()) {
+            push(LogEvent.valueOf(this, level, sourceClass, sourceMethod, null,
+                    msg, (Object[])null));
+        } else {
+            final PlatformLogger.Bridge spi = holder.platform();
+            spi.logp(level, sourceClass, sourceMethod, msg);
+        }
+    }
+
+    @Override
+    public void logp(PlatformLogger.Level level, String sourceClass,
+            String sourceMethod, Supplier<String> msgSupplier) {
+        if (checkBootstrapping()) {
+            push(LogEvent.valueOf(this, level, sourceClass, sourceMethod, msgSupplier, null));
+        } else {
+            final PlatformLogger.Bridge spi = holder.platform();
+            spi.logp(level, sourceClass, sourceMethod, msgSupplier);
+        }
+    }
+
+    @Override
+    public void logp(PlatformLogger.Level level, String sourceClass,
+            String sourceMethod, String msg, Object... params) {
+        if (checkBootstrapping()) {
+            push(LogEvent.valueOf(this, level, sourceClass, sourceMethod, null, msg, params));
+        } else {
+            final PlatformLogger.Bridge spi = holder.platform();
+            spi.logp(level, sourceClass, sourceMethod, msg, params);
+        }
+    }
+
+    @Override
+    public void logp(PlatformLogger.Level level, String sourceClass,
+            String sourceMethod, String msg, Throwable thrown) {
+        if (checkBootstrapping()) {
+            push(LogEvent.valueOf(this, level, sourceClass, sourceMethod, null, msg, thrown));
+        } else {
+            final PlatformLogger.Bridge spi = holder.platform();
+            spi.logp(level, sourceClass, sourceMethod, msg, thrown);
+        }
+    }
+
+    @Override
+    public void logp(PlatformLogger.Level level, String sourceClass,
+            String sourceMethod, Throwable thrown, Supplier<String> msgSupplier) {
+        if (checkBootstrapping()) {
+            push(LogEvent.valueOf(this, level, sourceClass, sourceMethod, msgSupplier, thrown));
+        } else {
+            final PlatformLogger.Bridge spi = holder.platform();
+            spi.logp(level, sourceClass, sourceMethod, thrown, msgSupplier);
+        }
+    }
+
+    @Override
+    public void logrb(PlatformLogger.Level level, String sourceClass,
+            String sourceMethod, ResourceBundle bundle, String msg, Object... params) {
+        if (checkBootstrapping()) {
+            push(LogEvent.valueOf(this, level, sourceClass, sourceMethod, bundle, msg, params));
+        } else {
+            final PlatformLogger.Bridge spi = holder.platform();
+            spi.logrb(level, sourceClass, sourceMethod, bundle, msg, params);
+        }
+    }
+
+    @Override
+    public void logrb(PlatformLogger.Level level, String sourceClass,
+            String sourceMethod, ResourceBundle bundle, String msg, Throwable thrown) {
+        if (checkBootstrapping()) {
+            push(LogEvent.valueOf(this, level, sourceClass, sourceMethod, bundle, msg, thrown));
+        } else {
+            final PlatformLogger.Bridge spi = holder.platform();
+            spi.logrb(level, sourceClass, sourceMethod, bundle, msg, thrown);
+        }
+    }
+
+    @Override
+    public void logrb(PlatformLogger.Level level, ResourceBundle bundle,
+            String msg, Object... params) {
+        if (checkBootstrapping()) {
+            push(LogEvent.valueOf(this, level, null, null, bundle, msg, params));
+        } else {
+            final PlatformLogger.Bridge spi = holder.platform();
+            spi.logrb(level, bundle, msg, params);
+        }
+    }
+
+    @Override
+    public void logrb(PlatformLogger.Level level, ResourceBundle bundle, String msg, Throwable thrown) {
+        if (checkBootstrapping()) {
+            push(LogEvent.valueOf(this, level, null, null, bundle, msg, thrown));
+        } else {
+            final PlatformLogger.Bridge spi = holder.platform();
+            spi.logrb(level, bundle, msg, thrown);
+        }
+    }
+
+    @Override
+    public LoggerConfiguration getLoggerConfiguration() {
+        if (checkBootstrapping()) {
+            // This practically means that PlatformLogger.setLevel()
+            // calls will be ignored if the VM is still bootstrapping. We could
+            // attempt to fix that but is it worth it?
+            return PlatformLogger.ConfigurableBridge.super.getLoggerConfiguration();
+        } else {
+            final PlatformLogger.Bridge spi = holder.platform();
+            return PlatformLogger.ConfigurableBridge.getLoggerConfiguration(spi);
+        }
+    }
+
+    // This BooleanSupplier is a hook for tests - so that we can simulate
+    // what would happen before the VM is booted.
+    private static volatile BooleanSupplier isBooted;
+    public static boolean isBooted() {
+        if (isBooted != null) return isBooted.getAsBoolean();
+        else return VM.isBooted();
+    }
+
+    // A bit of black magic. We try to find out the nature of the logging
+    // backend without actually loading it.
+    private static enum LoggingBackend {
+        // There is no LoggerFinder and JUL is not present
+        NONE(true),
+
+        // There is no LoggerFinder, but we have found a
+        // JdkLoggerFinder installed (which means JUL is present),
+        // and we haven't found any custom configuration for JUL.
+        // Until LogManager is initialized we can use a simple console
+        // logger.
+        JUL_DEFAULT(false),
+
+        // Same as above, except that we have found a custom configuration
+        // for JUL. We cannot use the simple console logger in this case.
+        JUL_WITH_CONFIG(true),
+
+        // We have found a custom LoggerFinder.
+        CUSTOM(true);
+
+        final boolean useLoggerFinder;
+        private LoggingBackend(boolean useLoggerFinder) {
+            this.useLoggerFinder = useLoggerFinder;
+        }
+    };
+
+    // The purpose of this class is to delay the initialization of
+    // the detectedBackend field until it is actually read.
+    // We do not want this field to get initialized if VM.isBooted() is false.
+    private static final class DetectBackend {
+        static final LoggingBackend detectedBackend;
+        static {
+            detectedBackend = AccessController.doPrivileged(new PrivilegedAction<LoggingBackend>() {
+                    @Override
+                    public LoggingBackend run() {
+                        final Iterator<LoggerFinder> iterator =
+                            ServiceLoader.load(LoggerFinder.class, ClassLoader.getSystemClassLoader())
+                            .iterator();
+                        if (iterator.hasNext()) {
+                            return LoggingBackend.CUSTOM; // Custom Logger Provider is registered
+                        }
+                        // No custom logger provider: we will be using the default
+                        // backend.
+                        final Iterator<DefaultLoggerFinder> iterator2 =
+                            ServiceLoader.loadInstalled(DefaultLoggerFinder.class)
+                            .iterator();
+                        if (iterator2.hasNext()) {
+                            // LoggingProviderImpl is registered. The default
+                            // implementation is java.util.logging
+                            String cname = System.getProperty("java.util.logging.config.class");
+                            String fname = System.getProperty("java.util.logging.config.file");
+                            return (cname != null || fname != null)
+                                ? LoggingBackend.JUL_WITH_CONFIG
+                                : LoggingBackend.JUL_DEFAULT;
+                        } else {
+                            // SimpleLogger is used
+                            return LoggingBackend.NONE;
+                        }
+                    }
+                });
+
+        }
+    }
+
+    // We will use temporary SimpleConsoleLoggers if
+    // the logging backend is JUL, there is no custom config,
+    // and the LogManager has not been initialized yet.
+    private static  boolean useTemporaryLoggers() {
+        // being paranoid: this should already have been checked
+        if (!isBooted()) return true;
+        return DetectBackend.detectedBackend == LoggingBackend.JUL_DEFAULT
+                && !logManagerConfigured;
+    }
+
+    // We will use lazy loggers if:
+    //    - the VM is not yet booted
+    //    - the logging backend is a custom backend
+    //    - the logging backend is JUL, there is no custom config,
+    //      and the LogManager has not been initialized yet.
+    public static synchronized boolean useLazyLoggers() {
+        return !BootstrapLogger.isBooted()
+                || DetectBackend.detectedBackend == LoggingBackend.CUSTOM
+                || useTemporaryLoggers();
+    }
+
+    // Called by LazyLoggerAccessor. This method will determine whether
+    // to create a BootstrapLogger (if the VM is not yet booted),
+    // a SimpleConsoleLogger (if JUL is the default backend and there
+    // is no custom JUL configuration and LogManager is not yet initialized),
+    // or a logger returned by the loaded LoggerFinder (all other cases).
+    static Logger getLogger(LazyLoggerAccessor accessor) {
+        if (!BootstrapLogger.isBooted()) {
+            return new BootstrapLogger(accessor);
+        } else {
+            boolean temporary = useTemporaryLoggers();
+            if (temporary) {
+                // JUL is the default backend, there is no custom configuration,
+                // LogManager has not been used.
+                synchronized(BootstrapLogger.class) {
+                    if (useTemporaryLoggers()) {
+                        return makeTemporaryLogger(accessor);
+                    }
+                }
+            }
+            // Already booted. Return the real logger.
+            return accessor.createLogger();
+        }
+    }
+
+
+    // If the backend is JUL, and there is no custom configuration, and
+    // nobody has attempted to call LogManager.getLogManager() yet, then
+    // we can temporarily substitute JUL Logger with SimpleConsoleLoggers,
+    // which avoids the cost of actually loading up the LogManager...
+    // The TemporaryLoggers class has the logic to create such temporary
+    // loggers, and to possibly replace them with real JUL loggers if
+    // someone calls LogManager.getLogManager().
+    static final class TemporaryLoggers implements
+            Function<LazyLoggerAccessor, SimpleConsoleLogger> {
+
+        // all accesses must be synchronized on the outer BootstrapLogger.class
+        final Map<LazyLoggerAccessor, SimpleConsoleLogger> temporaryLoggers =
+                new HashMap<>();
+
+        // all accesses must be synchronized on the outer BootstrapLogger.class
+        // The temporaryLoggers map will be cleared when LogManager is initialized.
+        boolean cleared;
+
+        @Override
+        // all accesses must be synchronized on the outer BootstrapLogger.class
+        public SimpleConsoleLogger apply(LazyLoggerAccessor t) {
+            if (cleared) throw new IllegalStateException("LoggerFinder already initialized");
+            return SimpleConsoleLogger.makeSimpleLogger(t.getLoggerName(), true);
+        }
+
+        // all accesses must be synchronized on the outer BootstrapLogger.class
+        SimpleConsoleLogger get(LazyLoggerAccessor a) {
+            if (cleared) throw new IllegalStateException("LoggerFinder already initialized");
+            return temporaryLoggers.computeIfAbsent(a, this);
+        }
+
+        // all accesses must be synchronized on the outer BootstrapLogger.class
+        Map<LazyLoggerAccessor, SimpleConsoleLogger> drainTemporaryLoggers() {
+            if (temporaryLoggers.isEmpty()) return null;
+            if (cleared) throw new IllegalStateException("LoggerFinder already initialized");
+            final Map<LazyLoggerAccessor, SimpleConsoleLogger> accessors = new HashMap<>(temporaryLoggers);
+            temporaryLoggers.clear();
+            cleared = true;
+            return accessors;
+        }
+
+        static void resetTemporaryLoggers(Map<LazyLoggerAccessor, SimpleConsoleLogger> accessors) {
+            // When the backend is JUL we want to force the creation of
+            // JUL loggers here: some tests are expecting that the
+            // PlatformLogger will create JUL loggers as soon as the
+            // LogManager is initialized.
+            //
+            // If the backend is not JUL then we can delay the re-creation
+            // of the wrapped logger until they are next accessed.
+            //
+            final LoggingBackend detectedBackend = DetectBackend.detectedBackend;
+            final boolean lazy = detectedBackend != LoggingBackend.JUL_DEFAULT
+                    && detectedBackend != LoggingBackend.JUL_WITH_CONFIG;
+            for (Map.Entry<LazyLoggerAccessor, SimpleConsoleLogger> a : accessors.entrySet()) {
+                a.getKey().release(a.getValue(), !lazy);
+            }
+        }
+
+        // all accesses must be synchronized on the outer BootstrapLogger.class
+        static final TemporaryLoggers INSTANCE = new TemporaryLoggers();
+    }
+
+    static synchronized Logger makeTemporaryLogger(LazyLoggerAccessor a) {
+        // accesses to TemporaryLoggers is synchronized on BootstrapLogger.class
+        return TemporaryLoggers.INSTANCE.get(a);
+    }
+
+    private static volatile boolean logManagerConfigured;
+
+    private static synchronized Map<LazyLoggerAccessor, SimpleConsoleLogger>
+         releaseTemporaryLoggers() {
+        // first check whether there's a chance that we have used
+        // temporary loggers; Will be false if logManagerConfigured is already
+        // true.
+        final boolean clearTemporaryLoggers = useTemporaryLoggers();
+
+        // then sets the flag that tells that the log manager is configured
+        logManagerConfigured = true;
+
+        // finally replace all temporary loggers by real JUL loggers
+        if (clearTemporaryLoggers) {
+            // accesses to TemporaryLoggers is synchronized on BootstrapLogger.class
+            return TemporaryLoggers.INSTANCE.drainTemporaryLoggers();
+        } else {
+            return null;
+        }
+    }
+
+    public static void redirectTemporaryLoggers() {
+        // This call is synchronized on BootstrapLogger.class.
+        final Map<LazyLoggerAccessor, SimpleConsoleLogger> accessors =
+                releaseTemporaryLoggers();
+
+        // We will now reset the logger accessors, triggering the
+        // (possibly lazy) replacement of any temporary logger by the
+        // real logger returned from the loaded LoggerFinder.
+        if (accessors != null) {
+            TemporaryLoggers.resetTemporaryLoggers(accessors);
+        }
+
+        BootstrapExecutors.flush();
+    }
+
+    // Hook for tests which need to wait until pending messages
+    // are processed.
+    static void awaitPendingTasks() {
+        BootstrapExecutors.awaitPendingTasks();
+    }
+    static boolean isAlive() {
+        return BootstrapExecutors.isAlive();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/jdk/internal/logger/DefaultLoggerFinder.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.internal.logger;
+
+import java.lang.ref.Reference;
+import java.lang.ref.WeakReference;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.Function;
+import java.lang.System.LoggerFinder;
+import java.lang.System.Logger;
+import java.lang.ref.ReferenceQueue;
+import java.util.Collection;
+import java.util.ResourceBundle;
+
+/**
+ * Internal Service Provider Interface (SPI) that makes it possible to use
+ * {@code java.util.logging} as backend when the {@link
+ * sun.util.logging.internal.LoggingProviderImpl
+ * sun.util.logging.internal.LoggingProviderImpl} is present.
+ * <p>
+ * The JDK default implementation of the {@link LoggerFinder} will
+ * attempt to locate and load an {@linkplain
+ * java.util.ServiceLoader#loadInstalled(java.lang.Class) installed}
+ * implementation of the {@code DefaultLoggerFinder}. If {@code java.util.logging}
+ * is present, this will usually resolve to an instance of {@link
+ * sun.util.logging.internal.LoggingProviderImpl sun.util.logging.internal.LoggingProviderImpl}.
+ * Otherwise, if no concrete service provider is declared for
+ * {@code DefaultLoggerFinder}, the default implementation provided by this class
+ * will be used.
+ * <p>
+ * When the {@link sun.util.logging.internal.LoggingProviderImpl
+ * sun.util.logging.internal.LoggingProviderImpl} is not present then the
+ * default implementation provided by this class is to use a simple logger
+ * that will log messages whose level is INFO and above to the console.
+ * These simple loggers are not configurable.
+ * <p>
+ * When configuration is needed, an application should either link with
+ * {@code java.util.logging} - and use the {@code java.util.logging} for
+ * configuration, or link with {@link LoggerFinder another implementation}
+ * of the {@link LoggerFinder}
+ * that provides the necessary configuration.
+ *
+ * @apiNote Programmers are not expected to call this class directly.
+ * Instead they should rely on the static methods defined by {@link
+ * java.lang.System java.lang.System} or {@link sun.util.logging.PlatformLogger
+ * sun.util.logging.PlatformLogger}.
+ *
+ * @see java.lang.System.LoggerFinder
+ * @see jdk.internal.logger
+ * @see sun.util.logging.internal
+ *
+ */
+public class DefaultLoggerFinder extends LoggerFinder {
+
+    static final RuntimePermission LOGGERFINDER_PERMISSION =
+                new RuntimePermission("loggerFinder");
+
+    /**
+     * Creates a new instance of DefaultLoggerFinder.
+     * @throws SecurityException if the calling code does not have the
+     * {@code RuntimePermission("loggerFinder")}
+     */
+    protected DefaultLoggerFinder() {
+        this(checkPermission());
+    }
+
+    private DefaultLoggerFinder(Void unused) {
+        // nothing to do.
+    }
+
+    private static Void checkPermission() {
+        final SecurityManager sm = System.getSecurityManager();
+        if (sm != null) {
+            sm.checkPermission(LOGGERFINDER_PERMISSION);
+        }
+        return null;
+    }
+
+    // SharedLoggers is a default cache of loggers used when JUL is not
+    // present - in that case we use instances of SimpleConsoleLogger which
+    // cannot be directly configure through public APIs.
+    //
+    // We can therefore afford to simply maintain two domains - one for the
+    // system, and one for the application.
+    //
+    static final class SharedLoggers {
+        private final Map<String, Reference<Logger>> loggers =
+                new HashMap<>();
+        private final ReferenceQueue<Logger> queue = new ReferenceQueue<>();
+
+        synchronized Logger get(Function<String, Logger> loggerSupplier, final String name) {
+            Reference<? extends Logger> ref = loggers.get(name);
+            Logger w = ref == null ? null :  ref.get();
+            if (w == null) {
+                w = loggerSupplier.apply(name);
+                loggers.put(name, new WeakReference<>(w, queue));
+            }
+
+            // Remove stale mapping...
+            Collection<Reference<Logger>> values = null;
+            while ((ref = queue.poll()) != null) {
+                if (values == null) values = loggers.values();
+                values.remove(ref);
+            }
+            return w;
+        }
+
+
+        final static SharedLoggers system = new SharedLoggers();
+        final static SharedLoggers application = new SharedLoggers();
+    }
+
+    @Override
+    public final Logger getLogger(String name,  /* Module */ Class<?> caller) {
+        checkPermission();
+        return demandLoggerFor(name, caller);
+    }
+
+    @Override
+    public final Logger getLocalizedLogger(String name, ResourceBundle bundle,
+                                           /* Module */  Class<?> caller) {
+        return super.getLocalizedLogger(name, bundle, caller);
+    }
+
+
+
+    /**
+     * Returns a {@link Logger logger} suitable for the caller usage.
+     *
+     * @implSpec The default implementation for this method is to return a
+     *    simple logger that will print all messages of INFO level and above
+     *    to the console. That simple logger is not configurable.
+     *
+     * @param name The name of the logger.
+     * @param caller The class on behalf of which the logger is created.
+     * @return A {@link Logger logger} suitable for the application usage.
+     * @throws SecurityException if the calling code does not have the
+     * {@code RuntimePermission("loggerFinder")}.
+     */
+    protected Logger demandLoggerFor(String name, /* Module */ Class<?> caller) {
+        checkPermission();
+        if (caller.getClassLoader() == null) {
+            return SharedLoggers.system.get(SimpleConsoleLogger::makeSimpleLogger, name);
+        } else {
+            return SharedLoggers.application.get(SimpleConsoleLogger::makeSimpleLogger, name);
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/jdk/internal/logger/LazyLoggers.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,446 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.internal.logger;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.function.BiFunction;
+import java.lang.System.LoggerFinder;
+import java.lang.System.Logger;
+import java.lang.ref.WeakReference;
+import java.util.Objects;
+import sun.misc.VM;
+import sun.util.logging.PlatformLogger;
+
+/**
+ * This class is a factory for Lazy Loggers; only system loggers can be
+ * Lazy Loggers.
+ */
+public final class LazyLoggers {
+
+    static final RuntimePermission LOGGERFINDER_PERMISSION =
+                new RuntimePermission("loggerFinder");
+
+    private LazyLoggers() {
+        throw new InternalError();
+    }
+
+    /**
+     * This class is used to hold the factories that a Lazy Logger will use
+     * to create (or map) its wrapped logger.
+     * @param <L> {@link Logger} or a subclass of {@link Logger}.
+     */
+    private static final class LazyLoggerFactories<L extends Logger> {
+
+        /**
+         * A factory method to create an SPI logger.
+         * Usually, this will be something like LazyLoggers::getSystemLogger.
+         */
+        final BiFunction<String, Class<?>, L> loggerSupplier;
+
+
+        public LazyLoggerFactories(BiFunction<String, Class<?>, L> loggerSupplier) {
+            this(Objects.requireNonNull(loggerSupplier),
+                 (Void)null);
+        }
+
+        private LazyLoggerFactories(BiFunction<String, Class<?>, L> loggerSupplier,
+                          Void unused) {
+            this.loggerSupplier = loggerSupplier;
+        }
+
+    }
+
+    static interface LoggerAccessor {
+        /**
+         * The logger name.
+         * @return The name of the logger that is / will be lazily created.
+         */
+        public String getLoggerName();
+
+        /**
+         * Returns the wrapped logger object.
+         * @return the wrapped logger object.
+         */
+        public Logger wrapped();
+
+        /**
+         * A PlatformLogger.Bridge view of the wrapped logger object.
+         * @return A PlatformLogger.Bridge view of the wrapped logger object.
+         */
+        public PlatformLogger.Bridge platform();
+    }
+
+    /**
+     * The LazyLoggerAccessor class holds all the logic that delays the creation
+     * of the SPI logger until such a time that the VM is booted and the logger
+     * is actually used for logging.
+     *
+     * This class uses the services of the BootstrapLogger class to instantiate
+     * temporary loggers if appropriate.
+     */
+    static final class LazyLoggerAccessor implements LoggerAccessor {
+
+        // The factories that will be used to create the logger lazyly
+        final LazyLoggerFactories<? extends Logger> factories;
+
+        // We need to pass the actual caller when creating the logger.
+        private final WeakReference<Class<?>> callerRef;
+
+        // The name of the logger that will be created lazyly
+        final String name;
+        // The plain logger SPI object - null until it is accessed for the
+        // first time.
+        private volatile Logger w;
+        // A PlatformLogger.Bridge view of w.
+        private volatile PlatformLogger.Bridge p;
+
+
+        private LazyLoggerAccessor(String name,
+                                   LazyLoggerFactories<? extends Logger> factories,
+                                   Class<?> caller) {
+            this(Objects.requireNonNull(name), Objects.requireNonNull(factories),
+                    Objects.requireNonNull(caller), null);
+        }
+
+        private LazyLoggerAccessor(String name,
+                                   LazyLoggerFactories<? extends Logger> factories,
+                                   Class<?> caller, Void unused) {
+            this.name = name;
+            this.factories = factories;
+            this.callerRef = new WeakReference<Class<?>>(caller);
+        }
+
+        /**
+         * The logger name.
+         * @return The name of the logger that is / will be lazily created.
+         */
+        @Override
+        public String getLoggerName() {
+            return name;
+        }
+
+        // must be called in synchronized block
+        // set wrapped logger if not set
+        private void setWrappedIfNotSet(Logger wrapped) {
+            if (w == null) {
+                w = wrapped;
+            }
+        }
+
+        /**
+         * Returns the logger SPI object, creating it if 'w' is still null.
+         * @return the logger SPI object.
+         */
+        public Logger wrapped() {
+            Logger wrapped = w;
+            if (wrapped != null) return wrapped;
+            // Wrapped logger not created yet: create it.
+            // BootstrapLogger has the logic to decide whether to invoke the
+            // SPI or use a temporary (BootstrapLogger or SimpleConsoleLogger)
+            // logger.
+            wrapped = BootstrapLogger.getLogger(this);
+            synchronized(this) {
+                // if w has already been in between, simply drop 'wrapped'.
+                setWrappedIfNotSet(wrapped);
+                return w;
+            }
+        }
+
+        /**
+         * A PlatformLogger.Bridge view of the wrapped logger.
+         * @return A PlatformLogger.Bridge view of the wrapped logger.
+         */
+        public PlatformLogger.Bridge platform() {
+            // We can afford to return the platform view of the previous
+            // logger - if that view is not null.
+            // Because that view will either be the BootstrapLogger, which
+            // will redirect to the new wrapper properly, or the temporary
+            // logger - which in effect is equivalent to logging something
+            // just before the application initialized LogManager.
+            PlatformLogger.Bridge platform = p;
+            if (platform != null) return platform;
+            synchronized (this) {
+                if (w != null) {
+                    if (p == null) p = PlatformLogger.Bridge.convert(w);
+                    return p;
+                }
+            }
+            // If we reach here it means that the wrapped logger may not
+            // have been created yet: attempt to create it.
+            // BootstrapLogger has the logic to decide whether to invoke the
+            // SPI or use a temporary (BootstrapLogger or SimpleConsoleLogger)
+            // logger.
+            final Logger wrapped = BootstrapLogger.getLogger(this);
+            synchronized(this) {
+                // if w has already been set, simply drop 'wrapped'.
+                setWrappedIfNotSet(wrapped);
+                if (p == null) p = PlatformLogger.Bridge.convert(w);
+                return p;
+            }
+        }
+
+        /**
+         * Makes this accessor release a temporary logger.
+         * This method is called
+         * by BootstrapLogger when JUL is the default backend and LogManager
+         * is initialized, in order to replace temporary SimpleConsoleLoggers by
+         * real JUL loggers. See BootstrapLogger for more details.
+         * If {@code replace} is {@code true}, then this method will force
+         * the accessor to eagerly recreate its wrapped logger.
+         * Note: passing {@code replace=false} is no guarantee that the
+         * method will not actually replace the released logger.
+         * @param temporary The temporary logger too be released.
+         * @param replace   Whether the released logger should be eagerly
+         *                  replaced.
+         */
+        void release(SimpleConsoleLogger temporary, boolean replace) {
+            PlatformLogger.ConfigurableBridge.LoggerConfiguration conf =
+                PlatformLogger.ConfigurableBridge.getLoggerConfiguration(temporary);
+            PlatformLogger.Level level = conf != null
+                    ? conf.getPlatformLevel()
+                    : null;
+            synchronized (this) {
+                if (this.w == temporary) {
+                    this.w = null; this.p = null;
+                }
+            }
+            PlatformLogger.Bridge platform =  replace || level != null
+                    ? this.platform() : null;
+
+            if (level != null) {
+                conf = (platform != null && platform != temporary)
+                        ? PlatformLogger.ConfigurableBridge.getLoggerConfiguration(platform)
+                        : null;
+                if (conf != null) conf.setPlatformLevel(level);
+            }
+        }
+
+        /**
+         * Replace 'w' by the real SPI logger and flush the log messages pending
+         * in the temporary 'bootstrap' Logger. Called by BootstrapLogger when
+         * this accessor's bootstrap logger is accessed and BootstrapLogger
+         * notices that the VM is no longer booting.
+         * @param bootstrap This accessor's bootstrap logger (usually this is 'w').
+         */
+        Logger getConcreteLogger(BootstrapLogger bootstrap) {
+            assert VM.isBooted();
+            synchronized(this) {
+                // another thread may have already invoked flush()
+                if (this.w == bootstrap) {
+                    this.w = null; this.p = null;
+                }
+            }
+            return this.wrapped();
+        }
+
+        PlatformLogger.Bridge getConcretePlatformLogger(BootstrapLogger bootstrap) {
+            assert VM.isBooted();
+            synchronized(this) {
+                // another thread may have already invoked flush()
+                if (this.w == bootstrap) {
+                    this.w = null; this.p = null;
+                }
+            }
+            return this.platform();
+        }
+
+        // Creates the wrapped logger by invoking the SPI.
+        Logger createLogger() {
+            final Class<?> caller = callerRef.get();
+            if (caller == null) {
+                throw new IllegalStateException("The class for which this logger"
+                        + " was created has been garbage collected");
+            }
+            return this.factories.loggerSupplier.apply(name, caller);
+        }
+
+        /**
+         * Creates a new lazy logger accessor for the named logger. The given
+         * factories will be use when it becomes necessary to actually create
+         * the logger.
+         * @param <T> An interface that extends {@link Logger}.
+         * @param name The logger name.
+         * @param factories The factories that should be used to create the
+         *                  wrapped logger.
+         * @return A new LazyLoggerAccessor.
+         */
+        public static LazyLoggerAccessor makeAccessor(String name,
+                LazyLoggerFactories<? extends Logger> factories, Class<?> caller) {
+                return new LazyLoggerAccessor(name, factories, caller);
+        }
+
+    }
+
+    /**
+     * An implementation of {@link Logger} that redirects all calls to a wrapped
+     * instance of {@code Logger}.
+     */
+    private static class LazyLoggerWrapper
+        extends AbstractLoggerWrapper<Logger> {
+
+        final LoggerAccessor loggerAccessor;
+
+        public LazyLoggerWrapper(LazyLoggerAccessor loggerSinkSupplier) {
+            this(Objects.requireNonNull(loggerSinkSupplier), (Void)null);
+        }
+
+        private LazyLoggerWrapper(LazyLoggerAccessor loggerSinkSupplier,
+                Void unused) {
+            this.loggerAccessor = loggerSinkSupplier;
+        }
+
+        @Override
+        final Logger wrapped() {
+            return loggerAccessor.wrapped();
+        }
+
+        @Override
+        PlatformLogger.Bridge platformProxy() {
+            return loggerAccessor.platform();
+        }
+
+    }
+
+    // Do not expose this outside of this package.
+    private static volatile LoggerFinder provider = null;
+    private static LoggerFinder accessLoggerFinder() {
+        if (provider == null) {
+            // no need to lock: it doesn't matter if we call
+            // getLoggerFinder() twice - since LoggerFinder already caches
+            // the result.
+            // This is just an optimization to avoid the cost of calling
+            // doPrivileged every time.
+            final SecurityManager sm = System.getSecurityManager();
+            provider = sm == null ? LoggerFinder.getLoggerFinder() :
+                AccessController.doPrivileged(
+                        (PrivilegedAction<LoggerFinder>)LoggerFinder::getLoggerFinder);
+        }
+        return provider;
+    }
+
+    // Avoid using lambda here as lazy loggers could be created early
+    // in the bootstrap sequence...
+    private static final BiFunction<String, Class<?>, Logger> loggerSupplier =
+           new BiFunction<>() {
+        @Override
+        public Logger apply(String name, Class<?> caller) {
+            return LazyLoggers.getLoggerFromFinder(name, caller);
+        }
+    };
+
+    private static final LazyLoggerFactories<Logger> factories =
+           new LazyLoggerFactories<>(loggerSupplier);
+
+
+
+    // A concrete implementation of Logger that delegates to a  System.Logger,
+    // but only creates the System.Logger instance lazily when it's used for
+    // the first time.
+    // The JdkLazyLogger uses a LazyLoggerAccessor objects, which relies
+    // on the logic embedded in BootstrapLogger to avoid loading the concrete
+    // logger provider until the VM has finished booting.
+    //
+    private static final class JdkLazyLogger extends LazyLoggerWrapper {
+        JdkLazyLogger(String name, Class<?> caller) {
+            this(LazyLoggerAccessor.makeAccessor(name, factories, caller),
+                 (Void)null);
+        }
+        private JdkLazyLogger(LazyLoggerAccessor holder, Void unused) {
+            super(holder);
+        }
+    }
+
+    /**
+     * Gets a logger from the LoggerFinder. Creates the actual concrete
+     * logger.
+     * @param name    name of the logger
+     * @param caller  class on behalf of which the logger is created
+     * @return  The logger returned by the LoggerFinder.
+     */
+    static Logger getLoggerFromFinder(String name, Class<?> caller) {
+        final SecurityManager sm = System.getSecurityManager();
+        if (sm == null) {
+            return accessLoggerFinder().getLogger(name, caller);
+        } else {
+            return AccessController.doPrivileged((PrivilegedAction<Logger>)
+                    () -> {return accessLoggerFinder().getLogger(name, caller);},
+                    null, LOGGERFINDER_PERMISSION);
+        }
+    }
+
+    /**
+     * Returns a (possibly lazy) Logger for the caller.
+     *
+     * @param name the logger name
+     * @param caller The class on behalf of which the logger is created.
+     *               If the caller is not loaded from the Boot ClassLoader,
+     *               the LoggerFinder is accessed and the logger returned
+     *               by {@link LoggerFinder#getLogger(java.lang.String, java.lang.Class)}
+     *               is returned to the caller directly.
+     *               Otherwise, the logger returned by
+     *               {@link #getLazyLogger(java.lang.String, java.lang.Class)}
+     *               is returned to the caller.
+     *
+     * @return  a (possibly lazy) Logger instance.
+     */
+    public static final Logger getLogger(String name, Class<?> caller) {
+        if (caller.getClassLoader() == null) {
+            return getLazyLogger(name, caller);
+        } else {
+            return getLoggerFromFinder(name, caller);
+        }
+    }
+
+    /**
+     * Returns a (possibly lazy) Logger suitable for system classes.
+     * Whether the returned logger is lazy or not depend on the result
+     * returned by {@link BootstrapLogger#useLazyLoggers()}.
+     *
+     * @param name the logger name
+     * @param caller the class on behalf of which the logger is created.
+     * @return  a (possibly lazy) Logger instance.
+     */
+    public static final Logger getLazyLogger(String name, Class<?> caller) {
+
+        // BootstrapLogger has the logic to determine whether a LazyLogger
+        // should be used. Usually, it is worth it only if:
+        //   - the VM is not yet booted
+        //   - or, the backend is JUL and there is no configuration
+        //   - or, the backend is a custom backend, as we don't know what
+        //     that is going to load...
+        // So if for instance the VM is booted and we use JUL with a custom
+        // configuration, we're not going to delay the creation of loggers...
+        final boolean useLazyLogger = BootstrapLogger.useLazyLoggers();
+        if (useLazyLogger) {
+            return new JdkLazyLogger(name, caller);
+        } else {
+            // Directly invoke the LoggerFinder.
+            return getLoggerFromFinder(name, caller);
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/jdk/internal/logger/LocalizedLoggerWrapper.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+
+package jdk.internal.logger;
+
+import java.util.ResourceBundle;
+import java.util.function.Supplier;
+import java.lang.System.Logger;
+import java.lang.System.Logger.Level;
+
+/**
+ * This implementation of {@link Logger} redirects all logging method
+ * calls to calls to {@code log(Level, String,  ResourceBundle, ...)}
+ * methods, passing the Logger's ResourceBundle as parameter.
+ * So for instance a call to {@link Logger#log(Level, String)
+ * log(Level.INFO, msg)} will be redirected
+ * to a call to {@link #log(java.lang.System.Logger.Level,
+ * java.util.ResourceBundle, java.lang.String, java.lang.Object...)
+ * this.log(Level.INFO, this.bundle, msg, (Object[]) null)}.
+ * <p>
+ * Note that methods that take a {@link Supplier Supplier&lt;String&gt;}
+ * or an Object are not redirected. It is assumed that a string returned
+ * by a {@code Supplier<String>} is already localized, or cannot be localized.
+ *
+ * @param <L> Type of the wrapped Logger: {@code Logger} or an
+ *        extension of the {@code Logger} interface.
+ */
+public class LocalizedLoggerWrapper<L extends Logger> extends LoggerWrapper<L> {
+
+    private final ResourceBundle bundle;
+
+    public LocalizedLoggerWrapper(L wrapped, ResourceBundle bundle) {
+        super(wrapped);
+        this.bundle = bundle;
+    }
+
+    public final ResourceBundle getBundle() {
+        return bundle;
+    }
+
+    // We assume that messages returned by Supplier<String> and Object are
+    // either already localized or not localizable. To be evaluated.
+
+    // -----------------------------------------------------------------
+    // Generic methods taking a Level as parameter
+    // -----------------------------------------------------------------
+
+    @Override
+    public final void log(Level level, String msg) {
+        log(level, bundle, msg, (Object[]) null);
+    }
+
+    @Override
+    public final void log(Level level,
+                   String msg, Throwable thrown) {
+        log(level, bundle, msg, thrown);
+    }
+
+    @Override
+    public final void log(Level level,
+                    String format, Object... params) {
+        log(level, bundle, format, params);
+    }
+
+    @Override
+    public final void log(Level level, Object obj) {
+        wrapped.log(level, obj);
+    }
+
+    @Override
+    public final void log(Level level, Supplier<String> msgSupplier) {
+        wrapped.log(level, msgSupplier);
+    }
+
+    @Override
+    public final void log(Level level, Supplier<String> msgSupplier, Throwable thrown) {
+        wrapped.log(level, msgSupplier, thrown);
+    }
+
+    @Override
+    public final void log(Level level, ResourceBundle bundle, String format, Object... params) {
+        wrapped.log(level, bundle, format, params);
+    }
+
+    @Override
+    public final void log(Level level, ResourceBundle bundle, String key, Throwable thrown) {
+        wrapped.log(level, bundle, key, thrown);
+    }
+
+    @Override
+    public final boolean isLoggable(Level level) {
+        return wrapped.isLoggable(level);
+    }
+
+    // Override methods from PlatformLogger.Bridge that don't take a
+    // resource bundle...
+
+    @Override
+    public final void logp(sun.util.logging.PlatformLogger.Level level, String sourceClass, String sourceMethod,
+            String key) {
+        logrb(level, sourceClass, sourceMethod, bundle, key, (Object[]) null);
+    }
+
+    @Override
+    public final void logp(sun.util.logging.PlatformLogger.Level level, String sourceClass, String sourceMethod,
+            String key, Throwable thrown) {
+        logrb(level, sourceClass, sourceMethod, bundle, key, thrown);
+    }
+
+    @Override
+    public final void logp(sun.util.logging.PlatformLogger.Level level, String sourceClass, String sourceMethod,
+            String key, Object... params) {
+        logrb(level, sourceClass, sourceMethod, bundle, key, params);
+    }
+
+    @Override
+    public final void log(sun.util.logging.PlatformLogger.Level level, String msg, Throwable thrown) {
+        logrb(level, bundle, msg, thrown);
+    }
+
+    @Override
+    public final void log(sun.util.logging.PlatformLogger.Level level, String msg) {
+        logrb(level, bundle, msg, (Object[]) null);
+    }
+
+    @Override
+    public final void log(sun.util.logging.PlatformLogger.Level level, String format, Object... params) {
+        logrb(level, bundle, format, params);
+    }
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/jdk/internal/logger/LoggerFinderLoader.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,210 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package jdk.internal.logger;
+
+import java.io.FilePermission;
+import java.security.AccessController;
+import java.security.Permission;
+import java.security.PrivilegedAction;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.ServiceConfigurationError;
+import java.util.ServiceLoader;
+import sun.security.util.SecurityConstants;
+
+/**
+ * Helper class used to load the {@link java.lang.System.LoggerFinder}.
+ */
+public final class LoggerFinderLoader {
+    private static volatile System.LoggerFinder service;
+    private static final Object lock = new int[0];
+    static final Permission CLASSLOADER_PERMISSION =
+            SecurityConstants.GET_CLASSLOADER_PERMISSION;
+    static final Permission READ_PERMISSION =
+            new FilePermission("<<ALL FILES>>",
+                    SecurityConstants.FILE_READ_ACTION);
+    public static final RuntimePermission LOGGERFINDER_PERMISSION =
+                new RuntimePermission("loggerFinder");
+
+    // This is used to control how the LoggerFinderLoader handles
+    // errors when instantiating the LoggerFinder provider.
+    // ERROR => throws ServiceConfigurationError
+    // WARNING => Do not fail, use plain default (simple logger) implementation,
+    //            prints warning on console. (this is the default)
+    // DEBUG => Do not fail, use plain default (simple logger) implementation,
+    //          prints warning and exception stack trace on console.
+    // QUIET => Do not fail and stay silent.
+    private static enum ErrorPolicy { ERROR, WARNING, DEBUG, QUIET };
+
+    // This class is static and cannot be instantiated.
+    private LoggerFinderLoader() {
+        throw new InternalError("LoggerFinderLoader cannot be instantiated");
+    }
+
+
+    // Return the loaded LoggerFinder, or load it if not already loaded.
+    private static System.LoggerFinder service() {
+        if (service != null) return service;
+        synchronized(lock) {
+            if (service != null) return service;
+            service = loadLoggerFinder();
+        }
+        // Since the LoggerFinder is already loaded - we can stop using
+        // temporary loggers.
+        BootstrapLogger.redirectTemporaryLoggers();
+        return service;
+    }
+
+    // Get configuration error policy
+    private static ErrorPolicy configurationErrorPolicy() {
+        final PrivilegedAction<String> getConfigurationErrorPolicy =
+                () -> System.getProperty("jdk.logger.finder.error");
+        String errorPolicy = AccessController.doPrivileged(getConfigurationErrorPolicy);
+        if (errorPolicy == null || errorPolicy.isEmpty()) {
+            return ErrorPolicy.WARNING;
+        }
+        try {
+            return ErrorPolicy.valueOf(errorPolicy.toUpperCase(Locale.ROOT));
+        } catch (IllegalArgumentException x) {
+            return ErrorPolicy.WARNING;
+        }
+    }
+
+    // Whether multiple provider should be considered as an error.
+    // This is further submitted to the configuration error policy.
+    private static boolean ensureSingletonProvider() {
+        final PrivilegedAction<Boolean> ensureSingletonProvider =
+                () -> Boolean.getBoolean("jdk.logger.finder.singleton");
+        return AccessController.doPrivileged(ensureSingletonProvider);
+    }
+
+    private static Iterator<System.LoggerFinder> findLoggerFinderProviders() {
+        final Iterator<System.LoggerFinder> iterator;
+        if (System.getSecurityManager() == null) {
+            iterator = ServiceLoader.load(System.LoggerFinder.class,
+                        ClassLoader.getSystemClassLoader()).iterator();
+        } else {
+            final PrivilegedAction<Iterator<System.LoggerFinder>> pa =
+                    () -> ServiceLoader.load(System.LoggerFinder.class,
+                        ClassLoader.getSystemClassLoader()).iterator();
+            iterator = AccessController.doPrivileged(pa, null,
+                        LOGGERFINDER_PERMISSION, CLASSLOADER_PERMISSION,
+                        READ_PERMISSION);
+        }
+        return iterator;
+    }
+
+    // Loads the LoggerFinder using ServiceLoader. If no LoggerFinder
+    // is found returns the default (possibly JUL based) implementation
+    private static System.LoggerFinder loadLoggerFinder() {
+        System.LoggerFinder result;
+        try {
+            // Iterator iterates with the access control context stored
+            // at ServiceLoader creation time.
+            final Iterator<System.LoggerFinder> iterator =
+                    findLoggerFinderProviders();
+            if (iterator.hasNext()) {
+                result = iterator.next();
+                if (iterator.hasNext() && ensureSingletonProvider()) {
+                    throw new ServiceConfigurationError(
+                            "More than on LoggerFinder implementation");
+                }
+            } else {
+                result = loadDefaultImplementation();
+            }
+        } catch (Error | RuntimeException x) {
+            // next caller will get the plain default impl (not linked
+            // to java.util.logging)
+            service = result = new DefaultLoggerFinder();
+            ErrorPolicy errorPolicy = configurationErrorPolicy();
+            if (errorPolicy == ErrorPolicy.ERROR) {
+                // rethrow any exception as a ServiceConfigurationError.
+                if (x instanceof Error) {
+                    throw x;
+                } else {
+                    throw new ServiceConfigurationError(
+                        "Failed to instantiate LoggerFinder provider; Using default.", x);
+                }
+            } else if (errorPolicy != ErrorPolicy.QUIET) {
+                // if QUIET just silently use the plain default impl
+                // otherwise, log a warning, possibly adding the exception
+                // stack trace (if DEBUG is specified).
+                SimpleConsoleLogger logger =
+                        new SimpleConsoleLogger("jdk.internal.logger", false);
+                logger.log(System.Logger.Level.WARNING,
+                        "Failed to instantiate LoggerFinder provider; Using default.");
+                if (errorPolicy == ErrorPolicy.DEBUG) {
+                    logger.log(System.Logger.Level.WARNING,
+                        "Exception raised trying to instantiate LoggerFinder", x);
+                }
+            }
+        }
+        return result;
+    }
+
+    private static System.LoggerFinder loadDefaultImplementation() {
+        final SecurityManager sm = System.getSecurityManager();
+        final Iterator<DefaultLoggerFinder> iterator;
+        if (sm == null) {
+            iterator = ServiceLoader.loadInstalled(DefaultLoggerFinder.class).iterator();
+        } else {
+            // We use limited do privileged here - the minimum set of
+            // permissions required to 'see' the META-INF/services resources
+            // seems to be CLASSLOADER_PERMISSION and READ_PERMISSION.
+            // Note that do privileged is required because
+            // otherwise the SecurityManager will prevent the ServiceLoader
+            // from seeing the installed provider.
+            PrivilegedAction<Iterator<DefaultLoggerFinder>> pa = () ->
+                    ServiceLoader.loadInstalled(DefaultLoggerFinder.class).iterator();
+            iterator = AccessController.doPrivileged(pa, null,
+                    LOGGERFINDER_PERMISSION, CLASSLOADER_PERMISSION,
+                    READ_PERMISSION);
+        }
+        DefaultLoggerFinder result = null;
+        try {
+            // Iterator iterates with the access control context stored
+            // at ServiceLoader creation time.
+            if (iterator.hasNext()) {
+                result = iterator.next();
+            }
+        } catch (RuntimeException x) {
+            throw new ServiceConfigurationError(
+                    "Failed to instantiate default LoggerFinder", x);
+        }
+        if (result == null) {
+            result = new DefaultLoggerFinder();
+        }
+        return result;
+    }
+
+    public static System.LoggerFinder getLoggerFinder() {
+        final SecurityManager sm = System.getSecurityManager();
+        if (sm != null) {
+            sm.checkPermission(LOGGERFINDER_PERMISSION);
+        }
+        return service();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/jdk/internal/logger/LoggerWrapper.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+
+package jdk.internal.logger;
+
+import java.util.Objects;
+import java.lang.System.Logger;
+import sun.util.logging.PlatformLogger;
+
+/**
+ * An implementation of {@link Logger} that redirects all calls to a wrapped
+ instance of Logger.
+ *
+ * @param <L> Type of the wrapped Logger: {@code Logger} or an
+ *            extension of that interface.
+ */
+public class LoggerWrapper<L extends Logger> extends AbstractLoggerWrapper<L> {
+
+    final L wrapped;
+    final PlatformLogger.Bridge platformProxy;
+
+    public LoggerWrapper(L wrapped) {
+        this(Objects.requireNonNull(wrapped), (Void)null);
+    }
+
+    LoggerWrapper(L wrapped, Void unused) {
+        this.wrapped = wrapped;
+        this.platformProxy = (wrapped instanceof PlatformLogger.Bridge) ?
+            (PlatformLogger.Bridge) wrapped : null;
+    }
+
+    @Override
+    public final L wrapped() {
+        return wrapped;
+    }
+
+    @Override
+    public final PlatformLogger.Bridge platformProxy() {
+        return platformProxy;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/jdk/internal/logger/SimpleConsoleLogger.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,486 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.internal.logger;
+
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.time.ZonedDateTime;
+import java.util.ResourceBundle;
+import java.util.function.Function;
+import java.lang.System.Logger;
+import java.lang.System.Logger.Level;
+import java.util.function.Supplier;
+import jdk.internal.misc.JavaLangAccess;
+import jdk.internal.misc.SharedSecrets;
+import sun.util.logging.PlatformLogger;
+import sun.util.logging.PlatformLogger.ConfigurableBridge.LoggerConfiguration;
+
+/**
+ * A simple console logger to emulate the behavior of JUL loggers when
+ * in the default configuration. SimpleConsoleLoggers are also used when
+ * JUL is not present and no DefaultLoggerFinder is installed.
+ */
+public class SimpleConsoleLogger extends LoggerConfiguration
+    implements Logger, PlatformLogger.Bridge, PlatformLogger.ConfigurableBridge {
+
+    static final PlatformLogger.Level DEFAULT_LEVEL = PlatformLogger.Level.INFO;
+
+    final String name;
+    volatile PlatformLogger.Level  level;
+    final boolean usePlatformLevel;
+    SimpleConsoleLogger(String name, boolean usePlatformLevel) {
+        this.name = name;
+        this.usePlatformLevel = usePlatformLevel;
+    }
+
+    @Override
+    public String getName() {
+        return name;
+    }
+
+    private Enum<?> logLevel(PlatformLogger.Level level) {
+        return usePlatformLevel ? level : level.systemLevel();
+    }
+
+    private Enum<?> logLevel(Level level) {
+        return usePlatformLevel ? PlatformLogger.toPlatformLevel(level) : level;
+    }
+
+    // ---------------------------------------------------
+    //                 From Logger
+    // ---------------------------------------------------
+
+    @Override
+    public boolean isLoggable(Level level) {
+        return isLoggable(PlatformLogger.toPlatformLevel(level));
+    }
+
+    @Override
+    public void log(Level level, ResourceBundle bundle, String key, Throwable thrown) {
+        if (isLoggable(level)) {
+            if (bundle != null) {
+                key = bundle.getString(key);
+            }
+            publish(getCallerInfo(), logLevel(level), key, thrown);
+        }
+    }
+
+    @Override
+    public void log(Level level, ResourceBundle bundle, String format, Object... params) {
+        if (isLoggable(level)) {
+            if (bundle != null) {
+                format = bundle.getString(format);
+            }
+            publish(getCallerInfo(), logLevel(level), format, params);
+        }
+    }
+
+    // ---------------------------------------------------
+    //             From PlatformLogger.Bridge
+    // ---------------------------------------------------
+
+    @Override
+    public boolean isLoggable(PlatformLogger.Level level) {
+        final PlatformLogger.Level effectiveLevel =  effectiveLevel();
+        return level != PlatformLogger.Level.OFF
+                && level.ordinal() >= effectiveLevel.ordinal();
+    }
+
+    @Override
+    public boolean isEnabled() {
+        return level != PlatformLogger.Level.OFF;
+    }
+
+    @Override
+    public void log(PlatformLogger.Level level, String msg) {
+        if (isLoggable(level)) {
+            publish(getCallerInfo(), logLevel(level), msg);
+        }
+    }
+
+    @Override
+    public void log(PlatformLogger.Level level, String msg, Throwable thrown) {
+        if (isLoggable(level)) {
+            publish(getCallerInfo(), logLevel(level), msg, thrown);
+        }
+    }
+
+    @Override
+    public void log(PlatformLogger.Level level, String msg, Object... params) {
+        if (isLoggable(level)) {
+            publish(getCallerInfo(), logLevel(level), msg, params);
+        }
+    }
+
+    private PlatformLogger.Level effectiveLevel() {
+        if (level == null) return DEFAULT_LEVEL;
+        return level;
+    }
+
+    @Override
+    public PlatformLogger.Level getPlatformLevel() {
+        return level;
+    }
+
+    @Override
+    public void setPlatformLevel(PlatformLogger.Level newLevel) {
+        level = newLevel;
+    }
+
+    @Override
+    public LoggerConfiguration getLoggerConfiguration() {
+        return this;
+    }
+
+    /**
+     * Default platform logging support - output messages to System.err -
+     * equivalent to ConsoleHandler with SimpleFormatter.
+     */
+    static PrintStream outputStream() {
+        return System.err;
+    }
+
+    // Returns the caller's class and method's name; best effort
+    // if cannot infer, return the logger's name.
+    private String getCallerInfo() {
+        String sourceClassName = null;
+        String sourceMethodName = null;
+
+        JavaLangAccess access = SharedSecrets.getJavaLangAccess();
+        Throwable throwable = new Throwable();
+        int depth = access.getStackTraceDepth(throwable);
+
+        String logClassName = "sun.util.logging.PlatformLogger";
+        String simpleLoggerClassName = "jdk.internal.logger.SimpleConsoleLogger";
+        boolean lookingForLogger = true;
+        for (int ix = 0; ix < depth; ix++) {
+            // Calling getStackTraceElement directly prevents the VM
+            // from paying the cost of building the entire stack frame.
+            final StackTraceElement frame =
+                access.getStackTraceElement(throwable, ix);
+            final String cname = frame.getClassName();
+            if (lookingForLogger) {
+                // Skip all frames until we have found the first logger frame.
+                if (cname.equals(logClassName) || cname.equals(simpleLoggerClassName)) {
+                    lookingForLogger = false;
+                }
+            } else {
+                if (skipLoggingFrame(cname)) continue;
+                if (!cname.equals(logClassName) && !cname.equals(simpleLoggerClassName)) {
+                    // We've found the relevant frame.
+                    sourceClassName = cname;
+                    sourceMethodName = frame.getMethodName();
+                    break;
+                }
+            }
+        }
+
+        if (sourceClassName != null) {
+            return sourceClassName + " " + sourceMethodName;
+        } else {
+            return name;
+        }
+    }
+
+    private String getCallerInfo(String sourceClassName, String sourceMethodName) {
+        if (sourceClassName == null) return name;
+        if (sourceMethodName == null) return sourceClassName;
+        return sourceClassName + " " + sourceMethodName;
+    }
+
+    private String toString(Throwable thrown) {
+        String throwable = "";
+        if (thrown != null) {
+            StringWriter sw = new StringWriter();
+            PrintWriter pw = new PrintWriter(sw);
+            pw.println();
+            thrown.printStackTrace(pw);
+            pw.close();
+            throwable = sw.toString();
+        }
+        return throwable;
+    }
+
+    private synchronized String format(Enum<?> level,
+            String msg, Throwable thrown, String callerInfo) {
+
+        ZonedDateTime zdt = ZonedDateTime.now();
+        String throwable = toString(thrown);
+
+        return String.format(Formatting.formatString,
+                         zdt,
+                         callerInfo,
+                         name,
+                         level.name(),
+                         msg,
+                         throwable);
+    }
+
+    // publish accepts both PlatformLogger Levels and LoggerFinder Levels.
+    private void publish(String callerInfo, Enum<?> level, String msg) {
+        outputStream().print(format(level, msg, null, callerInfo));
+    }
+    // publish accepts both PlatformLogger Levels and LoggerFinder Levels.
+    private void publish(String callerInfo, Enum<?> level, String msg, Throwable thrown) {
+        outputStream().print(format(level, msg, thrown, callerInfo));
+    }
+    // publish accepts both PlatformLogger Levels and LoggerFinder Levels.
+    private void publish(String callerInfo, Enum<?> level, String msg, Object... params) {
+        msg = params == null || params.length == 0 ? msg
+                : Formatting.formatMessage(msg, params);
+        outputStream().print(format(level, msg, null, callerInfo));
+    }
+
+    public static SimpleConsoleLogger makeSimpleLogger(String name, boolean usePlatformLevel) {
+        return new SimpleConsoleLogger(name, usePlatformLevel);
+    }
+
+    public static SimpleConsoleLogger makeSimpleLogger(String name) {
+        return new SimpleConsoleLogger(name, false);
+    }
+
+    public static String getSimpleFormat(Function<String, String> defaultPropertyGetter) {
+        return Formatting.getSimpleFormat(defaultPropertyGetter);
+    }
+
+    public static boolean skipLoggingFrame(String cname) {
+        return Formatting.skipLoggingFrame(cname);
+    }
+
+    @Override
+    public void log(PlatformLogger.Level level, Supplier<String> msgSupplier) {
+        if (isLoggable(level)) {
+            publish(getCallerInfo(), logLevel(level), msgSupplier.get());
+        }
+    }
+
+    @Override
+    public void log(PlatformLogger.Level level, Throwable thrown,
+            Supplier<String> msgSupplier) {
+        if (isLoggable(level)) {
+            publish(getCallerInfo(), logLevel(level), msgSupplier.get(), thrown);
+        }
+    }
+
+    @Override
+    public void logp(PlatformLogger.Level level, String sourceClass,
+            String sourceMethod, String msg) {
+        if (isLoggable(level)) {
+            publish(getCallerInfo(sourceClass, sourceMethod), logLevel(level), msg);
+        }
+    }
+
+    @Override
+    public void logp(PlatformLogger.Level level, String sourceClass,
+            String sourceMethod, Supplier<String> msgSupplier) {
+        if (isLoggable(level)) {
+            publish(getCallerInfo(sourceClass, sourceMethod), logLevel(level), msgSupplier.get());
+        }
+    }
+
+    @Override
+    public void logp(PlatformLogger.Level level, String sourceClass, String sourceMethod,
+            String msg, Object... params) {
+        if (isLoggable(level)) {
+            publish(getCallerInfo(sourceClass, sourceMethod), logLevel(level), msg, params);
+        }
+    }
+
+    @Override
+    public void logp(PlatformLogger.Level level, String sourceClass,
+            String sourceMethod, String msg, Throwable thrown) {
+        if (isLoggable(level)) {
+            publish(getCallerInfo(sourceClass, sourceMethod), logLevel(level), msg, thrown);
+        }
+    }
+
+    @Override
+    public void logp(PlatformLogger.Level level, String sourceClass,
+            String sourceMethod, Throwable thrown, Supplier<String> msgSupplier) {
+        if (isLoggable(level)) {
+            publish(getCallerInfo(sourceClass, sourceMethod), logLevel(level), msgSupplier.get(), thrown);
+        }
+    }
+
+    @Override
+    public void logrb(PlatformLogger.Level level, String sourceClass,
+            String sourceMethod, ResourceBundle bundle, String key, Object... params) {
+        if (isLoggable(level)) {
+            String msg = bundle == null ? key : bundle.getString(key);
+            publish(getCallerInfo(sourceClass, sourceMethod), logLevel(level), msg, params);
+        }
+    }
+
+    @Override
+    public void logrb(PlatformLogger.Level level, String sourceClass,
+            String sourceMethod, ResourceBundle bundle, String key, Throwable thrown) {
+        if (isLoggable(level)) {
+            String msg = bundle == null ? key : bundle.getString(key);
+            publish(getCallerInfo(sourceClass, sourceMethod), logLevel(level), msg, thrown);
+        }
+    }
+
+    @Override
+    public void logrb(PlatformLogger.Level level, ResourceBundle bundle,
+            String key, Object... params) {
+        if (isLoggable(level)) {
+            String msg = bundle == null ? key : bundle.getString(key);
+            publish(getCallerInfo(), logLevel(level), msg, params);
+        }
+    }
+
+    @Override
+    public void logrb(PlatformLogger.Level level, ResourceBundle bundle,
+            String key, Throwable thrown) {
+        if (isLoggable(level)) {
+            String msg = bundle == null ? key : bundle.getString(key);
+            publish(getCallerInfo(), logLevel(level), msg, thrown);
+        }
+    }
+
+    private static final class Formatting {
+        static final String DEFAULT_FORMAT =
+            "%1$tb %1$td, %1$tY %1$tl:%1$tM:%1$tS %1$Tp %2$s%n%4$s: %5$s%6$s%n";
+        static final String FORMAT_PROP_KEY =
+            "java.util.logging.SimpleFormatter.format";
+        static final String formatString = getSimpleFormat(null);
+
+        // Make it easier to wrap Logger...
+        static private final String[] skips;
+        static {
+            String additionalPkgs = AccessController.doPrivileged(
+                (PrivilegedAction<String>)
+                () -> System.getProperty("jdk.logger.packages"));
+            skips = additionalPkgs == null ? new String[0] : additionalPkgs.split(",");
+
+        }
+
+        static boolean skipLoggingFrame(String cname) {
+            // skip logging/logger infrastructure
+
+            // fast escape path: all the prefixes below start with 's' or 'j' and
+            // have more than 12 characters.
+            char c = cname.length() < 12 ? 0 : cname.charAt(0);
+            if (c == 's') {
+                // skip internal machinery classes
+                if (cname.startsWith("sun.util.logging."))   return true;
+                if (cname.startsWith("sun.reflect."))        return true;
+                if (cname.startsWith("sun.rmi.runtime.Log")) return true;
+            } else if (c == 'j') {
+                // Message delayed at Bootstrap: no need to go further up.
+                if (cname.startsWith("jdk.internal.logger.BootstrapLogger$LogEvent")) return false;
+                // skip public machinery classes
+                if (cname.startsWith("jdk.internal.logger."))          return true;
+                if (cname.startsWith("java.util.logging."))            return true;
+                if (cname.startsWith("java.lang.System$Logger"))       return true;
+                if (cname.startsWith("java.lang.reflect."))            return true;
+                if (cname.startsWith("java.lang.invoke.MethodHandle")) return true;
+                if (cname.startsWith("java.lang.invoke.LambdaForm"))    return true;
+                if (cname.startsWith("java.security.AccessController")) return true;
+            }
+
+            // check additional prefixes if any are specified.
+            if (skips.length > 0) {
+                for (int i=0; i<skips.length; i++) {
+                    if (!skips[i].isEmpty() && cname.startsWith(skips[i])) {
+                        return true;
+                    }
+                }
+            }
+
+            return false;
+        }
+
+        static String getSimpleFormat(Function<String, String> defaultPropertyGetter) {
+            // Using a lambda here causes
+            //    jdk/test/java/lang/invoke/lambda/LogGeneratedClassesTest.java
+            // to fail - because that test has a testcase which somehow references
+            // PlatformLogger and counts the number of generated lambda classes
+            // So we explicitely use new PrivilegedAction<String> here.
+            String format =
+                AccessController.doPrivileged(new PrivilegedAction<String>() {
+                @Override
+                public String run() {
+                    return System.getProperty(FORMAT_PROP_KEY);
+                }
+            });
+            if (format == null && defaultPropertyGetter != null) {
+                format = defaultPropertyGetter.apply(FORMAT_PROP_KEY);
+            }
+            if (format != null) {
+                try {
+                    // validate the user-defined format string
+                    String.format(format, ZonedDateTime.now(), "", "", "", "", "");
+                } catch (IllegalArgumentException e) {
+                    // illegal syntax; fall back to the default format
+                    format = DEFAULT_FORMAT;
+                }
+            } else {
+                format = DEFAULT_FORMAT;
+            }
+            return format;
+        }
+
+
+        // Copied from java.util.logging.Formatter.formatMessage
+        static String formatMessage(String format, Object... parameters) {
+            // Do the formatting.
+            try {
+                if (parameters == null || parameters.length == 0) {
+                    // No parameters.  Just return format string.
+                    return format;
+                }
+                // Is it a java.text style format?
+                // Ideally we could match with
+                // Pattern.compile("\\{\\d").matcher(format).find())
+                // However the cost is 14% higher, so we cheaply check for
+                //
+                boolean isJavaTestFormat = false;
+                final int len = format.length();
+                for (int i=0; i<len-2; i++) {
+                    final char c = format.charAt(i);
+                    if (c == '{') {
+                        final int d = format.charAt(i+1);
+                        if (d >= '0' && d <= '9') {
+                            isJavaTestFormat = true;
+                            break;
+                        }
+                    }
+                }
+                if (isJavaTestFormat) {
+                    return java.text.MessageFormat.format(format, parameters);
+                }
+                return format;
+            } catch (Exception ex) {
+                // Formatting failed: use format string.
+                return format;
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/jdk/internal/logger/package-info.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * <b>[JDK INTERNAL]</b>
+ * The {@code jdk.internal.logger} package defines an internal provider
+ * whose default naive implementation is replaced by the {@code java.logging}
+ * module when the {@code java.logging} module is present.
+ * <p>
+ * <b>Default Implementation</b>
+ * <p>
+ * The JDK default implementation of the System.LoggerFinder will attempt to
+ * load an installed instance of the {@link jdk.internal.logger.DefaultLoggerFinder}
+ * defined in this package.
+ * When the {@code java.util.logging} package is present, this will usually
+ * resolve to an instance of {@link sun.util.logging.internal.LoggingProviderImpl} -
+ * which provides an implementation of the Logger whose backend is a
+ * {@link java.util.logging.Logger java.util.logging.Logger}.
+ * Configuration can thus be performed by direct access to the regular
+ * {@code java.util.logging} APIs,
+ * using {@link java.util.logging.Logger java.util.logging.Logger} and
+ * {@link java.util.logging.LogManager} to access and configure the backend
+ * Loggers.
+ * <br>
+ * If however {@code java.util.logging} is not linked with the application, then
+ * the default implementation will return a simple logger that will print out
+ * all log messages of INFO level and above to the console ({@code System.err}),
+ * as implemented by the base {@link jdk.internal.logger.DefaultLoggerFinder} class.
+ * <p>
+ * <b>Message Levels and Mapping to java.util.logging</b>
+ * <p>
+ * The {@link java.lang.System.LoggerFinder} class documentation describe how
+ * {@linkplain java.lang.System.Logger.Level System.Logger levels} are mapped
+ * to {@linkplain java.util.logging.Level JUL levels} when {@code
+ * java.util.logging} is the backend.
+ *
+ * @see jdk.internal.logger.DefaultLoggerFinder
+ * @see sun.util.logging.internal.LoggingProviderImpl
+ * @see java.lang.System.LoggerFinder
+ * @see java.lang.System.Logger
+ * @see sun.util.logging.PlatformLogger.Bridge
+ * @see sun.util.logging.internal
+ *
+ * @since 1.9
+ */
+package jdk.internal.logger;
--- a/jdk/src/java.base/share/classes/sun/invoke/util/Wrapper.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/src/java.base/share/classes/sun/invoke/util/Wrapper.java	Mon Nov 23 14:37:04 2015 -0500
@@ -26,36 +26,34 @@
 package sun.invoke.util;
 
 public enum Wrapper {
-    //        wrapperType    primitiveType  char            zero         emptyArray          format
-    BOOLEAN(  Boolean.class, boolean.class, 'Z',          Boolean.FALSE, new boolean[0], Format.unsigned( 1)),
+    //        wrapperType    primitiveType  char     emptyArray          format
+    BOOLEAN(  Boolean.class, boolean.class, 'Z', new boolean[0], Format.unsigned( 1)),
     // These must be in the order defined for widening primitive conversions in JLS 5.1.2
     // Avoid boxing integral types here to defer initialization of internal caches
-    BYTE   (     Byte.class,    byte.class, 'B',      new Byte((byte)0), new    byte[0], Format.signed(   8)),
-    SHORT  (    Short.class,   short.class, 'S',    new Short((short)0), new   short[0], Format.signed(  16)),
-    CHAR   (Character.class,    char.class, 'C', new Character((char)0), new    char[0], Format.unsigned(16)),
-    INT    (  Integer.class,     int.class, 'I',         new Integer(0), new     int[0], Format.signed(  32)),
-    LONG   (     Long.class,    long.class, 'J',            new Long(0), new    long[0], Format.signed(  64)),
-    FLOAT  (    Float.class,   float.class, 'F',        (Float)(float)0, new   float[0], Format.floating(32)),
-    DOUBLE (   Double.class,  double.class, 'D',      (Double)(double)0, new  double[0], Format.floating(64)),
-    OBJECT (   Object.class,  Object.class, 'L',                   null, new  Object[0], Format.other(    1)),
+    BYTE   (     Byte.class,    byte.class, 'B', new    byte[0], Format.signed(   8)),
+    SHORT  (    Short.class,   short.class, 'S', new   short[0], Format.signed(  16)),
+    CHAR   (Character.class,    char.class, 'C', new    char[0], Format.unsigned(16)),
+    INT    (  Integer.class,     int.class, 'I', new     int[0], Format.signed(  32)),
+    LONG   (     Long.class,    long.class, 'J', new    long[0], Format.signed(  64)),
+    FLOAT  (    Float.class,   float.class, 'F', new   float[0], Format.floating(32)),
+    DOUBLE (   Double.class,  double.class, 'D', new  double[0], Format.floating(64)),
+    OBJECT (   Object.class,  Object.class, 'L', new  Object[0], Format.other(    1)),
     // VOID must be the last type, since it is "assignable" from any other type:
-    VOID   (     Void.class,    void.class, 'V',                   null,           null, Format.other(    0)),
+    VOID   (     Void.class,    void.class, 'V',           null, Format.other(    0)),
     ;
 
     private final Class<?> wrapperType;
     private final Class<?> primitiveType;
     private final char     basicTypeChar;
-    private final Object   zero;
     private final Object   emptyArray;
     private final int      format;
     private final String   wrapperSimpleName;
     private final String   primitiveSimpleName;
 
-    private Wrapper(Class<?> wtype, Class<?> ptype, char tchar, Object zero, Object emptyArray, int format) {
+    private Wrapper(Class<?> wtype, Class<?> ptype, char tchar, Object emptyArray, int format) {
         this.wrapperType = wtype;
         this.primitiveType = ptype;
         this.basicTypeChar = tchar;
-        this.zero = zero;
         this.emptyArray = emptyArray;
         this.format = format;
         this.wrapperSimpleName = wtype.getSimpleName();
@@ -66,7 +64,7 @@
     public String detailString() {
         return wrapperSimpleName+
                 java.util.Arrays.asList(wrapperType, primitiveType,
-                basicTypeChar, zero,
+                basicTypeChar, zero(),
                 "0x"+Integer.toHexString(format));
     }
 
@@ -223,13 +221,39 @@
      *  type.  (For void, it is what a reflective method returns
      *  instead of no value at all.)
      */
-    public Object zero() { return zero; }
+    public Object zero() {
+        switch (this) {
+            case BOOLEAN:
+                return Boolean.FALSE;
+            case INT:
+                return (Integer)0;
+            case BYTE:
+                return (Byte)(byte)0;
+            case CHAR:
+                return (Character)(char)0;
+            case SHORT:
+                return (Short)(short)0;
+            case LONG:
+                return (Long)(long)0;
+            case FLOAT:
+                return FLOAT_ZERO;
+            case DOUBLE:
+                return DOUBLE_ZERO;
+            case VOID:
+            case OBJECT:
+            default:
+                return null;
+        }
+    }
+
+    private static final Object DOUBLE_ZERO = (Double)(double)0;
+    private static final Object FLOAT_ZERO = (Float)(float)0;
 
     /** Produce a zero value for the given wrapper type T.
      *  The optional argument must a type compatible with this wrapper.
      *  Equivalent to {@code this.cast(this.zero(), type)}.
      */
-    public <T> T zero(Class<T> type) { return convert(zero, type); }
+    public <T> T zero(Class<T> type) { return convert(zero(), type); }
 
     /** Return the wrapper that wraps values of the given type.
      *  The type may be {@code Object}, meaning the {@code OBJECT} wrapper.
@@ -474,7 +498,7 @@
             }
         } else if (x == null) {
             @SuppressWarnings("unchecked")
-            T z = (T) zero;
+            T z = (T) zero();
             return z;
         }
         @SuppressWarnings("unchecked")
--- a/jdk/src/java.base/share/classes/sun/security/x509/AlgorithmId.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/src/java.base/share/classes/sun/security/x509/AlgorithmId.java	Mon Nov 23 14:37:04 2015 -0500
@@ -977,4 +977,69 @@
         }
         return null;
     }
+
+    /**
+     * Checks if a signature algorithm matches a key algorithm, i.e. a
+     * signature can be initialized with a key.
+     *
+     * @param kAlg must not be null
+     * @param sAlg must not be null
+     * @throws IllegalArgumentException if they do not match
+     */
+    public static void checkKeyAndSigAlgMatch(String kAlg, String sAlg) {
+        String sAlgUp = sAlg.toUpperCase(Locale.US);
+        if ((sAlgUp.endsWith("WITHRSA") && !kAlg.equalsIgnoreCase("RSA")) ||
+                (sAlgUp.endsWith("WITHECDSA") && !kAlg.equalsIgnoreCase("EC")) ||
+                (sAlgUp.endsWith("WITHDSA") && !kAlg.equalsIgnoreCase("DSA"))) {
+            throw new IllegalArgumentException(
+                    "key algorithm not compatible with signature algorithm");
+        }
+    }
+
+    /**
+     * Returns the default signature algorithm for a private key. The digest
+     * part might evolve with time. Remember to update the spec of
+     * {@link jdk.security.jarsigner.JarSigner.Builder#getDefaultSignatureAlgorithm(PrivateKey)}
+     * if updated.
+     *
+     * @param k cannot be null
+     * @return the default alg, might be null if unsupported
+     */
+    public static String getDefaultSigAlgForKey(PrivateKey k) {
+        switch (k.getAlgorithm().toUpperCase()) {
+            case "EC":
+                return ecStrength(KeyUtil.getKeySize(k))
+                    + "withECDSA";
+            case "DSA":
+                return ifcFfcStrength(KeyUtil.getKeySize(k))
+                    + "withDSA";
+            case "RSA":
+                return ifcFfcStrength(KeyUtil.getKeySize(k))
+                    + "withRSA";
+            default:
+                return null;
+        }
+    }
+
+    // Values from SP800-57 part 1 rev 3 tables 2 and three
+    private static String ecStrength (int bitLength) {
+        if (bitLength >= 512) { // 256 bits of strength
+            return "SHA512";
+        } else if (bitLength >= 384) {  // 192 bits of strength
+            return "SHA384";
+        } else { // 128 bits of strength and less
+            return "SHA256";
+        }
+    }
+
+    // same values for RSA and DSA
+    private static String ifcFfcStrength (int bitLength) {
+        if (bitLength > 7680) { // 256 bits
+            return "SHA512";
+        } else if (bitLength > 3072) {  // 192 bits
+            return "SHA384";
+        } else  { // 128 bits and less
+            return "SHA256";
+        }
+    }
 }
--- a/jdk/src/java.base/share/classes/sun/util/logging/LoggingProxy.java	Thu Nov 19 18:18:05 2015 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,68 +0,0 @@
-/*
- * Copyright (c) 2009, 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.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-
-package sun.util.logging;
-
-/**
- * A proxy interface for the java.util.logging support.
- *
- * @see sun.util.logging.LoggingSupport
- */
-public interface LoggingProxy {
-    // Methods to bridge java.util.logging.Logger methods
-    public Object getLogger(String name);
-
-    public Object getLevel(Object logger);
-
-    public void setLevel(Object logger, Object newLevel);
-
-    public boolean isLoggable(Object logger, Object level);
-
-    public void log(Object logger, Object level, String msg);
-
-    public void log(Object logger, Object level, String msg, Throwable t);
-
-    public void log(Object logger, Object level, String msg, Object... params);
-
-    // Methods to bridge java.util.logging.LoggingMXBean methods
-    public java.util.List<String> getLoggerNames();
-
-    public String getLoggerLevel(String loggerName);
-
-    public void setLoggerLevel(String loggerName, String levelName);
-
-    public String getParentLoggerName(String loggerName);
-
-    // Methods to bridge Level.parse() and Level.getName() method
-    public Object parseLevel(String levelName);
-
-    public String getLevelName(Object level);
-
-    public int getLevelValue(Object level);
-
-    // return the logging property
-    public String getProperty(String key);
-}
--- a/jdk/src/java.base/share/classes/sun/util/logging/LoggingSupport.java	Thu Nov 19 18:18:05 2015 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,190 +0,0 @@
-/*
- * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-
-package sun.util.logging;
-
-import java.lang.reflect.Field;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
-import java.time.ZonedDateTime;
-
-/**
- * Internal API to support JRE implementation to detect if the java.util.logging
- * support is available but with no dependency on the java.util.logging
- * classes.  This LoggingSupport class provides several static methods to
- * access the java.util.logging functionality that requires the caller
- * to ensure that the logging support is {@linkplain #isAvailable available}
- * before invoking it.
- *
- * @see sun.util.logging.PlatformLogger if you want to log messages even
- * if the logging support is not available
- */
-public class LoggingSupport {
-    private LoggingSupport() { }
-
-    private static final LoggingProxy proxy =
-        AccessController.doPrivileged(new PrivilegedAction<LoggingProxy>() {
-            public LoggingProxy run() {
-                try {
-                    // create a LoggingProxyImpl instance when
-                    // java.util.logging classes exist
-                    Class<?> c = Class.forName("java.util.logging.LoggingProxyImpl", true, null);
-                    Field f = c.getDeclaredField("INSTANCE");
-                    f.setAccessible(true);
-                    return (LoggingProxy) f.get(null);
-                } catch (ClassNotFoundException cnf) {
-                    return null;
-                } catch (NoSuchFieldException e) {
-                    throw new AssertionError(e);
-                } catch (IllegalAccessException e) {
-                    throw new AssertionError(e);
-                }
-            }});
-
-    /**
-     * Returns true if java.util.logging support is available.
-     */
-    public static boolean isAvailable() {
-        return proxy != null;
-    }
-
-    private static void ensureAvailable() {
-        if (proxy == null)
-            throw new AssertionError("Should not here");
-    }
-
-    public static java.util.List<String> getLoggerNames() {
-        ensureAvailable();
-        return proxy.getLoggerNames();
-    }
-    public static String getLoggerLevel(String loggerName) {
-        ensureAvailable();
-        return proxy.getLoggerLevel(loggerName);
-    }
-
-    public static void setLoggerLevel(String loggerName, String levelName) {
-        ensureAvailable();
-        proxy.setLoggerLevel(loggerName, levelName);
-    }
-
-    public static String getParentLoggerName(String loggerName) {
-        ensureAvailable();
-        return proxy.getParentLoggerName(loggerName);
-    }
-
-    public static Object getLogger(String name) {
-        ensureAvailable();
-        return proxy.getLogger(name);
-    }
-
-    public static Object getLevel(Object logger) {
-        ensureAvailable();
-        return proxy.getLevel(logger);
-    }
-
-    public static void setLevel(Object logger, Object newLevel) {
-        ensureAvailable();
-        proxy.setLevel(logger, newLevel);
-    }
-
-    public static boolean isLoggable(Object logger, Object level) {
-        ensureAvailable();
-        return proxy.isLoggable(logger,level);
-    }
-
-    public static void log(Object logger, Object level, String msg) {
-        ensureAvailable();
-        proxy.log(logger, level, msg);
-    }
-
-    public static void log(Object logger, Object level, String msg, Throwable t) {
-        ensureAvailable();
-        proxy.log(logger, level, msg, t);
-    }
-
-    public static void log(Object logger, Object level, String msg, Object... params) {
-        ensureAvailable();
-        proxy.log(logger, level, msg, params);
-    }
-
-    public static Object parseLevel(String levelName) {
-        ensureAvailable();
-        return proxy.parseLevel(levelName);
-    }
-
-    public static String getLevelName(Object level) {
-        ensureAvailable();
-        return proxy.getLevelName(level);
-    }
-
-    public static int getLevelValue(Object level) {
-        ensureAvailable();
-        return proxy.getLevelValue(level);
-    }
-
-    // Since JDK 9, logging uses java.time to get more precise time stamps.
-    // It is possible to configure the simple format to print nano seconds (.%1$tN)
-    // by specifying:
-    // java.util.logging.SimpleFormatter.format=%1$tb %1$td, %1$tY %1$tl:%1$tM:%1$tS.%1$tN %1$Tp %2$s%n%4$s: %5$s%6$s%n
-    // in the logging configuration
-    private static final String DEFAULT_FORMAT =
-        "%1$tb %1$td, %1$tY %1$tl:%1$tM:%1$tS %1$Tp %2$s%n%4$s: %5$s%6$s%n";
-
-    private static final String FORMAT_PROP_KEY = "java.util.logging.SimpleFormatter.format";
-    public static String getSimpleFormat() {
-        return getSimpleFormat(true);
-    }
-
-    // useProxy if true will cause initialization of
-    // java.util.logging and read its configuration
-    static String getSimpleFormat(boolean useProxy) {
-        String format =
-            AccessController.doPrivileged(
-                new PrivilegedAction<String>() {
-                    public String run() {
-                        return System.getProperty(FORMAT_PROP_KEY);
-                    }
-                });
-
-        if (useProxy && proxy != null && format == null) {
-            format = proxy.getProperty(FORMAT_PROP_KEY);
-        }
-
-        if (format != null) {
-            try {
-                // validate the user-defined format string
-                String.format(format, ZonedDateTime.now(), "", "", "", "", "");
-            } catch (IllegalArgumentException e) {
-                // illegal syntax; fall back to the default format
-                format = DEFAULT_FORMAT;
-            }
-        } else {
-            format = DEFAULT_FORMAT;
-        }
-        return format;
-    }
-
-}
--- a/jdk/src/java.base/share/classes/sun/util/logging/PlatformLogger.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/src/java.base/share/classes/sun/util/logging/PlatformLogger.java	Mon Nov 23 14:37:04 2015 -0500
@@ -27,20 +27,13 @@
 package sun.util.logging;
 
 import java.lang.ref.WeakReference;
-import java.io.PrintStream;
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
-import java.time.Clock;
-import java.time.Instant;
-import java.time.ZoneId;
-import java.time.ZonedDateTime;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Map;
-import jdk.internal.misc.JavaLangAccess;
-import jdk.internal.misc.SharedSecrets;
+import java.util.ResourceBundle;
+import java.util.function.Supplier;
+import jdk.internal.logger.LazyLoggers;
+import jdk.internal.logger.LoggerWrapper;
 
 /**
  * Platform logger provides an API for the JRE components to log
@@ -56,18 +49,28 @@
  * the stack frame information issuing the log message.
  *
  * When the logging facility is enabled (at startup or runtime),
- * the java.util.logging.Logger will be created for each platform
+ * the backend logger will be created for each platform
  * logger and all log messages will be forwarded to the Logger
  * to handle.
  *
+ * The PlatformLogger uses an underlying PlatformLogger.Bridge instance
+ * obtained by calling {@link PlatformLogger.Bridge#convert PlatformLogger.Bridge.convert(}
+ * {@link jdk.internal.logger.LazyLoggers#getLazyLogger(java.lang.String, java.lang.Class)
+ * jdk.internal.logger.LazyLoggers#getLazyLogger(name, PlatformLogger.class))}.
+ *
  * Logging facility is "enabled" when one of the following
  * conditions is met:
- * 1) a system property "java.util.logging.config.class" or
- *    "java.util.logging.config.file" is set
- * 2) java.util.logging.LogManager or java.util.logging.Logger
- *    is referenced that will trigger the logging initialization.
+ * 1) ServiceLoader.load({@link java.lang.System.LoggerFinder LoggerFinder.class},
+ *    ClassLoader.getSystemClassLoader()).iterator().hasNext().
+ * 2) ServiceLoader.loadInstalled({@link jdk.internal.logger.DefaultLoggerFinder}).iterator().hasNext(),
+ *    and 2.1) a system property "java.util.logging.config.class" or
+ *             "java.util.logging.config.file" is set
+ *     or  2.2) java.util.logging.LogManager or java.util.logging.Logger
+ *              is referenced that will trigger the logging initialization.
  *
  * Default logging configuration:
+ *
+ *   No LoggerFinder service implementation declared
  *   global logging level = INFO
  *   handlers = java.util.logging.ConsoleHandler
  *   java.util.logging.ConsoleHandler.level = INFO
@@ -84,71 +87,84 @@
  * The platform loggers are designed for JDK developers use and
  * this limitation can be workaround with setting
  * -Djava.util.logging.config.file system property.
+ * <br>
+ * Calling PlatformLogger.setLevel will not work when there is a custom
+ * LoggerFinder installed - and as a consequence {@link #setLevel setLevel}
+ * is now deprecated.
  *
  * @since 1.7
  */
 public class PlatformLogger {
 
-    // The integer values must match that of {@code java.util.logging.Level}
-    // objects.
-    private static final int OFF     = Integer.MAX_VALUE;
-    private static final int SEVERE  = 1000;
-    private static final int WARNING = 900;
-    private static final int INFO    = 800;
-    private static final int CONFIG  = 700;
-    private static final int FINE    = 500;
-    private static final int FINER   = 400;
-    private static final int FINEST  = 300;
-    private static final int ALL     = Integer.MIN_VALUE;
-
     /**
      * PlatformLogger logging levels.
      */
     public static enum Level {
         // The name and value must match that of {@code java.util.logging.Level}s.
         // Declare in ascending order of the given value for binary search.
-        ALL,
-        FINEST,
-        FINER,
-        FINE,
-        CONFIG,
-        INFO,
-        WARNING,
-        SEVERE,
-        OFF;
+        ALL(System.Logger.Level.ALL),
+        FINEST(System.Logger.Level.TRACE),
+        FINER(System.Logger.Level.TRACE),
+        FINE(System.Logger.Level.DEBUG),
+        CONFIG(System.Logger.Level.DEBUG),
+        INFO(System.Logger.Level.INFO),
+        WARNING(System.Logger.Level.WARNING),
+        SEVERE(System.Logger.Level.ERROR),
+        OFF(System.Logger.Level.OFF);
 
-        /**
-         * Associated java.util.logging.Level lazily initialized in
-         * JavaLoggerProxy's static initializer only once
-         * when java.util.logging is available and enabled.
-         * Only accessed by JavaLoggerProxy.
-         */
-        /* java.util.logging.Level */ Object javaLevel;
+        final System.Logger.Level systemLevel;
+        Level(System.Logger.Level systemLevel) {
+            this.systemLevel = systemLevel;
+        }
+
+        // The integer values must match that of {@code java.util.logging.Level}
+        // objects.
+        private static final int SEVERITY_OFF     = Integer.MAX_VALUE;
+        private static final int SEVERITY_SEVERE  = 1000;
+        private static final int SEVERITY_WARNING = 900;
+        private static final int SEVERITY_INFO    = 800;
+        private static final int SEVERITY_CONFIG  = 700;
+        private static final int SEVERITY_FINE    = 500;
+        private static final int SEVERITY_FINER   = 400;
+        private static final int SEVERITY_FINEST  = 300;
+        private static final int SEVERITY_ALL     = Integer.MIN_VALUE;
 
         // ascending order for binary search matching the list of enum constants
         private static final int[] LEVEL_VALUES = new int[] {
-            PlatformLogger.ALL, PlatformLogger.FINEST, PlatformLogger.FINER,
-            PlatformLogger.FINE, PlatformLogger.CONFIG, PlatformLogger.INFO,
-            PlatformLogger.WARNING, PlatformLogger.SEVERE, PlatformLogger.OFF
+            SEVERITY_ALL, SEVERITY_FINEST, SEVERITY_FINER,
+            SEVERITY_FINE, SEVERITY_CONFIG, SEVERITY_INFO,
+            SEVERITY_WARNING, SEVERITY_SEVERE, SEVERITY_OFF
         };
 
+        public System.Logger.Level systemLevel() {
+            return systemLevel;
+        }
+
         public int intValue() {
             return LEVEL_VALUES[this.ordinal()];
         }
 
-        static Level valueOf(int level) {
+        /**
+         * Maps a severity value to an effective logger level.
+         * @param level The severity of the messages that should be
+         *        logged with a logger set to the returned level.
+         * @return The effective logger level, which is the nearest Level value
+         *         whose severity is greater or equal to the given level.
+         *         For level > SEVERE (OFF excluded), return SEVERE.
+         */
+        public static Level valueOf(int level) {
             switch (level) {
                 // ordering per the highest occurrences in the jdk source
                 // finest, fine, finer, info first
-                case PlatformLogger.FINEST  : return Level.FINEST;
-                case PlatformLogger.FINE    : return Level.FINE;
-                case PlatformLogger.FINER   : return Level.FINER;
-                case PlatformLogger.INFO    : return Level.INFO;
-                case PlatformLogger.WARNING : return Level.WARNING;
-                case PlatformLogger.CONFIG  : return Level.CONFIG;
-                case PlatformLogger.SEVERE  : return Level.SEVERE;
-                case PlatformLogger.OFF     : return Level.OFF;
-                case PlatformLogger.ALL     : return Level.ALL;
+                case SEVERITY_FINEST  : return Level.FINEST;
+                case SEVERITY_FINE    : return Level.FINE;
+                case SEVERITY_FINER   : return Level.FINER;
+                case SEVERITY_INFO    : return Level.INFO;
+                case SEVERITY_WARNING : return Level.WARNING;
+                case SEVERITY_CONFIG  : return Level.CONFIG;
+                case SEVERITY_SEVERE  : return Level.SEVERE;
+                case SEVERITY_OFF     : return Level.OFF;
+                case SEVERITY_ALL     : return Level.ALL;
             }
             // return the nearest Level value >= the given level,
             // for level > SEVERE, return SEVERE and exclude OFF
@@ -157,39 +173,110 @@
         }
     }
 
-    private static final Level DEFAULT_LEVEL = Level.INFO;
-    private static boolean loggingEnabled;
-    static {
-        loggingEnabled = AccessController.doPrivileged(
-            new PrivilegedAction<>() {
-                public Boolean run() {
-                    String cname = System.getProperty("java.util.logging.config.class");
-                    String fname = System.getProperty("java.util.logging.config.file");
-                    return (cname != null || fname != null);
-                }
-            });
+    /**
+     *
+     * The PlatformLogger.Bridge interface is implemented by the System.Logger
+     * objects returned by our default JUL provider - so that JRE classes using
+     * PlatformLogger see no difference when JUL is the actual backend.
+     *
+     * PlatformLogger is now only a thin adaptation layer over the same
+     * loggers than returned by java.lang.System.getLogger(String name).
+     *
+     * The recommendation for JRE classes going forward is to use
+     * java.lang.System.getLogger(String name), which will
+     * use Lazy Loggers when possible and necessary.
+     *
+     */
+    public static interface Bridge {
+
+        /**
+         * Gets the name for this platform logger.
+         * @return the name of the platform logger.
+         */
+        public String getName();
+
+        /**
+         * Returns true if a message of the given level would actually
+         * be logged by this logger.
+         * @param level the level
+         * @return whether a message of that level would be logged
+         */
+        public boolean isLoggable(Level level);
+        public boolean isEnabled();
 
-        // force loading of all JavaLoggerProxy (sub)classes to make JIT de-optimizations
-        // less probable.  Don't initialize JavaLoggerProxy class since
-        // java.util.logging may not be enabled.
-        try {
-            Class.forName("sun.util.logging.PlatformLogger$DefaultLoggerProxy",
-                          false,
-                          PlatformLogger.class.getClassLoader());
-            Class.forName("sun.util.logging.PlatformLogger$JavaLoggerProxy",
-                          false,   // do not invoke class initializer
-                          PlatformLogger.class.getClassLoader());
-        } catch (ClassNotFoundException ex) {
-            throw new InternalError(ex);
+        public void log(Level level, String msg);
+        public void log(Level level, String msg, Throwable thrown);
+        public void log(Level level, String msg, Object... params);
+        public void log(Level level, Supplier<String> msgSupplier);
+        public void log(Level level, Throwable thrown, Supplier<String> msgSupplier);
+        public void logp(Level level, String sourceClass, String sourceMethod, String msg);
+        public void logp(Level level, String sourceClass, String sourceMethod,
+                         Supplier<String> msgSupplier);
+        public void logp(Level level, String sourceClass, String sourceMethod,
+                                                    String msg, Object... params);
+        public void logp(Level level, String sourceClass, String sourceMethod,
+                         String msg, Throwable thrown);
+        public void logp(Level level, String sourceClass, String sourceMethod,
+                         Throwable thrown, Supplier<String> msgSupplier);
+        public void logrb(Level level, String sourceClass, String sourceMethod,
+                          ResourceBundle bundle, String msg, Object... params);
+        public void logrb(Level level, String sourceClass, String sourceMethod,
+                          ResourceBundle bundle, String msg, Throwable thrown);
+        public void logrb(Level level, ResourceBundle bundle, String msg,
+                Object... params);
+        public void logrb(Level level, ResourceBundle bundle, String msg,
+                Throwable thrown);
+
+
+        public static Bridge convert(System.Logger logger) {
+            if (logger instanceof PlatformLogger.Bridge) {
+                return (Bridge) logger;
+            } else {
+                return new LoggerWrapper<>(logger);
+            }
+        }
+    }
+
+    /**
+     * The {@code PlatformLogger.ConfigurableBridge} interface is used to
+     * implement the deprecated {@link PlatformLogger#setLevel} method.
+     *
+     * PlatformLogger is now only a thin adaptation layer over the same
+     * loggers than returned by java.lang.System.getLogger(String name).
+     *
+     * The recommendation for JRE classes going forward is to use
+     * java.lang.System.getLogger(String name), which will
+     * use Lazy Loggers when possible and necessary.
+     *
+     */
+    public static interface ConfigurableBridge {
+
+        public abstract class LoggerConfiguration {
+            public abstract Level getPlatformLevel();
+            public abstract void setPlatformLevel(Level level);
+        }
+
+        public default LoggerConfiguration getLoggerConfiguration() {
+            return null;
+        }
+
+        public static LoggerConfiguration getLoggerConfiguration(PlatformLogger.Bridge logger) {
+            if (logger instanceof PlatformLogger.ConfigurableBridge) {
+                return ((ConfigurableBridge) logger).getLoggerConfiguration();
+            } else {
+                return null;
+            }
         }
     }
 
     // Table of known loggers.  Maps names to PlatformLoggers.
-    private static Map<String,WeakReference<PlatformLogger>> loggers =
+    private static final Map<String,WeakReference<PlatformLogger>> loggers =
         new HashMap<>();
 
     /**
      * Returns a PlatformLogger of a given name.
+     * @param name the name of the logger
+     * @return a PlatformLogger
      */
     public static synchronized PlatformLogger getLogger(String name) {
         PlatformLogger log = null;
@@ -198,56 +285,31 @@
             log = ref.get();
         }
         if (log == null) {
-            log = new PlatformLogger(name);
+            log = new PlatformLogger(PlatformLogger.Bridge.convert(
+                    // We pass PlatformLogger.class rather than the actual caller
+                    // because we want PlatformLoggers to be system loggers: we
+                    // won't need to resolve any resource bundles anyway.
+                    // Note: Many unit tests depend on the fact that
+                    //       PlatformLogger.getLoggerFromFinder is not caller sensitive.
+                    LazyLoggers.getLazyLogger(name, PlatformLogger.class)));
             loggers.put(name, new WeakReference<>(log));
         }
         return log;
     }
 
-    /**
-     * Initialize java.util.logging.Logger objects for all platform loggers.
-     * This method is called from LogManager.readPrimordialConfiguration().
-     */
-    public static synchronized void redirectPlatformLoggers() {
-        if (loggingEnabled || !LoggingSupport.isAvailable()) return;
-
-        loggingEnabled = true;
-        for (Map.Entry<String, WeakReference<PlatformLogger>> entry : loggers.entrySet()) {
-            WeakReference<PlatformLogger> ref = entry.getValue();
-            PlatformLogger plog = ref.get();
-            if (plog != null) {
-                plog.redirectToJavaLoggerProxy();
-            }
-        }
-    }
-
-    /**
-     * Creates a new JavaLoggerProxy and redirects the platform logger to it
-     */
-    private void redirectToJavaLoggerProxy() {
-        DefaultLoggerProxy lp = DefaultLoggerProxy.class.cast(this.loggerProxy);
-        JavaLoggerProxy jlp = new JavaLoggerProxy(lp.name, lp.level);
-        // the order of assignments is important
-        this.javaLoggerProxy = jlp;   // isLoggable checks javaLoggerProxy if set
-        this.loggerProxy = jlp;
-    }
-
-    // DefaultLoggerProxy may be replaced with a JavaLoggerProxy object
-    // when the java.util.logging facility is enabled
-    private volatile LoggerProxy loggerProxy;
-    // javaLoggerProxy is only set when the java.util.logging facility is enabled
-    private volatile JavaLoggerProxy javaLoggerProxy;
-    private PlatformLogger(String name) {
-        if (loggingEnabled) {
-            this.loggerProxy = this.javaLoggerProxy = new JavaLoggerProxy(name);
-        } else {
-            this.loggerProxy = new DefaultLoggerProxy(name);
-        }
+    // The system loggerProxy returned by LazyLoggers
+    // This may be a lazy logger - see jdk.internal.logger.LazyLoggers,
+    // or may be a Logger instance (or a wrapper thereof).
+    //
+    private final PlatformLogger.Bridge loggerProxy;
+    private PlatformLogger(PlatformLogger.Bridge loggerProxy) {
+        this.loggerProxy = loggerProxy;
     }
 
     /**
      * A convenience method to test if the logger is turned off.
      * (i.e. its level is OFF).
+     * @return whether the logger is turned off.
      */
     public boolean isEnabled() {
         return loggerProxy.isEnabled();
@@ -255,22 +317,24 @@
 
     /**
      * Gets the name for this platform logger.
+     * @return the name of the platform logger.
      */
     public String getName() {
-        return loggerProxy.name;
+        return loggerProxy.getName();
     }
 
     /**
      * Returns true if a message of the given level would actually
      * be logged by this logger.
+     * @param level the level
+     * @return whether a message of that level would be logged
      */
     public boolean isLoggable(Level level) {
         if (level == null) {
             throw new NullPointerException();
         }
-        // performance-sensitive method: use two monomorphic call-sites
-        JavaLoggerProxy jlp = javaLoggerProxy;
-        return jlp != null ? jlp.isLoggable(level) : loggerProxy.isLoggable(level);
+
+        return loggerProxy.isLoggable(level);
     }
 
     /**
@@ -281,13 +345,15 @@
      * @return  this PlatformLogger's level
      */
     public Level level() {
-        return loggerProxy.getLevel();
+        final ConfigurableBridge.LoggerConfiguration spi =
+                PlatformLogger.ConfigurableBridge.getLoggerConfiguration(loggerProxy);
+        return spi == null ? null : spi.getPlatformLevel();
     }
 
     /**
      * Set the log level specifying which message levels will be
      * logged by this logger.  Message levels lower than this
-     * value will be discarded.  The level value {@link #OFF}
+     * value will be discarded.  The level value {@link Level#OFF}
      * can be used to turn off logging.
      * <p>
      * If the new level is null, it means that this node should
@@ -295,366 +361,153 @@
      * (non-null) level value.
      *
      * @param newLevel the new value for the log level (may be null)
+     * @deprecated Platform Loggers should not be configured programmatically.
+     *             This method will not work if a custom {@link
+     *             java.lang.System.LoggerFinder} is installed.
      */
+    @Deprecated
     public void setLevel(Level newLevel) {
-        loggerProxy.setLevel(newLevel);
+        final ConfigurableBridge.LoggerConfiguration spi =
+                PlatformLogger.ConfigurableBridge.getLoggerConfiguration(loggerProxy);;
+        if (spi != null) {
+            spi.setPlatformLevel(newLevel);
+        }
     }
 
     /**
      * Logs a SEVERE message.
+     * @param msg the message
      */
     public void severe(String msg) {
-        loggerProxy.doLog(Level.SEVERE, msg);
+        loggerProxy.log(Level.SEVERE, msg, (Object[])null);
     }
 
     public void severe(String msg, Throwable t) {
-        loggerProxy.doLog(Level.SEVERE, msg, t);
+        loggerProxy.log(Level.SEVERE, msg, t);
     }
 
     public void severe(String msg, Object... params) {
-        loggerProxy.doLog(Level.SEVERE, msg, params);
+        loggerProxy.log(Level.SEVERE, msg, params);
     }
 
     /**
      * Logs a WARNING message.
+     * @param msg the message
      */
     public void warning(String msg) {
-        loggerProxy.doLog(Level.WARNING, msg);
+        loggerProxy.log(Level.WARNING, msg, (Object[])null);
     }
 
     public void warning(String msg, Throwable t) {
-        loggerProxy.doLog(Level.WARNING, msg, t);
+        loggerProxy.log(Level.WARNING, msg, t);
     }
 
     public void warning(String msg, Object... params) {
-        loggerProxy.doLog(Level.WARNING, msg, params);
+        loggerProxy.log(Level.WARNING, msg, params);
     }
 
     /**
      * Logs an INFO message.
+     * @param msg the message
      */
     public void info(String msg) {
-        loggerProxy.doLog(Level.INFO, msg);
+        loggerProxy.log(Level.INFO, msg, (Object[])null);
     }
 
     public void info(String msg, Throwable t) {
-        loggerProxy.doLog(Level.INFO, msg, t);
+        loggerProxy.log(Level.INFO, msg, t);
     }
 
     public void info(String msg, Object... params) {
-        loggerProxy.doLog(Level.INFO, msg, params);
+        loggerProxy.log(Level.INFO, msg, params);
     }
 
     /**
      * Logs a CONFIG message.
+     * @param msg the message
      */
     public void config(String msg) {
-        loggerProxy.doLog(Level.CONFIG, msg);
+        loggerProxy.log(Level.CONFIG, msg, (Object[])null);
     }
 
     public void config(String msg, Throwable t) {
-        loggerProxy.doLog(Level.CONFIG, msg, t);
+        loggerProxy.log(Level.CONFIG, msg, t);
     }
 
     public void config(String msg, Object... params) {
-        loggerProxy.doLog(Level.CONFIG, msg, params);
+        loggerProxy.log(Level.CONFIG, msg, params);
     }
 
     /**
      * Logs a FINE message.
+     * @param msg the message
      */
     public void fine(String msg) {
-        loggerProxy.doLog(Level.FINE, msg);
+        loggerProxy.log(Level.FINE, msg, (Object[])null);
     }
 
     public void fine(String msg, Throwable t) {
-        loggerProxy.doLog(Level.FINE, msg, t);
+        loggerProxy.log(Level.FINE, msg, t);
     }
 
     public void fine(String msg, Object... params) {
-        loggerProxy.doLog(Level.FINE, msg, params);
+        loggerProxy.log(Level.FINE, msg, params);
     }
 
     /**
      * Logs a FINER message.
+     * @param msg the message
      */
     public void finer(String msg) {
-        loggerProxy.doLog(Level.FINER, msg);
+        loggerProxy.log(Level.FINER, msg, (Object[])null);
     }
 
     public void finer(String msg, Throwable t) {
-        loggerProxy.doLog(Level.FINER, msg, t);
+        loggerProxy.log(Level.FINER, msg, t);
     }
 
     public void finer(String msg, Object... params) {
-        loggerProxy.doLog(Level.FINER, msg, params);
+        loggerProxy.log(Level.FINER, msg, params);
     }
 
     /**
      * Logs a FINEST message.
+     * @param msg the message
      */
     public void finest(String msg) {
-        loggerProxy.doLog(Level.FINEST, msg);
+        loggerProxy.log(Level.FINEST, msg, (Object[])null);
     }
 
     public void finest(String msg, Throwable t) {
-        loggerProxy.doLog(Level.FINEST, msg, t);
+        loggerProxy.log(Level.FINEST, msg, t);
     }
 
     public void finest(String msg, Object... params) {
-        loggerProxy.doLog(Level.FINEST, msg, params);
-    }
-
-    /**
-     * Abstract base class for logging support, defining the API and common field.
-     */
-    private abstract static class LoggerProxy {
-        final String name;
-
-        protected LoggerProxy(String name) {
-            this.name = name;
-        }
-
-        abstract boolean isEnabled();
-
-        abstract Level getLevel();
-        abstract void setLevel(Level newLevel);
-
-        abstract void doLog(Level level, String msg);
-        abstract void doLog(Level level, String msg, Throwable thrown);
-        abstract void doLog(Level level, String msg, Object... params);
-
-        abstract boolean isLoggable(Level level);
+        loggerProxy.log(Level.FINEST, msg, params);
     }
 
-
-    private static final class DefaultLoggerProxy extends LoggerProxy {
-        /**
-         * Default platform logging support - output messages to System.err -
-         * equivalent to ConsoleHandler with SimpleFormatter.
-         */
-        private static PrintStream outputStream() {
-            return System.err;
-        }
-
-        volatile Level effectiveLevel; // effective level (never null)
-        volatile Level level;          // current level set for this node (may be null)
-
-        DefaultLoggerProxy(String name) {
-            super(name);
-            this.effectiveLevel = deriveEffectiveLevel(null);
-            this.level = null;
-        }
-
-        boolean isEnabled() {
-            return effectiveLevel != Level.OFF;
-        }
-
-        Level getLevel() {
-            return level;
-        }
-
-        void setLevel(Level newLevel) {
-            Level oldLevel = level;
-            if (oldLevel != newLevel) {
-                level = newLevel;
-                effectiveLevel = deriveEffectiveLevel(newLevel);
-            }
-        }
-
-        void doLog(Level level, String msg) {
-            if (isLoggable(level)) {
-                outputStream().print(format(level, msg, null));
-            }
-        }
-
-        void doLog(Level level, String msg, Throwable thrown) {
-            if (isLoggable(level)) {
-                outputStream().print(format(level, msg, thrown));
-            }
-        }
-
-        void doLog(Level level, String msg, Object... params) {
-            if (isLoggable(level)) {
-                String newMsg = formatMessage(msg, params);
-                outputStream().print(format(level, newMsg, null));
-            }
-        }
-
-        boolean isLoggable(Level level) {
-            Level effectiveLevel = this.effectiveLevel;
-            return level.intValue() >= effectiveLevel.intValue() && effectiveLevel != Level.OFF;
-        }
-
-        // derive effective level (could do inheritance search like j.u.l.Logger)
-        private Level deriveEffectiveLevel(Level level) {
-            return level == null ? DEFAULT_LEVEL : level;
-        }
+    // ------------------------------------
+    // Maps used for Level conversion
+    // ------------------------------------
 
-        // Copied from java.util.logging.Formatter.formatMessage
-        private String formatMessage(String format, Object... parameters) {
-            // Do the formatting.
-            try {
-                if (parameters == null || parameters.length == 0) {
-                    // No parameters.  Just return format string.
-                    return format;
-                }
-                // Is it a java.text style format?
-                // Ideally we could match with
-                // Pattern.compile("\\{\\d").matcher(format).find())
-                // However the cost is 14% higher, so we cheaply check for
-                // 1 of the first 4 parameters
-                if (format.indexOf("{0") >= 0 || format.indexOf("{1") >=0 ||
-                            format.indexOf("{2") >=0|| format.indexOf("{3") >=0) {
-                    return java.text.MessageFormat.format(format, parameters);
-                }
-                return format;
-            } catch (Exception ex) {
-                // Formatting failed: use format string.
-                return format;
-            }
-        }
-
-        private static final String formatString =
-            LoggingSupport.getSimpleFormat(false); // don't check logging.properties
-        private final ZoneId zoneId = ZoneId.systemDefault();
-        private synchronized String format(Level level, String msg, Throwable thrown) {
-            ZonedDateTime zdt = ZonedDateTime.now(zoneId);
-            String throwable = "";
-            if (thrown != null) {
-                StringWriter sw = new StringWriter();
-                PrintWriter pw = new PrintWriter(sw);
-                pw.println();
-                thrown.printStackTrace(pw);
-                pw.close();
-                throwable = sw.toString();
-            }
+    // This map is indexed by java.util.spi.Logger.Level.ordinal() and returns
+    // a PlatformLogger.Level
+    //
+    // ALL, TRACE, DEBUG, INFO, WARNING, ERROR, OFF
+    private static final Level[] spi2platformLevelMapping = {
+            Level.ALL,     // mapped from ALL
+            Level.FINER,   // mapped from TRACE
+            Level.FINE,    // mapped from DEBUG
+            Level.INFO,    // mapped from INFO
+            Level.WARNING, // mapped from WARNING
+            Level.SEVERE,  // mapped from ERROR
+            Level.OFF      // mapped from OFF
+    };
 
-            return String.format(formatString,
-                                 zdt,
-                                 getCallerInfo(),
-                                 name,
-                                 level.name(),
-                                 msg,
-                                 throwable);
-        }
-
-        // Returns the caller's class and method's name; best effort
-        // if cannot infer, return the logger's name.
-        private String getCallerInfo() {
-            String sourceClassName = null;
-            String sourceMethodName = null;
-
-            JavaLangAccess access = SharedSecrets.getJavaLangAccess();
-            Throwable throwable = new Throwable();
-            int depth = access.getStackTraceDepth(throwable);
-
-            String logClassName = "sun.util.logging.PlatformLogger";
-            boolean lookingForLogger = true;
-            for (int ix = 0; ix < depth; ix++) {
-                // Calling getStackTraceElement directly prevents the VM
-                // from paying the cost of building the entire stack frame.
-                StackTraceElement frame =
-                    access.getStackTraceElement(throwable, ix);
-                String cname = frame.getClassName();
-                if (lookingForLogger) {
-                    // Skip all frames until we have found the first logger frame.
-                    if (cname.equals(logClassName)) {
-                        lookingForLogger = false;
-                    }
-                } else {
-                    if (!cname.equals(logClassName)) {
-                        // We've found the relevant frame.
-                        sourceClassName = cname;
-                        sourceMethodName = frame.getMethodName();
-                        break;
-                    }
-                }
-            }
-
-            if (sourceClassName != null) {
-                return sourceClassName + " " + sourceMethodName;
-            } else {
-                return name;
-            }
-        }
+    public static Level toPlatformLevel(java.lang.System.Logger.Level level) {
+        if (level == null) return null;
+        assert level.ordinal() < spi2platformLevelMapping.length;
+        return spi2platformLevelMapping[level.ordinal()];
     }
 
-    /**
-     * JavaLoggerProxy forwards all the calls to its corresponding
-     * java.util.logging.Logger object.
-     */
-    private static final class JavaLoggerProxy extends LoggerProxy {
-        // initialize javaLevel fields for mapping from Level enum -> j.u.l.Level object
-        static {
-            for (Level level : Level.values()) {
-                level.javaLevel = LoggingSupport.parseLevel(level.name());
-            }
-        }
-
-        private final /* java.util.logging.Logger */ Object javaLogger;
-
-        JavaLoggerProxy(String name) {
-            this(name, null);
-        }
-
-        JavaLoggerProxy(String name, Level level) {
-            super(name);
-            this.javaLogger = LoggingSupport.getLogger(name);
-            if (level != null) {
-                // level has been updated and so set the Logger's level
-                LoggingSupport.setLevel(javaLogger, level.javaLevel);
-            }
-        }
-
-        void doLog(Level level, String msg) {
-            LoggingSupport.log(javaLogger, level.javaLevel, msg);
-        }
-
-        void doLog(Level level, String msg, Throwable t) {
-            LoggingSupport.log(javaLogger, level.javaLevel, msg, t);
-        }
-
-        void doLog(Level level, String msg, Object... params) {
-            if (!isLoggable(level)) {
-                return;
-            }
-            // only pass String objects to the j.u.l.Logger which may
-            // be created by untrusted code
-            int len = (params != null) ? params.length : 0;
-            Object[] sparams = new String[len];
-            for (int i = 0; i < len; i++) {
-                sparams [i] = String.valueOf(params[i]);
-            }
-            LoggingSupport.log(javaLogger, level.javaLevel, msg, sparams);
-        }
-
-        boolean isEnabled() {
-            return LoggingSupport.isLoggable(javaLogger, Level.OFF.javaLevel);
-        }
-
-        /**
-         * Returns the PlatformLogger.Level mapped from j.u.l.Level
-         * set in the logger.  If the j.u.l.Logger is set to a custom Level,
-         * this method will return the nearest Level.
-         */
-        Level getLevel() {
-            Object javaLevel = LoggingSupport.getLevel(javaLogger);
-            if (javaLevel == null) return null;
-
-            try {
-                return Level.valueOf(LoggingSupport.getLevelName(javaLevel));
-            } catch (IllegalArgumentException e) {
-                return Level.valueOf(LoggingSupport.getLevelValue(javaLevel));
-            }
-        }
-
-        void setLevel(Level level) {
-            LoggingSupport.setLevel(javaLogger, level == null ? null : level.javaLevel);
-        }
-
-        boolean isLoggable(Level level) {
-            return LoggingSupport.isLoggable(javaLogger, level.javaLevel);
-        }
-    }
 }
--- a/jdk/src/java.base/unix/classes/java/lang/ProcessImpl.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/src/java.base/unix/classes/java/lang/ProcessImpl.java	Mon Nov 23 14:37:04 2015 -0500
@@ -224,48 +224,75 @@
         FileOutputStream f2 = null;
 
         try {
+            boolean forceNullOutputStream = false;
             if (redirects == null) {
                 std_fds = new int[] { -1, -1, -1 };
             } else {
                 std_fds = new int[3];
 
-                if (redirects[0] == Redirect.PIPE)
+                if (redirects[0] == Redirect.PIPE) {
                     std_fds[0] = -1;
-                else if (redirects[0] == Redirect.INHERIT)
+                } else if (redirects[0] == Redirect.INHERIT) {
                     std_fds[0] = 0;
-                else {
+                } else if (redirects[0] instanceof ProcessBuilder.RedirectPipeImpl) {
+                    std_fds[0] = fdAccess.get(((ProcessBuilder.RedirectPipeImpl) redirects[0]).getFd());
+                } else {
                     f0 = new FileInputStream(redirects[0].file());
                     std_fds[0] = fdAccess.get(f0.getFD());
                 }
 
-                if (redirects[1] == Redirect.PIPE)
+                if (redirects[1] == Redirect.PIPE) {
                     std_fds[1] = -1;
-                else if (redirects[1] == Redirect.INHERIT)
+                } else if (redirects[1] == Redirect.INHERIT) {
                     std_fds[1] = 1;
-                else {
+                } else if (redirects[1] instanceof ProcessBuilder.RedirectPipeImpl) {
+                    std_fds[1] = fdAccess.get(((ProcessBuilder.RedirectPipeImpl) redirects[1]).getFd());
+                    // Force getInputStream to return a null stream,
+                    // the fd is directly assigned to the next process.
+                    forceNullOutputStream = true;
+                } else {
                     f1 = new FileOutputStream(redirects[1].file(),
                             redirects[1].append());
                     std_fds[1] = fdAccess.get(f1.getFD());
                 }
 
-                if (redirects[2] == Redirect.PIPE)
+                if (redirects[2] == Redirect.PIPE) {
                     std_fds[2] = -1;
-                else if (redirects[2] == Redirect.INHERIT)
+                } else if (redirects[2] == Redirect.INHERIT) {
                     std_fds[2] = 2;
-                else {
+                } else if (redirects[2] instanceof ProcessBuilder.RedirectPipeImpl) {
+                    std_fds[2] = fdAccess.get(((ProcessBuilder.RedirectPipeImpl) redirects[2]).getFd());
+                } else {
                     f2 = new FileOutputStream(redirects[2].file(),
                             redirects[2].append());
                     std_fds[2] = fdAccess.get(f2.getFD());
                 }
             }
 
-            return new ProcessImpl
+            Process p = new ProcessImpl
                     (toCString(cmdarray[0]),
                             argBlock, args.length,
                             envBlock, envc[0],
                             toCString(dir),
                             std_fds,
+                            forceNullOutputStream,
                             redirectErrorStream);
+            if (redirects != null) {
+                // Copy the fd's if they are to be redirected to another process
+                if (std_fds[0] >= 0 &&
+                        redirects[0] instanceof ProcessBuilder.RedirectPipeImpl) {
+                    fdAccess.set(((ProcessBuilder.RedirectPipeImpl) redirects[0]).getFd(), std_fds[0]);
+                }
+                if (std_fds[1] >= 0 &&
+                        redirects[1] instanceof ProcessBuilder.RedirectPipeImpl) {
+                    fdAccess.set(((ProcessBuilder.RedirectPipeImpl) redirects[1]).getFd(), std_fds[1]);
+                }
+                if (std_fds[2] >= 0 &&
+                        redirects[2] instanceof ProcessBuilder.RedirectPipeImpl) {
+                    fdAccess.set(((ProcessBuilder.RedirectPipeImpl) redirects[2]).getFd(), std_fds[2]);
+                }
+            }
+            return p;
         } finally {
             // In theory, close() can throw IOException
             // (although it is rather unlikely to happen here)
@@ -311,6 +338,7 @@
                 final byte[] envBlock, final int envc,
                 final byte[] dir,
                 final int[] fds,
+                final boolean forceNullOutputStream,
                 final boolean redirectErrorStream)
             throws IOException {
 
@@ -326,7 +354,7 @@
 
         try {
             doPrivileged((PrivilegedExceptionAction<Void>) () -> {
-                initStreams(fds);
+                initStreams(fds, forceNullOutputStream);
                 return null;
             });
         } catch (PrivilegedActionException ex) {
@@ -340,7 +368,14 @@
         return fileDescriptor;
     }
 
-    void initStreams(int[] fds) throws IOException {
+    /**
+     * Initialize the streams from the file descriptors.
+     * @param fds array of stdin, stdout, stderr fds
+     * @param forceNullOutputStream true if the stdout is being directed to
+     *        a subsequent process. The stdout stream should be a null output stream .
+     * @throws IOException
+     */
+    void initStreams(int[] fds, boolean forceNullOutputStream) throws IOException {
         switch (platform) {
             case LINUX:
             case BSD:
@@ -348,7 +383,7 @@
                         ProcessBuilder.NullOutputStream.INSTANCE :
                         new ProcessPipeOutputStream(fds[0]);
 
-                stdout = (fds[1] == -1) ?
+                stdout = (fds[1] == -1 || forceNullOutputStream) ?
                          ProcessBuilder.NullInputStream.INSTANCE :
                          new ProcessPipeInputStream(fds[1]);
 
--- a/jdk/src/java.base/windows/classes/java/lang/ProcessImpl.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/src/java.base/windows/classes/java/lang/ProcessImpl.java	Mon Nov 23 14:37:04 2015 -0500
@@ -110,38 +110,63 @@
             } else {
                 stdHandles = new long[3];
 
-                if (redirects[0] == Redirect.PIPE)
+                if (redirects[0] == Redirect.PIPE) {
                     stdHandles[0] = -1L;
-                else if (redirects[0] == Redirect.INHERIT)
+                } else if (redirects[0] == Redirect.INHERIT) {
                     stdHandles[0] = fdAccess.getHandle(FileDescriptor.in);
-                else {
+                } else if (redirects[0] instanceof ProcessBuilder.RedirectPipeImpl) {
+                    stdHandles[0] = fdAccess.getHandle(((ProcessBuilder.RedirectPipeImpl) redirects[0]).getFd());
+                } else {
                     f0 = new FileInputStream(redirects[0].file());
                     stdHandles[0] = fdAccess.getHandle(f0.getFD());
                 }
 
-                if (redirects[1] == Redirect.PIPE)
+                if (redirects[1] == Redirect.PIPE) {
                     stdHandles[1] = -1L;
-                else if (redirects[1] == Redirect.INHERIT)
+                } else if (redirects[1] == Redirect.INHERIT) {
                     stdHandles[1] = fdAccess.getHandle(FileDescriptor.out);
-                else {
+                } else if (redirects[1] instanceof ProcessBuilder.RedirectPipeImpl) {
+                    stdHandles[1] = fdAccess.getHandle(((ProcessBuilder.RedirectPipeImpl) redirects[1]).getFd());
+                } else {
                     f1 = newFileOutputStream(redirects[1].file(),
                                              redirects[1].append());
                     stdHandles[1] = fdAccess.getHandle(f1.getFD());
                 }
 
-                if (redirects[2] == Redirect.PIPE)
+                if (redirects[2] == Redirect.PIPE) {
                     stdHandles[2] = -1L;
-                else if (redirects[2] == Redirect.INHERIT)
+                } else if (redirects[2] == Redirect.INHERIT) {
                     stdHandles[2] = fdAccess.getHandle(FileDescriptor.err);
-                else {
+                } else if (redirects[2] instanceof ProcessBuilder.RedirectPipeImpl) {
+                    stdHandles[2] = fdAccess.getHandle(((ProcessBuilder.RedirectPipeImpl) redirects[2]).getFd());
+                } else {
                     f2 = newFileOutputStream(redirects[2].file(),
                                              redirects[2].append());
                     stdHandles[2] = fdAccess.getHandle(f2.getFD());
                 }
             }
 
-            return new ProcessImpl(cmdarray, envblock, dir,
+            Process p = new ProcessImpl(cmdarray, envblock, dir,
                                    stdHandles, redirectErrorStream);
+            if (redirects != null) {
+                // Copy the handles's if they are to be redirected to another process
+                if (stdHandles[0] >= 0
+                        && redirects[0] instanceof ProcessBuilder.RedirectPipeImpl) {
+                    fdAccess.setHandle(((ProcessBuilder.RedirectPipeImpl) redirects[0]).getFd(),
+                            stdHandles[0]);
+                }
+                if (stdHandles[1] >= 0
+                        && redirects[1] instanceof ProcessBuilder.RedirectPipeImpl) {
+                    fdAccess.setHandle(((ProcessBuilder.RedirectPipeImpl) redirects[1]).getFd(),
+                            stdHandles[1]);
+                }
+                if (stdHandles[2] >= 0
+                        && redirects[2] instanceof ProcessBuilder.RedirectPipeImpl) {
+                    fdAccess.setHandle(((ProcessBuilder.RedirectPipeImpl) redirects[2]).getFd(),
+                            stdHandles[2]);
+                }
+            }
+            return p;
         } finally {
             // In theory, close() can throw IOException
             // (although it is rather unlikely to happen here)
--- a/jdk/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTView.m	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/src/java.desktop/macosx/native/libawt_lwawt/awt/AWTView.m	Mon Nov 23 14:37:04 2015 -0500
@@ -42,6 +42,7 @@
 -(void) deliverResize: (NSRect) rect;
 -(void) resetTrackingArea;
 -(void) deliverJavaKeyEventHelper: (NSEvent*) event;
+-(BOOL) isCodePointInUnicodeBlockNeedingIMEvent: (unichar) codePoint;
 @end
 
 // Uncomment this line to see fprintfs of each InputMethod API being called on this View
@@ -509,6 +510,14 @@
     }
 }
 
+-(BOOL) isCodePointInUnicodeBlockNeedingIMEvent: (unichar) codePoint {
+    if ((codePoint >= 0x3000) && (codePoint <= 0x303F)) {
+        // Code point is in 'CJK Symbols and Punctuation' Unicode block.
+        return YES;
+    }
+    return NO;
+}
+
 // NSAccessibility support
 - (jobject)awtComponent:(JNIEnv*)env
 {
@@ -889,8 +898,14 @@
     // (i.e., when the user uses the Character palette or Inkwell), or when the string to insert is a complex
     // Unicode value.
     NSUInteger utf16Length = [aString lengthOfBytesUsingEncoding:NSUTF16StringEncoding];
+    NSUInteger utf8Length = [aString lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
+    BOOL aStringIsComplex = NO;
+    if ((utf16Length > 2) ||
+        ((utf8Length > 1) && [self isCodePointInUnicodeBlockNeedingIMEvent:[aString characterAtIndex:0]])) {
+        aStringIsComplex = YES;
+    }
 
-    if ([self hasMarkedText] || !fProcessingKeystroke || (utf16Length > 2)) {
+    if ([self hasMarkedText] || !fProcessingKeystroke || aStringIsComplex) {
         JNIEnv *env = [ThreadUtilities getJNIEnv];
 
         static JNF_MEMBER_CACHE(jm_selectPreviousGlyph, jc_CInputMethod, "selectPreviousGlyph", "()V");
--- a/jdk/src/java.desktop/macosx/native/libawt_lwawt/font/AWTFont.m	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/src/java.desktop/macosx/native/libawt_lwawt/font/AWTFont.m	Mon Nov 23 14:37:04 2015 -0500
@@ -307,6 +307,8 @@
 
         JNFCallVoidMethod(env, jthis,
                           jm_registerFont, jFontName, jFontFamilyName);
+        (*env)->DeleteLocalRef(env, jFontName);
+        (*env)->DeleteLocalRef(env, jFontFamilyName);
     }
 
 JNF_COCOA_EXIT(env);
--- a/jdk/src/java.desktop/share/classes/com/sun/media/sound/SoftSynthesizer.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/src/java.desktop/share/classes/com/sun/media/sound/SoftSynthesizer.java	Mon Nov 23 14:37:04 2015 -0500
@@ -671,6 +671,40 @@
             actions.add(new PrivilegedAction<InputStream>() {
                 public InputStream run() {
                     if (System.getProperties().getProperty("os.name")
+                            .startsWith("Linux")) {
+
+                        File[] systemSoundFontsDir = new File[] {
+                            /* Arch, Fedora, Mageia */
+                            new File("/usr/share/soundfonts/"),
+                            new File("/usr/local/share/soundfonts/"),
+                            /* Debian, Gentoo, OpenSUSE, Ubuntu */
+                            new File("/usr/share/sounds/sf2/"),
+                            new File("/usr/local/share/sounds/sf2/"),
+                        };
+
+                        /*
+                         * Look for a default.sf2
+                         */
+                        for (File systemSoundFontDir : systemSoundFontsDir) {
+                            if (systemSoundFontDir.exists()) {
+                                File defaultSoundFont = new File(systemSoundFontDir, "default.sf2");
+                                if (defaultSoundFont.exists()) {
+                                    try {
+                                        return new FileInputStream(defaultSoundFont);
+                                    } catch (IOException e) {
+                                        // continue with lookup
+                                    }
+                                }
+                            }
+                        }
+                    }
+                    return null;
+                }
+            });
+
+            actions.add(new PrivilegedAction<InputStream>() {
+                public InputStream run() {
+                    if (System.getProperties().getProperty("os.name")
                             .startsWith("Windows")) {
                         File gm_dls = new File(System.getenv("SystemRoot")
                                 + "\\system32\\drivers\\gm.dls");
--- a/jdk/src/java.desktop/share/classes/java/awt/KeyboardFocusManager.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/src/java.desktop/share/classes/java/awt/KeyboardFocusManager.java	Mon Nov 23 14:37:04 2015 -0500
@@ -325,23 +325,6 @@
     };
 
     /**
-     * The default strokes for initializing the default focus traversal keys.
-     */
-    private static final AWTKeyStroke[][] defaultFocusTraversalKeyStrokes = {
-        {
-            AWTKeyStroke.getAWTKeyStroke(KeyEvent.VK_TAB, 0, false),
-            AWTKeyStroke.getAWTKeyStroke(KeyEvent.VK_TAB, InputEvent.CTRL_DOWN_MASK | InputEvent.CTRL_MASK, false),
-        },
-        {
-            AWTKeyStroke.getAWTKeyStroke(KeyEvent.VK_TAB, InputEvent.SHIFT_DOWN_MASK | InputEvent.SHIFT_MASK, false),
-            AWTKeyStroke.getAWTKeyStroke(KeyEvent.VK_TAB,
-                                         InputEvent.SHIFT_DOWN_MASK | InputEvent.SHIFT_MASK | InputEvent.CTRL_DOWN_MASK | InputEvent.CTRL_MASK,
-                                         false),
-        },
-        {},
-        {},
-      };
-    /**
      * The default focus traversal keys. Each array of traversal keys will be
      * in effect on all Windows that have no such array of their own explicitly
      * set. Each array will also be inherited, recursively, by any child
@@ -431,6 +414,27 @@
      * Initializes a KeyboardFocusManager.
      */
     public KeyboardFocusManager() {
+        AWTKeyStroke[][] defaultFocusTraversalKeyStrokes = {
+                {
+                        AWTKeyStroke.getAWTKeyStroke(KeyEvent.VK_TAB, 0, false),
+                        AWTKeyStroke.getAWTKeyStroke(KeyEvent.VK_TAB,
+                                InputEvent.CTRL_DOWN_MASK |
+                                        InputEvent.CTRL_MASK, false),
+                },
+                {
+                        AWTKeyStroke.getAWTKeyStroke(KeyEvent.VK_TAB,
+                                InputEvent.SHIFT_DOWN_MASK |
+                                        InputEvent.SHIFT_MASK, false),
+                        AWTKeyStroke.getAWTKeyStroke(KeyEvent.VK_TAB,
+                                InputEvent.SHIFT_DOWN_MASK |
+                                        InputEvent.SHIFT_MASK |
+                                        InputEvent.CTRL_DOWN_MASK |
+                                        InputEvent.CTRL_MASK,
+                                false),
+                },
+                {},
+                {},
+        };
         for (int i = 0; i < TRAVERSAL_KEY_LENGTH; i++) {
             Set<AWTKeyStroke> work_set = new HashSet<>();
             for (int j = 0; j < defaultFocusTraversalKeyStrokes[i].length; j++) {
--- a/jdk/src/java.desktop/share/classes/javax/swing/plaf/nimbus/AbstractRegionPainter.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/src/java.desktop/share/classes/javax/swing/plaf/nimbus/AbstractRegionPainter.java	Mon Nov 23 14:37:04 2015 -0500
@@ -640,10 +640,6 @@
         // check if we can scale to the requested size
         Dimension canvas = ctx.canvasSize;
         Insets insets = ctx.stretchingInsets;
-        if (insets.left + insets.right > w || insets.top + insets.bottom > h) {
-            return;
-        }
-
         if (w <= (canvas.width * ctx.maxHorizontalScaleFactor) && h <= (canvas.height * ctx.maxVerticalScaleFactor)) {
             // get image at canvas size
             VolatileImage img = getImage(g.getDeviceConfiguration(), c, canvas.width, canvas.height, extendedCacheKeys);
--- a/jdk/src/java.desktop/share/classes/javax/swing/plaf/nimbus/skin.laf	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/src/java.desktop/share/classes/javax/swing/plaf/nimbus/skin.laf	Mon Nov 23 14:37:04 2015 -0500
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 
 <!--
- Copyright (c) 1998, 2014, Oracle and/or its affiliates. All rights reserved.
+ Copyright (c) 1998, 2015, Oracle and/or its affiliates. All rights reserved.
  DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 
  This code is free software; you can redistribute it and/or modify it
@@ -16195,6 +16195,10 @@
                   <dimension width="150" height="19"/>
                </uiProperty>
                <uiProperty name="cycleTime" type="INT" value="250"/>
+               <uiProperty name="minBarSize" type="DIMENSION">
+                  <dimension width="6" height="6"/>
+               </uiProperty>
+               <uiProperty name="glowWidth" type="INT" value="2"/>
             </uiproperties>
          </style>
          <backgroundStates>
@@ -16351,7 +16355,7 @@
                <canvas>
                   <size width="27" height="19"/>
                   <nextLayerNameIndex>2</nextLayerNameIndex>
-                  <stretchingInsets top="5" bottom="5" left="5" right="5"/>
+                  <stretchingInsets top="3" bottom="3" left="3" right="3"/>
                   <layer name="Layer 1">
                      <opacity>1.0</opacity>
                      <fillOpacity>1.0</fillOpacity>
@@ -16444,7 +16448,7 @@
                <canvas>
                   <size width="27" height="19"/>
                   <nextLayerNameIndex>2</nextLayerNameIndex>
-                  <stretchingInsets top="5" bottom="5" left="5" right="5"/>
+                  <stretchingInsets top="3" bottom="3" left="3" right="3"/>
                   <layer name="Layer 1">
                      <opacity>1.0</opacity>
                      <fillOpacity>1.0</fillOpacity>
@@ -16528,7 +16532,7 @@
                <canvas>
                   <size width="30" height="13"/>
                   <nextLayerNameIndex>2</nextLayerNameIndex>
-                  <stretchingInsets top="5" bottom="5" left="5" right="5"/>
+                  <stretchingInsets top="3" bottom="3" left="3" right="3"/>
                   <layer name="Layer 1">
                      <opacity>1.0</opacity>
                      <fillOpacity>1.0</fillOpacity>
@@ -16619,7 +16623,7 @@
                <canvas>
                   <size width="27" height="19"/>
                   <nextLayerNameIndex>2</nextLayerNameIndex>
-                  <stretchingInsets top="5" bottom="5" left="5" right="5"/>
+                  <stretchingInsets top="3" bottom="3" left="3" right="3"/>
                   <layer name="Layer 1">
                      <opacity>1.0</opacity>
                      <fillOpacity>1.0</fillOpacity>
@@ -16701,7 +16705,7 @@
                <canvas>
                   <size width="27" height="19"/>
                   <nextLayerNameIndex>2</nextLayerNameIndex>
-                  <stretchingInsets top="5" bottom="5" left="5" right="5"/>
+                  <stretchingInsets top="3" bottom="3" left="3" right="3"/>
                   <layer name="Layer 1">
                      <opacity>1.0</opacity>
                      <fillOpacity>1.0</fillOpacity>
@@ -16785,7 +16789,7 @@
                <canvas>
                   <size width="30" height="13"/>
                   <nextLayerNameIndex>2</nextLayerNameIndex>
-                  <stretchingInsets top="5" bottom="5" left="5" right="5"/>
+                  <stretchingInsets top="3" bottom="3" left="3" right="3"/>
                   <layer name="Layer 1">
                      <opacity>1.0</opacity>
                      <fillOpacity>1.0</fillOpacity>
--- a/jdk/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthProgressBarUI.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/src/java.desktop/share/classes/javax/swing/plaf/synth/SynthProgressBarUI.java	Mon Nov 23 14:37:04 2015 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -49,6 +49,8 @@
     private boolean paintOutsideClip;
     private boolean tileWhenIndeterminate; //whether to tile indeterminate painting
     private int tileWidth; //the width of each tile
+    private Dimension minBarSize; // minimal visible bar size
+    private int glowWidth; // Glow around the bar foreground
 
     /**
      * Creates a new UI object for the given component.
@@ -114,6 +116,8 @@
                 tileWidth *= 0.784;
             }
         }
+        minBarSize = (Dimension)style.get(context, "ProgressBar.minBarSize");
+        glowWidth = style.getInt(context, "ProgressBar.glowWidth", 0);
         context.dispose();
     }
 
@@ -258,7 +262,7 @@
 
                     if (!SynthLookAndFeel.isLeftToRight(pBar)) {
                         x = pBar.getWidth() - pBarInsets.right - width
-                                - progressPadding;
+                                - progressPadding - glowWidth;
                     }
                 } else {  // JProgressBar.VERTICAL
                     x = pBarInsets.left + progressPadding;
@@ -271,9 +275,9 @@
                     y = pBar.getHeight() - pBarInsets.bottom - height
                             - progressPadding;
 
-                    // When the progress bar is vertical we always paint
-                    // from bottom to top, not matter what the component
-                    // orientation is.
+                    if (SynthLookAndFeel.isLeftToRight(pBar)) {
+                        y -= glowWidth;
+                    }
                 }
             }
         } else {
@@ -307,8 +311,11 @@
             }
             g.setClip(clip);
         } else {
-            context.getPainter().paintProgressBarForeground(context, g,
-                    x, y, width, height, pBar.getOrientation());
+            if (minBarSize == null || (width >= minBarSize.width
+                    && height >= minBarSize.height)) {
+                context.getPainter().paintProgressBarForeground(context, g,
+                        x, y, width, height, pBar.getOrientation());
+            }
         }
 
         if (pBar.isStringPainted()) {
--- a/jdk/src/java.desktop/share/classes/sun/awt/KeyboardFocusManagerPeerImpl.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/src/java.desktop/share/classes/sun/awt/KeyboardFocusManagerPeerImpl.java	Mon Nov 23 14:37:04 2015 -0500
@@ -42,8 +42,10 @@
 
     private static final PlatformLogger focusLog = PlatformLogger.getLogger("sun.awt.focus.KeyboardFocusManagerPeerImpl");
 
-    private static AWTAccessor.KeyboardFocusManagerAccessor kfmAccessor =
-        AWTAccessor.getKeyboardFocusManagerAccessor();
+    private static class KfmAccessor {
+        private static AWTAccessor.KeyboardFocusManagerAccessor instance =
+                AWTAccessor.getKeyboardFocusManagerAccessor();
+    }
 
     // The constants are copied from java.awt.KeyboardFocusManager
     public static final int SNFH_FAILURE         = 0;
@@ -152,12 +154,13 @@
                                                      long time,
                                                      CausedFocusEvent.Cause cause)
     {
-        return kfmAccessor.shouldNativelyFocusHeavyweight(
-            heavyweight, descendant, temporary, focusedWindowChangeAllowed, time, cause);
+        return KfmAccessor.instance.shouldNativelyFocusHeavyweight(
+            heavyweight, descendant, temporary, focusedWindowChangeAllowed,
+                time, cause);
     }
 
     public static void removeLastFocusRequest(Component heavyweight) {
-        kfmAccessor.removeLastFocusRequest(heavyweight);
+        KfmAccessor.instance.removeLastFocusRequest(heavyweight);
     }
 
     // WARNING: Don't call it on the Toolkit thread.
@@ -167,7 +170,8 @@
                                                                 boolean focusedWindowChangeAllowed,
                                                                 long time)
     {
-        return kfmAccessor.processSynchronousLightweightTransfer(
-            heavyweight, descendant, temporary, focusedWindowChangeAllowed, time);
+        return KfmAccessor.instance.processSynchronousLightweightTransfer(
+            heavyweight, descendant, temporary, focusedWindowChangeAllowed,
+                time);
     }
 }
--- a/jdk/src/java.desktop/share/classes/sun/awt/OSInfo.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/src/java.desktop/share/classes/sun/awt/OSInfo.java	Mon Nov 23 14:37:04 2015 -0500
@@ -71,7 +71,7 @@
         windowsVersionMap.put(WINDOWS_XP.toString(), WINDOWS_XP);
         windowsVersionMap.put(WINDOWS_2003.toString(), WINDOWS_2003);
         windowsVersionMap.put(WINDOWS_VISTA.toString(), WINDOWS_VISTA);
-        windowsVersionMap.put(WINDOWS_VISTA.toString(), WINDOWS_7);
+        windowsVersionMap.put(WINDOWS_7.toString(), WINDOWS_7);
     }
 
     private static final PrivilegedAction<OSType> osTypeAction = new PrivilegedAction<OSType>() {
--- a/jdk/src/java.desktop/share/classes/sun/font/FontUtilities.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/src/java.desktop/share/classes/sun/font/FontUtilities.java	Mon Nov 23 14:37:04 2015 -0500
@@ -72,6 +72,8 @@
     static {
 
         AccessController.doPrivileged(new PrivilegedAction<Object>() {
+            @SuppressWarnings("deprecation") // PlatformLogger.setLevel is deprecated.
+            @Override
             public Object run() {
                 String osName = System.getProperty("os.name", "unknownOS");
                 isSolaris = osName.startsWith("SunOS");
--- a/jdk/src/java.desktop/share/classes/sun/print/PrintJob2D.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/src/java.desktop/share/classes/sun/print/PrintJob2D.java	Mon Nov 23 14:37:04 2015 -0500
@@ -631,7 +631,7 @@
 
         String printerName = jobAttributes.getPrinter();
         if (printerName != null && printerName != ""
-            && !printerName.equals(pServ.getName())) {
+            && pServ != null && !printerName.equals(pServ.getName())) {
 
             // Search for the given printerName in the list of PrintServices
             PrintService []services = PrinterJob.lookupPrintServices();
@@ -648,7 +648,7 @@
         }
 
         DestinationType dest = jobAttributes.getDestination();
-        if (dest == DestinationType.FILE &&
+        if (dest == DestinationType.FILE && pServ != null &&
             pServ.isAttributeCategorySupported(Destination.class)) {
 
             String fileName = jobAttributes.getFileName();
--- a/jdk/src/java.desktop/share/native/libfontmanager/freetypeScaler.c	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/src/java.desktop/share/native/libfontmanager/freetypeScaler.c	Mon Nov 23 14:37:04 2015 -0500
@@ -103,7 +103,6 @@
 }
 
 static void freeNativeResources(JNIEnv *env, FTScalerInfo* scalerInfo) {
-    void *stream;
 
     if (scalerInfo == NULL)
         return;
--- a/jdk/src/java.desktop/unix/classes/sun/print/IPPPrintService.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/src/java.desktop/unix/classes/sun/print/IPPPrintService.java	Mon Nov 23 14:37:04 2015 -0500
@@ -321,7 +321,11 @@
         if ((name == null) || (url == null)){
             throw new IllegalArgumentException("null uri or printer name");
         }
-        printer = name;
+        try {
+            printer = java.net.URLDecoder.decode(name, "UTF-8");
+        } catch (java.io.UnsupportedEncodingException e) {
+            printer = name;
+        }
         supportedDocFlavors = null;
         supportedCats = null;
         mediaSizeNames = null;
@@ -351,7 +355,11 @@
         if ((name == null) || (uriStr == null)){
             throw new IllegalArgumentException("null uri or printer name");
         }
-        printer = name;
+        try {
+            printer = java.net.URLDecoder.decode(name, "UTF-8");
+        } catch (java.io.UnsupportedEncodingException e) {
+            printer = name;
+        }
         supportedDocFlavors = null;
         supportedCats = null;
         mediaSizeNames = null;
--- a/jdk/src/java.desktop/unix/native/common/awt/fontpath.c	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/src/java.desktop/unix/native/common/awt/fontpath.c	Mon Nov 23 14:37:04 2015 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1998, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1998, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -178,6 +178,7 @@
                                                        "()Z");
         JNU_CHECK_EXCEPTION_RETURN(env, JNI_FALSE);
         isLocal = (*env)->CallBooleanMethod(env, ge, isDisplayLocal);
+        JNU_CHECK_EXCEPTION_RETURN(env, JNI_FALSE);
       } else {
         isLocal = True;
       }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.logging/share/classes/META-INF/services/jdk.internal.logger.DefaultLoggerFinder	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,1 @@
+sun.util.logging.internal.LoggingProviderImpl
--- a/jdk/src/java.logging/share/classes/java/util/logging/LogManager.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/src/java.logging/share/classes/java/util/logging/LogManager.java	Mon Nov 23 14:37:04 2015 -0500
@@ -43,6 +43,7 @@
 import jdk.internal.misc.JavaAWTAccess;
 import jdk.internal.misc.SharedSecrets;
 import sun.misc.ManagedLocalsThread;
+import sun.util.logging.internal.LoggingProviderImpl;
 
 /**
  * There is a single global LogManager object that is used to
@@ -436,7 +437,8 @@
                 readConfiguration();
 
                 // Platform loggers begin to delegate to java.util.logging.Logger
-                sun.util.logging.PlatformLogger.redirectPlatformLoggers();
+                jdk.internal.logger.BootstrapLogger.redirectTemporaryLoggers();
+
             } catch (Exception ex) {
                 assert false : "Exception raised while reading logging configuration: " + ex;
             }
@@ -1481,7 +1483,7 @@
      * <p>
      * Any {@linkplain #addConfigurationListener registered configuration
      * listener} will be invoked after the properties are read.
-     * <p>
+     *
      * @apiNote This {@code readConfiguration} method should only be used for
      * initializing the configuration during LogManager initialization or
      * used with the "java.util.logging.config.class" property.
@@ -2363,7 +2365,8 @@
         }
     }
 
-    static final Permission controlPermission = new LoggingPermission("control", null);
+    static final Permission controlPermission =
+            new LoggingPermission("control", null);
 
     void checkPermission() {
         SecurityManager sm = System.getSecurityManager();
@@ -2607,4 +2610,69 @@
         if (t instanceof RuntimeException) throw (RuntimeException)t;
     }
 
+    /**
+     * This class allows the {@link LoggingProviderImpl} to demand loggers on
+     * behalf of system and application classes.
+     */
+    private static final class LoggingProviderAccess
+        implements LoggingProviderImpl.LogManagerAccess,
+                   PrivilegedAction<Void> {
+
+        private LoggingProviderAccess() {
+        }
+
+        /**
+         * Demands a logger on behalf of the given {@code caller}.
+         * <p>
+         * If a named logger suitable for the given caller is found
+         * returns it.
+         * Otherwise, creates a new logger suitable for the given caller.
+         *
+         * @param name   The logger name.
+         * @param caller The caller on which behalf the logger is created/retrieved.
+         * @return A logger for the given {@code caller}.
+         *
+         * @throws NullPointerException if {@code name} is {@code null}
+         *         or {@code caller} is {@code null}.
+         * @throws IllegalArgumentException if {@code manager} is not the default
+         *         LogManager.
+         * @throws SecurityException if a security manager is present and the
+         *         calling code doesn't have the
+         *        {@link LoggingPermission LoggingPermission("demandLogger", null)}.
+         */
+        @Override
+        public Logger demandLoggerFor(LogManager manager, String name, /* Module */ Class<?> caller) {
+            if (manager != getLogManager()) {
+                // having LogManager as parameter just ensures that the
+                // caller will have initialized the LogManager before reaching
+                // here.
+                throw new IllegalArgumentException("manager");
+            }
+            Objects.requireNonNull(name);
+            SecurityManager sm = System.getSecurityManager();
+            if (sm != null) {
+                sm.checkPermission(controlPermission);
+            }
+            if (caller.getClassLoader() == null) {
+                return manager.demandSystemLogger(name,
+                    Logger.SYSTEM_LOGGER_RB_NAME, caller);
+            } else {
+                return manager.demandLogger(name, null, caller);
+            }
+        }
+
+        @Override
+        public Void run() {
+            LoggingProviderImpl.setLogManagerAccess(INSTANCE);
+            return null;
+        }
+
+        static final LoggingProviderAccess INSTANCE = new LoggingProviderAccess();
+    }
+
+    static {
+        AccessController.doPrivileged(LoggingProviderAccess.INSTANCE, null,
+                                      controlPermission);
+    }
+
 }
--- a/jdk/src/java.logging/share/classes/java/util/logging/LogRecord.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/src/java.logging/share/classes/java/util/logging/LogRecord.java	Mon Nov 23 14:37:04 2015 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -33,6 +33,7 @@
 
 import jdk.internal.misc.JavaLangAccess;
 import jdk.internal.misc.SharedSecrets;
+import static jdk.internal.logger.SimpleConsoleLogger.skipLoggingFrame;
 
 /**
  * LogRecord objects are used to pass logging requests between
@@ -637,6 +638,27 @@
     }
 
     // Private method to infer the caller's class and method names
+    //
+    // Note:
+    // For testing purposes - it is possible to customize the process
+    // by which LogRecord will infer the source class name and source method name
+    // when analyzing the call stack.
+    // <p>
+    // The system property {@code jdk.logger.packages} can define a comma separated
+    // list of strings corresponding to additional package name prefixes that
+    // should be ignored when trying to infer the source caller class name.
+    // Those stack frames whose {@linkplain StackTraceElement#getClassName()
+    // declaring class name} start with one such prefix will be ignored.
+    // <p>
+    // This is primarily useful when providing utility logging classes wrapping
+    // a logger instance, as it makes it possible to instruct LogRecord to skip
+    // those utility frames when inferring the caller source class name.
+    // <p>
+    // The {@code jdk.logger.packages} system property is consulted only once.
+    // <p>
+    // This property is not standard, implementation specific, and yet
+    // undocumented (and thus subject to changes without notice).
+    //
     private void inferCaller() {
         needToInferCaller = false;
         JavaLangAccess access = SharedSecrets.getJavaLangAccess();
@@ -658,8 +680,8 @@
                 }
             } else {
                 if (!isLoggerImpl) {
-                    // skip reflection call
-                    if (!cname.startsWith("java.lang.reflect.") && !cname.startsWith("sun.reflect.")) {
+                    // skip logging/logger infrastructure and reflection calls
+                    if (!skipLoggingFrame(cname)) {
                        // We've found the relevant frame.
                        setSourceClassName(cname);
                        setSourceMethodName(frame.getMethodName());
@@ -675,7 +697,6 @@
     private boolean isLoggerImplFrame(String cname) {
         // the log record could be created for a platform logger
         return (cname.equals("java.util.logging.Logger") ||
-                cname.startsWith("java.util.logging.LoggingProxyImpl") ||
-                cname.startsWith("sun.util.logging."));
+                cname.startsWith("sun.util.logging.PlatformLogger"));
     }
 }
--- a/jdk/src/java.logging/share/classes/java/util/logging/Logger.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/src/java.logging/share/classes/java/util/logging/Logger.java	Mon Nov 23 14:37:04 2015 -0500
@@ -447,8 +447,7 @@
 
     private static Logger demandLogger(String name, String resourceBundleName, Class<?> caller) {
         LogManager manager = LogManager.getLogManager();
-        SecurityManager sm = System.getSecurityManager();
-        if (sm != null && !SystemLoggerHelper.disableCallerCheck) {
+        if (!SystemLoggerHelper.disableCallerCheck) {
             if (caller.getClassLoader() == null) {
                 return manager.demandSystemLogger(name, resourceBundleName, caller);
             }
@@ -1254,14 +1253,14 @@
      * with an optional list of message parameters.
      * <p>
      * If the logger is currently enabled for the given message
-     * level then a corresponding LogRecord is created and forwarded
-     * to all the registered output Handler objects.
+     * {@code level} then a corresponding {@code LogRecord} is created and
+     * forwarded to all the registered output {@code Handler} objects.
      * <p>
      * The {@code msg} string is localized using the given resource bundle.
      * If the resource bundle is {@code null}, then the {@code msg} string is not
      * localized.
      *
-     * @param   level   One of the message level identifiers, e.g., SEVERE
+     * @param   level   One of the message level identifiers, e.g., {@code SEVERE}
      * @param   sourceClass    Name of the class that issued the logging request
      * @param   sourceMethod   Name of the method that issued the logging request
      * @param   bundle         Resource bundle to localize {@code msg},
@@ -1285,6 +1284,36 @@
     }
 
     /**
+     * Log a message, specifying source class, method, and resource bundle,
+     * with an optional list of message parameters.
+     * <p>
+     * If the logger is currently enabled for the given message
+     * {@code level} then a corresponding {@code LogRecord} is created
+     * and forwarded to all the registered output {@code Handler} objects.
+     * <p>
+     * The {@code msg} string is localized using the given resource bundle.
+     * If the resource bundle is {@code null}, then the {@code msg} string is not
+     * localized.
+     * <p>
+     * @param   level   One of the message level identifiers, e.g., {@code SEVERE}
+     * @param   bundle  Resource bundle to localize {@code msg};
+     *                  can be {@code null}.
+     * @param   msg     The string message (or a key in the message catalog)
+     * @param   params  Parameters to the message (optional, may be none).
+     * @since 1.9
+     */
+    public void logrb(Level level, ResourceBundle bundle, String msg, Object... params) {
+        if (!isLoggable(level)) {
+            return;
+        }
+        LogRecord lr = new LogRecord(level, msg);
+        if (params != null && params.length != 0) {
+            lr.setParameters(params);
+        }
+        doLog(lr, bundle);
+    }
+
+    /**
      * Log a message, specifying source class, method, and resource bundle name,
      * with associated Throwable information.
      * <p>
@@ -1330,19 +1359,20 @@
      * with associated Throwable information.
      * <p>
      * If the logger is currently enabled for the given message
-     * level then the given arguments are stored in a LogRecord
+     * {@code level} then the given arguments are stored in a {@code LogRecord}
      * which is forwarded to all registered output handlers.
      * <p>
      * The {@code msg} string is localized using the given resource bundle.
      * If the resource bundle is {@code null}, then the {@code msg} string is not
      * localized.
      * <p>
-     * Note that the thrown argument is stored in the LogRecord thrown
-     * property, rather than the LogRecord parameters property.  Thus it is
-     * processed specially by output Formatters and is not treated
-     * as a formatting parameter to the LogRecord message property.
+     * Note that the {@code thrown} argument is stored in the {@code LogRecord}
+     * {@code thrown} property, rather than the {@code LogRecord}
+     * {@code parameters} property.  Thus it is
+     * processed specially by output {@code Formatter} objects and is not treated
+     * as a formatting parameter to the {@code LogRecord} {@code message} property.
      *
-     * @param   level   One of the message level identifiers, e.g., SEVERE
+     * @param   level   One of the message level identifiers, e.g., {@code SEVERE}
      * @param   sourceClass    Name of the class that issued the logging request
      * @param   sourceMethod   Name of the method that issued the logging request
      * @param   bundle         Resource bundle to localize {@code msg},
@@ -1363,6 +1393,42 @@
         doLog(lr, bundle);
     }
 
+    /**
+     * Log a message, specifying source class, method, and resource bundle,
+     * with associated Throwable information.
+     * <p>
+     * If the logger is currently enabled for the given message
+     * {@code level} then the given arguments are stored in a {@code LogRecord}
+     * which is forwarded to all registered output handlers.
+     * <p>
+     * The {@code msg} string is localized using the given resource bundle.
+     * If the resource bundle is {@code null}, then the {@code msg} string is not
+     * localized.
+     * <p>
+     * Note that the {@code thrown} argument is stored in the {@code LogRecord}
+     * {@code thrown} property, rather than the {@code LogRecord}
+     * {@code parameters} property.  Thus it is
+     * processed specially by output {@code Formatter} objects and is not treated
+     * as a formatting parameter to the {@code LogRecord} {@code message}
+     * property.
+     * <p>
+     * @param   level   One of the message level identifiers, e.g., {@code SEVERE}
+     * @param   bundle  Resource bundle to localize {@code msg};
+     *                  can be {@code null}.
+     * @param   msg     The string message (or a key in the message catalog)
+     * @param   thrown  Throwable associated with the log message.
+     * @since 1.9
+     */
+    public void logrb(Level level, ResourceBundle bundle, String msg,
+            Throwable thrown) {
+        if (!isLoggable(level)) {
+            return;
+        }
+        LogRecord lr = new LogRecord(level, msg);
+        lr.setThrown(thrown);
+        doLog(lr, bundle);
+    }
+
     //======================================================================
     // Start of convenience methods for logging method entries and returns.
     //======================================================================
--- a/jdk/src/java.logging/share/classes/java/util/logging/LoggingProxyImpl.java	Thu Nov 19 18:18:05 2015 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,117 +0,0 @@
-/*
- * Copyright (c) 2009, 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.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package java.util.logging;
-
-import sun.util.logging.LoggingProxy;
-
-/**
- * Implementation of LoggingProxy when java.util.logging classes exist.
- */
-class LoggingProxyImpl implements LoggingProxy {
-    static final LoggingProxy INSTANCE = new LoggingProxyImpl();
-
-    private LoggingProxyImpl() { }
-
-    @Override
-    public Object getLogger(String name) {
-        // always create a platform logger with the resource bundle name
-        return Logger.getPlatformLogger(name);
-    }
-
-    @Override
-    public Object getLevel(Object logger) {
-        return ((Logger) logger).getLevel();
-    }
-
-    @Override
-    public void setLevel(Object logger, Object newLevel) {
-        ((Logger) logger).setLevel((Level) newLevel);
-    }
-
-    @Override
-    public boolean isLoggable(Object logger, Object level) {
-        return ((Logger) logger).isLoggable((Level) level);
-    }
-
-    @Override
-    public void log(Object logger, Object level, String msg) {
-        ((Logger) logger).log((Level) level, msg);
-    }
-
-    @Override
-    public void log(Object logger, Object level, String msg, Throwable t) {
-        ((Logger) logger).log((Level) level, msg, t);
-    }
-
-    @Override
-    public void log(Object logger, Object level, String msg, Object... params) {
-        ((Logger) logger).log((Level) level, msg, params);
-    }
-
-    @Override
-    public java.util.List<String> getLoggerNames() {
-        return LogManager.getLoggingMXBean().getLoggerNames();
-    }
-
-    @Override
-    public String getLoggerLevel(String loggerName) {
-        return LogManager.getLoggingMXBean().getLoggerLevel(loggerName);
-    }
-
-    @Override
-    public void setLoggerLevel(String loggerName, String levelName) {
-        LogManager.getLoggingMXBean().setLoggerLevel(loggerName, levelName);
-    }
-
-    @Override
-    public String getParentLoggerName(String loggerName) {
-        return LogManager.getLoggingMXBean().getParentLoggerName(loggerName);
-    }
-
-    @Override
-    public Object parseLevel(String levelName) {
-        Level level = Level.findLevel(levelName);
-        if (level == null) {
-            throw new IllegalArgumentException("Unknown level \"" + levelName + "\"");
-        }
-        return level;
-    }
-
-    @Override
-    public String getLevelName(Object level) {
-        return ((Level) level).getLevelName();
-    }
-
-    @Override
-    public int getLevelValue(Object level) {
-        return ((Level) level).intValue();
-    }
-
-    @Override
-    public String getProperty(String key) {
-        return LogManager.getLogManager().getProperty(key);
-    }
-}
--- a/jdk/src/java.logging/share/classes/java/util/logging/SimpleFormatter.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/src/java.logging/share/classes/java/util/logging/SimpleFormatter.java	Mon Nov 23 14:37:04 2015 -0500
@@ -27,10 +27,9 @@
 package java.util.logging;
 
 import java.io.*;
-import java.time.LocalDateTime;
 import java.time.ZoneId;
 import java.time.ZonedDateTime;
-import sun.util.logging.LoggingSupport;
+import jdk.internal.logger.SimpleConsoleLogger;
 
 /**
  * Print a brief summary of the {@code LogRecord} in a human readable
@@ -60,8 +59,12 @@
 public class SimpleFormatter extends Formatter {
 
     // format string for printing the log record
-    private final String format = LoggingSupport.getSimpleFormat();
-    private final ZoneId zoneId = ZoneId.systemDefault();
+    static String getLoggingProperty(String name) {
+        return LogManager.getLogManager().getProperty(name);
+    }
+
+    private final String format =
+        SimpleConsoleLogger.getSimpleFormat(SimpleFormatter::getLoggingProperty);
 
     /**
      * Format the given LogRecord.
@@ -152,7 +155,7 @@
     @Override
     public synchronized String format(LogRecord record) {
         ZonedDateTime zdt = ZonedDateTime.ofInstant(
-                record.getInstant(), zoneId);
+                record.getInstant(), ZoneId.systemDefault());
         String source;
         if (record.getSourceClassName() != null) {
             source = record.getSourceClassName();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.logging/share/classes/sun/util/logging/internal/LoggingProviderImpl.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,466 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+
+package sun.util.logging.internal;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.ResourceBundle;
+import java.util.function.Supplier;
+import java.lang.System.LoggerFinder;
+import java.lang.System.Logger;
+import java.util.Objects;
+import java.util.logging.LogManager;
+import jdk.internal.logger.DefaultLoggerFinder;
+import java.util.logging.LoggingPermission;
+import sun.util.logging.PlatformLogger;
+import sun.util.logging.PlatformLogger.ConfigurableBridge.LoggerConfiguration;
+
+/**
+ * This {@code LoggingProviderImpl} is the JDK internal implementation of the
+ * {@link jdk.internal.logger.DefaultLoggerFinder} which is used by
+ * the default implementation of the {@link Logger}
+ * when no {@link LoggerFinder} is found
+ * and {@code java.util.logging} is present.
+ * When {@code java.util.logging} is present, the {@code LoggingProviderImpl}
+ * is {@linkplain java.util.ServiceLoader#loadInstalled(Class) installed} as
+ * an internal service provider, making it possible to use {@code java.util.logging}
+ * as the backend for loggers returned by the default LoggerFinder implementation.
+ * <p>
+ * This implementation of {@link DefaultLoggerFinder} returns instances of
+ * {@link java.lang.System.Logger} which
+ * delegate to a wrapped instance of {@link java.util.logging.Logger
+ * java.util.logging.Logger}.
+ * <br>
+ * Loggers returned by this class can therefore be configured by accessing
+ * their wrapped implementation through the regular {@code java.util.logging}
+ * APIs - such as {@link java.util.logging.LogManager java.util.logging.LogManager}
+ * and {@link java.util.logging.Logger java.util.logging.Logger}.
+ *
+ * @apiNote Programmers are not expected to call this class directly.
+ * Instead they should rely on the static methods defined by
+ * {@link java.lang.System java.lang.System}.
+ * <p>
+ * To replace this default
+ * {@code java.util.logging} backend, an application is expected to install
+ * its own {@link java.lang.System.LoggerFinder}.
+ *
+ * @see java.lang.System.Logger
+ * @see java.lang.System.LoggerFinder
+ * @see sun.util.logging.PlatformLogger.Bridge
+ * @see java.lang.System
+ * @see jdk.internal.logger
+ * @see jdk.internal.logger
+ *
+ */
+public final class LoggingProviderImpl extends DefaultLoggerFinder {
+    static final RuntimePermission LOGGERFINDER_PERMISSION =
+                new RuntimePermission("loggerFinder");
+    private static final LoggingPermission LOGGING_CONTROL_PERMISSION =
+            new LoggingPermission("control", null);
+
+    /**
+     * Creates a new instance of LoggingProviderImpl.
+     * @throws SecurityException if the calling code does not have the
+     * {@code RuntimePermission("loggerFinder")}.
+     */
+    public LoggingProviderImpl() {
+    }
+
+    /**
+     * A logger that delegates to a java.util.logging.Logger delegate.
+     */
+    static final class JULWrapper extends LoggerConfiguration
+            implements System.Logger, PlatformLogger.Bridge,
+                       PlatformLogger.ConfigurableBridge {
+
+
+        private static final java.util.logging.Level[] spi2JulLevelMapping = {
+                java.util.logging.Level.ALL,     // mapped from ALL
+                java.util.logging.Level.FINER,   // mapped from TRACE
+                java.util.logging.Level.FINE,    // mapped from DEBUG
+                java.util.logging.Level.INFO,    // mapped from INFO
+                java.util.logging.Level.WARNING, // mapped from WARNING
+                java.util.logging.Level.SEVERE,  // mapped from ERROR
+                java.util.logging.Level.OFF      // mapped from OFF
+        };
+
+        private static final java.util.logging.Level[] platform2JulLevelMapping = {
+                java.util.logging.Level.ALL,     // mapped from ALL
+                java.util.logging.Level.FINEST,  // mapped from FINEST
+                java.util.logging.Level.FINER,   // mapped from FINER
+                java.util.logging.Level.FINE,    // mapped from FINE
+                java.util.logging.Level.CONFIG,  // mapped from CONFIG
+                java.util.logging.Level.INFO,    // mapped from INFO
+                java.util.logging.Level.WARNING, // mapped from WARNING
+                java.util.logging.Level.SEVERE,  // mapped from SEVERE
+                java.util.logging.Level.OFF      // mapped from OFF
+        };
+
+        private final java.util.logging.Logger julLogger;
+
+
+        private JULWrapper(java.util.logging.Logger logger) {
+            this.julLogger = logger;
+        }
+
+        @Override
+        public String getName() {
+            return julLogger.getName();
+        }
+        @Override
+        public void log(sun.util.logging.PlatformLogger.Level level, String msg, Throwable throwable) {
+            julLogger.log(toJUL(level), msg, throwable);
+        }
+
+        @Override
+        public void log(sun.util.logging.PlatformLogger.Level level, String format, Object... params) {
+            julLogger.log(toJUL(level), format, params);
+        }
+
+        @Override
+        public void log(sun.util.logging.PlatformLogger.Level level, String msg) {
+            julLogger.log(toJUL(level), msg);
+        }
+
+        @Override
+        public void log(sun.util.logging.PlatformLogger.Level level, Supplier<String> msgSuppier) {
+            julLogger.log(toJUL(level), msgSuppier);
+        }
+
+        @Override
+        public void log(sun.util.logging.PlatformLogger.Level level, Throwable thrown, Supplier<String> msgSuppier) {
+            julLogger.log(toJUL(level), thrown, msgSuppier);
+        }
+
+        @Override
+        public void logrb(sun.util.logging.PlatformLogger.Level level, ResourceBundle bundle, String key, Throwable throwable) {
+            julLogger.logrb(toJUL(level), bundle, key, throwable);
+        }
+
+        @Override
+        public void logrb(sun.util.logging.PlatformLogger.Level level, ResourceBundle bundle, String key, Object... params) {
+            julLogger.logrb(toJUL(level), bundle, key, params);
+        }
+
+        @Override
+        public void logp(sun.util.logging.PlatformLogger.Level level, String sourceClass, String sourceMethod, String msg) {
+            julLogger.logp(toJUL(level), sourceClass, sourceMethod, msg);
+        }
+
+        @Override
+        public void logp(sun.util.logging.PlatformLogger.Level level, String sourceClass, String sourceMethod,
+                Supplier<String> msgSupplier) {
+            julLogger.logp(toJUL(level), sourceClass, sourceMethod, msgSupplier);
+        }
+
+        @Override
+        public void logp(sun.util.logging.PlatformLogger.Level level, String sourceClass, String sourceMethod,
+                String msg, Object... params) {
+            julLogger.logp(toJUL(level), sourceClass, sourceMethod, msg, params);
+        }
+
+        @Override
+        public void logp(sun.util.logging.PlatformLogger.Level level, String sourceClass, String sourceMethod,
+                String msg, Throwable thrown) {
+            julLogger.logp(toJUL(level), sourceClass, sourceMethod, msg, thrown);
+        }
+
+        @Override
+        public void logp(sun.util.logging.PlatformLogger.Level level, String sourceClass, String sourceMethod,
+                Throwable thrown, Supplier<String> msgSupplier) {
+            julLogger.logp(toJUL(level), sourceClass, sourceMethod,
+                    thrown, msgSupplier);
+        }
+
+        @Override
+        public void logrb(sun.util.logging.PlatformLogger.Level level, String sourceClass, String sourceMethod,
+                ResourceBundle bundle, String key, Object... params) {
+            julLogger.logrb(toJUL(level), sourceClass, sourceMethod,
+                    bundle, key, params);
+        }
+
+        @Override
+        public void logrb(sun.util.logging.PlatformLogger.Level level, String sourceClass, String sourceMethod,
+                ResourceBundle bundle, String key, Throwable thrown) {
+            julLogger.logrb(toJUL(level), sourceClass, sourceMethod,
+                    bundle, key, thrown);
+        }
+
+        @Override
+        public  boolean isLoggable(sun.util.logging.PlatformLogger.Level level) {
+            return julLogger.isLoggable(toJUL(level));
+        }
+
+        // -----------------------------------------------------------------
+        // Generic methods taking a Level as parameter
+        // -----------------------------------------------------------------
+
+
+        @Override
+        public boolean isLoggable(Level level) {
+            return julLogger.isLoggable(toJUL(level));
+        }
+
+        @Override
+        public void log(Level level, String msg) {
+            julLogger.log(toJUL(level), msg);
+        }
+
+        @Override
+        public void log(Level level,
+                        Supplier<String> msgSupplier) {
+            // We need to check for null here to satisfy the contract
+            // of System.Logger - because the underlying implementation
+            // of julLogger will check for it only if the level is
+            // loggable
+            Objects.requireNonNull(msgSupplier);
+            julLogger.log(toJUL(level), msgSupplier);
+        }
+
+        @Override
+        public void log(Level level, Object obj) {
+            // We need to check for null here to satisfy the contract
+            // of System.Logger - because the underlying implementation
+            // of julLogger will check for it only if the level is
+            // loggable
+            Objects.requireNonNull(obj);
+            julLogger.log(toJUL(level), () -> obj.toString());
+        }
+
+        @Override
+        public void log(Level level,
+                        String msg, Throwable thrown) {
+            julLogger.log(toJUL(level), msg, thrown);
+        }
+
+        @Override
+        public void log(Level level, Supplier<String> msgSupplier,
+                        Throwable thrown) {
+            // We need to check for null here to satisfy the contract
+            // of System.Logger - because the underlying implementation
+            // of julLogger will check for it only if the level is
+            // loggable
+            Objects.requireNonNull(msgSupplier);
+            julLogger.log(toJUL(level), thrown, msgSupplier);
+        }
+
+        @Override
+        public void log(Level level,
+                        String format, Object... params) {
+            julLogger.log(toJUL(level), format, params);
+        }
+
+        @Override
+        public void log(Level level, ResourceBundle bundle,
+                        String key, Throwable thrown) {
+            julLogger.logrb(toJUL(level), bundle, key, thrown);
+        }
+
+        @Override
+        public void log(Level level, ResourceBundle bundle,
+                        String format, Object... params) {
+            julLogger.logrb(toJUL(level), bundle, format, params);
+        }
+
+        static java.util.logging.Level toJUL(Level level) {
+            if (level == null) return null;
+            assert level.ordinal() < spi2JulLevelMapping.length;
+            return spi2JulLevelMapping[level.ordinal()];
+        }
+
+        // ---------------------------------------------------------
+        // Methods from PlatformLogger.Bridge
+        // ---------------------------------------------------------
+
+        @Override
+        public boolean isEnabled() {
+            return julLogger.getLevel() != java.util.logging.Level.OFF;
+        }
+
+        @Override
+        public PlatformLogger.Level getPlatformLevel() {
+            final java.util.logging.Level javaLevel = julLogger.getLevel();
+            if (javaLevel == null) return null;
+            try {
+                return PlatformLogger.Level.valueOf(javaLevel.getName());
+            } catch (IllegalArgumentException e) {
+                return PlatformLogger.Level.valueOf(javaLevel.intValue());
+            }
+        }
+
+        @Override
+        public void setPlatformLevel(PlatformLogger.Level level) {
+            // null is allowed here
+            julLogger.setLevel(toJUL(level));
+        }
+
+        @Override
+        public LoggerConfiguration getLoggerConfiguration() {
+            return this;
+        }
+
+        static java.util.logging.Level toJUL(PlatformLogger.Level level) {
+            // The caller will throw if null is invalid in its context.
+            // There's at least one case where a null level is valid.
+            if (level == null) return null;
+            assert level.ordinal() < platform2JulLevelMapping.length;
+            return platform2JulLevelMapping[level.ordinal()];
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            return (obj instanceof JULWrapper)
+                    && obj.getClass() == this.getClass()
+                    && ((JULWrapper)obj).julLogger == this.julLogger;
+        }
+
+        @Override
+        public int hashCode() {
+            return julLogger.hashCode();
+        }
+
+        // A JULWrapper is just a stateless thin shell over a JUL logger - so
+        // for a given JUL logger, we could always return the same wrapper.
+        //
+        // This is an optimization which may - or may not - be worth the
+        // trouble: if many classes use the same logger, and if each class
+        // keeps a reference to that logger, then caching the wrapper will
+        // be worthwhile. Otherwise, if each logger is only referred once,
+        // then the cache will eat up more memory than would be necessary...
+        //
+        // Here is an example of how we could implement JULWrapper.of(...)
+        // if we wanted to create at most one wrapper instance for each logger
+        // instance:
+        //
+        //        static final WeakHashMap<JULWrapper, WeakReference<JULWrapper>>
+        //                wrappers = new WeakHashMap<>();
+        //
+        //        static JULWrapper of(java.util.logging.Logger logger) {
+        //
+        //            // First access without synchronizing
+        //            final JULWrapper candidate = new JULWrapper(logger);
+        //            WeakReference<JULWrapper> ref = wrappers.get(candidate);
+        //            JULWrapper found = ref.get();
+        //
+        //            // OK - we found it - lets return it.
+        //            if (found != null) return found;
+        //
+        //            // Not found. Need to synchronize.
+        //            synchronized (wrappers) {
+        //                ref = wrappers.get(candidate);
+        //                found = ref.get();
+        //                if (found == null) {
+        //                    wrappers.put(candidate, new WeakReference<>(candidate));
+        //                    found = candidate;
+        //                }
+        //            }
+        //            assert found != null;
+        //            return found;
+        //        }
+        //
+        // But given that it may end up eating more memory in the nominal case
+        // (where each class that does logging has its own logger with the
+        //  class name as logger name and stashes that logger away in a static
+        //  field, thus making the cache redundant - as only one wrapper will
+        //  ever be created anyway) - then we will simply return a new wrapper
+        // for each invocation of JULWrapper.of(...) - which may
+        // still prove more efficient in terms of memory consumption...
+        //
+        static JULWrapper of(java.util.logging.Logger logger) {
+            return new JULWrapper(logger);
+        }
+
+
+    }
+
+    /**
+     * Creates a java.util.logging.Logger for the given caller.
+     * @param name the logger name.
+     * @param caller the caller for which the logger should be created.
+     * @return a Logger suitable for use in the given caller.
+     */
+    private static java.util.logging.Logger demandJULLoggerFor(final String name,
+                                                            /* Module */
+                                                            final Class<?> caller) {
+        final LogManager manager = LogManager.getLogManager();
+        final SecurityManager sm = System.getSecurityManager();
+        if (sm == null) {
+            return logManagerAccess.demandLoggerFor(manager, name, caller);
+        } else {
+            final PrivilegedAction<java.util.logging.Logger> pa =
+                    () -> logManagerAccess.demandLoggerFor(manager, name, caller);
+            return AccessController.doPrivileged(pa, null, LOGGING_CONTROL_PERMISSION);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @apiNote The logger returned by this method can be configured through
+     * its {@linkplain java.util.logging.LogManager#getLogger(String)
+     * corresponding java.util.logging.Logger backend}.
+     *
+     * @return {@inheritDoc}
+     * @throws SecurityException if the calling code doesn't have the
+     * {@code RuntimePermission("loggerFinder")}.
+     */
+    @Override
+    protected Logger demandLoggerFor(String name, /* Module */ Class<?> caller) {
+        final SecurityManager sm = System.getSecurityManager();
+        if (sm != null) {
+            sm.checkPermission(LOGGERFINDER_PERMISSION);
+        }
+        return JULWrapper.of(demandJULLoggerFor(name,caller));
+    }
+
+    public static interface LogManagerAccess {
+        java.util.logging.Logger demandLoggerFor(LogManager manager,
+                String name, /* Module */ Class<?> caller);
+    }
+
+    // Hook for tests
+    public static LogManagerAccess getLogManagerAccess() {
+        final SecurityManager sm = System.getSecurityManager();
+        if (sm != null) {
+            sm.checkPermission(LOGGING_CONTROL_PERMISSION);
+        }
+        // Triggers initialization of accessJulLogger if not set.
+        if (logManagerAccess == null) LogManager.getLogManager();
+        return logManagerAccess;
+    }
+
+
+    private static volatile LogManagerAccess logManagerAccess;
+    public static void setLogManagerAccess(LogManagerAccess accesLoggers) {
+        final SecurityManager sm = System.getSecurityManager();
+        if (sm != null) {
+            sm.checkPermission(LOGGING_CONTROL_PERMISSION);
+        }
+        logManagerAccess = accesLoggers;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.logging/share/classes/sun/util/logging/internal/package-info.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * <p>
+ * <b>[JDK INTERNAL]</b>
+ * The {@code sun.util.logging.internal} package defines an internal
+ * implementation of the {@link jdk.internal.logger.DefaultLoggerFinder} which
+ * provides an extension of the {@link java.lang.System.Logger System.Logger}
+ * interface making it easy to bridge from {@link java.util.logging};
+ * the JDK default implementation of the LoggerFinder will return loggers
+ * implementing this extension when {@code java.util.logging} is present.
+ * </p>
+ * <p>
+ * When {@code java.util.logging} is present, Logger instances returned by
+ * the JDK default implementation of the LoggerFinder
+ * wrap an instance of {@link java.util.logging.Logger java.util.logging.Logger}
+ * and implement the {@link
+ * sun.util.logging.PlatformLogger.Bridge PlatformLogger.Bridge}
+ * extension, overriding all the methods defined in
+ * that extension in order to call the corresponding methods on their wrapped
+ * {@linkplain java.util.logging.Logger backend Logger} instance.
+ * <p>
+ * <br>
+ * @see java.lang.System.LoggerFinder
+ * @see java.lang.System.Logger
+ * @see sun.util.logging.PlatformLogger
+ * @see sun.util.logging.PlatformLogger.Bridge
+ * @see jdk.internal.logger
+ *
+ * @since 1.9
+ */
+package sun.util.logging.internal;
--- a/jdk/src/java.management/share/classes/java/lang/management/DefaultPlatformMBeanProvider.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/src/java.management/share/classes/java/lang/management/DefaultPlatformMBeanProvider.java	Mon Nov 23 14:37:04 2015 -0500
@@ -355,36 +355,38 @@
             }
         });
 
-        /**
-         * Logging facility.
-         */
-        initMBeanList.add(new PlatformComponent<PlatformLoggingMXBean>() {
-            private final Set<String> platformLoggingMXBeanInterfaceNames
+        if (ManagementFactoryHelper.isPlatformLoggingMXBeanAvailable()) {
+            /**
+             * Logging facility.
+             */
+            initMBeanList.add(new PlatformComponent<PlatformLoggingMXBean>() {
+                private final Set<String> platformLoggingMXBeanInterfaceNames
                     = Collections.unmodifiableSet(Collections.singleton(
                             "java.lang.management.PlatformLoggingMXBean"));
 
-            @Override
-            public Set<Class<? extends PlatformLoggingMXBean>> mbeanInterfaces() {
-                return Collections.singleton(PlatformLoggingMXBean.class);
-            }
+                @Override
+                public Set<Class<? extends PlatformLoggingMXBean>> mbeanInterfaces() {
+                    return Collections.singleton(PlatformLoggingMXBean.class);
+                }
 
-            @Override
-            public Set<String> mbeanInterfaceNames() {
-                return platformLoggingMXBeanInterfaceNames;
-            }
+                @Override
+                public Set<String> mbeanInterfaceNames() {
+                    return platformLoggingMXBeanInterfaceNames;
+                }
 
-            @Override
-            public String getObjectNamePattern() {
-                return "java.util.logging:type=Logging";
-            }
+                @Override
+                public String getObjectNamePattern() {
+                    return "java.util.logging:type=Logging";
+                }
 
-            @Override
-            public Map<String, PlatformLoggingMXBean> nameToMBeanMap() {
-                return Collections.singletonMap(
+                @Override
+                public Map<String, PlatformLoggingMXBean> nameToMBeanMap() {
+                    return Collections.singletonMap(
                         "java.util.logging:type=Logging",
                         ManagementFactoryHelper.getPlatformLoggingMXBean());
-            }
-        });
+                }
+            });
+        }
 
         /**
          * Buffer pools.
--- a/jdk/src/java.management/share/classes/sun/management/ManagementFactoryHelper.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/src/java.management/share/classes/sun/management/ManagementFactoryHelper.java	Mon Nov 23 14:37:04 2015 -0500
@@ -39,10 +39,14 @@
 
 import jdk.internal.misc.JavaNioAccess;
 import jdk.internal.misc.SharedSecrets;
-import sun.util.logging.LoggingSupport;
+
 import java.util.ArrayList;
 import java.util.List;
 
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.security.PrivilegedAction;
+
 /**
  * ManagementFactoryHelper provides static factory methods to create
  * instances of the management interface.
@@ -141,13 +145,17 @@
     }
 
     public static PlatformLoggingMXBean getPlatformLoggingMXBean() {
-        if (LoggingSupport.isAvailable()) {
+        if (LoggingMXBeanSupport.isAvailable()) {
             return PlatformLoggingImpl.instance;
         } else {
             return null;
         }
     }
 
+    public static boolean isPlatformLoggingMXBeanAvailable() {
+        return LoggingMXBeanSupport.isAvailable();
+    }
+
     /**
      * The logging MXBean object is an instance of
      * PlatformLoggingMXBean and java.util.logging.LoggingMXBean
@@ -165,8 +173,44 @@
         extends PlatformLoggingMXBean, java.util.logging.LoggingMXBean {
     }
 
+    // This is a trick: if java.util.logging is not present then
+    // attempting to access something that implements
+    // java.util.logging.LoggingMXBean will trigger a CNFE.
+    // So we cannot directly call any static method or access any static field
+    // on PlatformLoggingImpl, as we would risk raising a CNFE.
+    // Instead we use this intermediate LoggingMXBeanSupport class to determine
+    // whether java.util.logging is present, and load the actual LoggingMXBean
+    // implementation.
+    //
+    static final class LoggingMXBeanSupport {
+        final static Object loggingImpl =
+                AccessController.doPrivileged(new PrivilegedAction<Object>() {
+            @Override
+            public Object run() {
+                try {
+                    // create a LoggingProxyImpl instance when
+                    // java.util.logging classes exist
+                    Class<?> c = Class.forName("java.util.logging.Logging", true, null);
+                    Constructor<?> cons = c.getDeclaredConstructor();
+                    cons.setAccessible(true);
+                    return cons.newInstance();
+                } catch (ClassNotFoundException cnf) {
+                    return null;
+                } catch (NoSuchMethodException | InstantiationException
+                        | IllegalAccessException | InvocationTargetException e) {
+                    throw new AssertionError(e);
+                }
+            }});
+
+        static boolean isAvailable() {
+            return loggingImpl != null;
+        }
+    }
+
     static class PlatformLoggingImpl implements LoggingMXBean
     {
+        final static java.util.logging.LoggingMXBean impl =
+                (java.util.logging.LoggingMXBean) LoggingMXBeanSupport.loggingImpl;
         final static PlatformLoggingMXBean instance = new PlatformLoggingImpl();
         final static String LOGGING_MXBEAN_NAME = "java.util.logging:type=Logging";
 
@@ -188,22 +232,22 @@
 
         @Override
         public java.util.List<String> getLoggerNames() {
-            return LoggingSupport.getLoggerNames();
+            return impl.getLoggerNames();
         }
 
         @Override
         public String getLoggerLevel(String loggerName) {
-            return LoggingSupport.getLoggerLevel(loggerName);
+            return impl.getLoggerLevel(loggerName);
         }
 
         @Override
         public void setLoggerLevel(String loggerName, String levelName) {
-            LoggingSupport.setLoggerLevel(loggerName, levelName);
+            impl.setLoggerLevel(loggerName, levelName);
         }
 
         @Override
         public String getParentLoggerName(String loggerName) {
-            return LoggingSupport.getParentLoggerName(loggerName);
+            return impl.getParentLoggerName(loggerName);
         }
     }
 
--- a/jdk/src/jdk.crypto.mscapi/windows/classes/sun/security/mscapi/KeyStore.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/src/jdk.crypto.mscapi/windows/classes/sun/security/mscapi/KeyStore.java	Mon Nov 23 14:37:04 2015 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -310,7 +310,7 @@
             if (alias.equals(entry.getAlias()))
             {
                 X509Certificate[] certChain = entry.getCertificateChain();
-                return certChain[0];
+                return certChain.length == 0 ? null : certChain[0];
             }
         }
 
@@ -840,7 +840,7 @@
 
             // Obtain certificate factory
             if (certificateFactory == null) {
-                certificateFactory = CertificateFactory.getInstance("X.509");
+                certificateFactory = CertificateFactory.getInstance("X.509", "SUN");
             }
 
             // Generate certificate
--- a/jdk/src/jdk.dev/share/classes/jdk/tools/jimage/ExtractedImage.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/src/jdk.dev/share/classes/jdk/tools/jimage/ExtractedImage.java	Mon Nov 23 14:37:04 2015 -0500
@@ -102,7 +102,6 @@
             }
             chop = dirPath.toString().length() + 1;
             this.moduleName = dirPath.getFileName().toString();
-            System.out.println("Module name " + this.moduleName);
             this.dirPath = dirPath;
         }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/jdk.jartool/share/classes/jdk/security/jarsigner/JarSigner.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,1286 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.security.jarsigner;
+
+import com.sun.jarsigner.ContentSigner;
+import com.sun.jarsigner.ContentSignerParameters;
+import sun.security.tools.PathList;
+import sun.security.tools.jarsigner.TimestampedSigner;
+import sun.security.util.ManifestDigester;
+import sun.security.util.SignatureFileVerifier;
+import sun.security.x509.AlgorithmId;
+
+import java.io.*;
+import java.net.SocketTimeoutException;
+import java.net.URI;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.security.*;
+import java.security.cert.CertPath;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.*;
+import java.util.function.BiConsumer;
+import java.util.jar.Attributes;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.jar.Manifest;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipOutputStream;
+
+/**
+ * An immutable utility class to sign a jar file.
+ * <p>
+ * A caller creates a {@code JarSigner.Builder} object, (optionally) sets
+ * some parameters, and calls {@link JarSigner.Builder#build build} to create
+ * a {@code JarSigner} object. This {@code JarSigner} object can then
+ * be used to sign a jar file.
+ * <p>
+ * Unless otherwise stated, calling a method of {@code JarSigner} or
+ * {@code JarSigner.Builder} with a null argument will throw
+ * a {@link NullPointerException}.
+ * <p>
+ * Example:
+ * <pre>
+ * JarSigner signer = new JarSigner.Builder(key, certPath)
+ *         .digestAlgorithm("SHA-1")
+ *         .signatureAlgorithm("SHA1withDSA")
+ *         .build();
+ * try (ZipFile in = new ZipFile(inputFile);
+ *         FileOutputStream out = new FileOutputStream(outputFile)) {
+ *     signer.sign(in, out);
+ * }
+ * </pre>
+ *
+ * @since 1.9
+ */
+@jdk.Exported
+public final class JarSigner {
+
+    /**
+     * A mutable builder class that can create an immutable {@code JarSigner}
+     * from various signing-related parameters.
+     *
+     * @since 1.9
+     */
+    @jdk.Exported
+    public static class Builder {
+
+        // Signer materials:
+        final PrivateKey privateKey;
+        final X509Certificate[] certChain;
+
+        // JarSigner options:
+        // Support multiple digestalg internally. Can be null, but not empty
+        String[] digestalg;
+        String sigalg;
+        // Precisely should be one provider for each digestalg, maybe later
+        Provider digestProvider;
+        Provider sigProvider;
+        URI tsaUrl;
+        String signerName;
+        BiConsumer<String,String> handler;
+
+        // Implementation-specific properties:
+        String tSAPolicyID;
+        String tSADigestAlg;
+        boolean signManifest = true;
+        boolean externalSF = true;
+        String altSignerPath;
+        String altSigner;
+
+        /**
+         * Creates a {@code JarSigner.Builder} object with
+         * a {@link KeyStore.PrivateKeyEntry} object.
+         *
+         * @param entry the {@link KeyStore.PrivateKeyEntry} of the signer.
+         */
+        public Builder(KeyStore.PrivateKeyEntry entry) {
+            this.privateKey = entry.getPrivateKey();
+            try {
+                // called internally, no need to clone
+                Certificate[] certs = entry.getCertificateChain();
+                this.certChain = Arrays.copyOf(certs, certs.length,
+                        X509Certificate[].class);
+            } catch (ArrayStoreException ase) {
+                // Wrong type, not X509Certificate. Won't document.
+                throw new IllegalArgumentException(
+                        "Entry does not contain X509Certificate");
+            }
+        }
+
+        /**
+         * Creates a {@code JarSigner.Builder} object with a private key and
+         * a certification path.
+         *
+         * @param privateKey the private key of the signer.
+         * @param certPath the certification path of the signer.
+         * @throws IllegalArgumentException if {@code certPath} is empty, or
+         *      the {@code privateKey} algorithm does not match the algorithm
+         *      of the {@code PublicKey} in the end entity certificate
+         *      (the first certificate in {@code certPath}).
+         */
+        public Builder(PrivateKey privateKey, CertPath certPath) {
+            List<? extends Certificate> certs = certPath.getCertificates();
+            if (certs.isEmpty()) {
+                throw new IllegalArgumentException("certPath cannot be empty");
+            }
+            if (!privateKey.getAlgorithm().equals
+                    (certs.get(0).getPublicKey().getAlgorithm())) {
+                throw new IllegalArgumentException
+                        ("private key algorithm does not match " +
+                                "algorithm of public key in end entity " +
+                                "certificate (the 1st in certPath)");
+            }
+            this.privateKey = privateKey;
+            try {
+                this.certChain = certs.toArray(new X509Certificate[certs.size()]);
+            } catch (ArrayStoreException ase) {
+                // Wrong type, not X509Certificate.
+                throw new IllegalArgumentException(
+                        "Entry does not contain X509Certificate");
+            }
+        }
+
+        /**
+         * Sets the digest algorithm. If no digest algorithm is specified,
+         * the default algorithm returned by {@link #getDefaultDigestAlgorithm}
+         * will be used.
+         *
+         * @param algorithm the standard name of the algorithm. See
+         *      the {@code MessageDigest} section in the <a href=
+         *      "{@docRoot}/../technotes/guides/security/StandardNames.html#MessageDigest">
+         *      Java Cryptography Architecture Standard Algorithm Name
+         *      Documentation</a> for information about standard algorithm names.
+         * @return the {@code JarSigner.Builder} itself.
+         * @throws NoSuchAlgorithmException if {@code algorithm} is not available.
+         */
+        public Builder digestAlgorithm(String algorithm) throws NoSuchAlgorithmException {
+            MessageDigest.getInstance(Objects.requireNonNull(algorithm));
+            this.digestalg = new String[]{algorithm};
+            this.digestProvider = null;
+            return this;
+        }
+
+        /**
+         * Sets the digest algorithm from the specified provider.
+         * If no digest algorithm is specified, the default algorithm
+         * returned by {@link #getDefaultDigestAlgorithm} will be used.
+         *
+         * @param algorithm the standard name of the algorithm. See
+         *      the {@code MessageDigest} section in the <a href=
+         *      "{@docRoot}/../technotes/guides/security/StandardNames.html#MessageDigest">
+         *      Java Cryptography Architecture Standard Algorithm Name
+         *      Documentation</a> for information about standard algorithm names.
+         * @param provider the provider.
+         * @return the {@code JarSigner.Builder} itself.
+         * @throws NoSuchAlgorithmException if {@code algorithm} is not
+         *      available in the specified provider.
+         */
+        public Builder digestAlgorithm(String algorithm, Provider provider)
+                throws NoSuchAlgorithmException {
+            MessageDigest.getInstance(
+                    Objects.requireNonNull(algorithm),
+                    Objects.requireNonNull(provider));
+            this.digestalg = new String[]{algorithm};
+            this.digestProvider = provider;
+            return this;
+        }
+
+        /**
+         * Sets the signature algorithm. If no signature algorithm
+         * is specified, the default signature algorithm returned by
+         * {@link #getDefaultSignatureAlgorithm} for the private key
+         * will be used.
+         *
+         * @param algorithm the standard name of the algorithm. See
+         *      the {@code Signature} section in the <a href=
+         *      "{@docRoot}/../technotes/guides/security/StandardNames.html#Signature">
+         *      Java Cryptography Architecture Standard Algorithm Name
+         *      Documentation</a> for information about standard algorithm names.
+         * @return the {@code JarSigner.Builder} itself.
+         * @throws NoSuchAlgorithmException if {@code algorithm} is not available.
+         * @throws IllegalArgumentException if {@code algorithm} is not
+         *      compatible with the algorithm of the signer's private key.
+         */
+        public Builder signatureAlgorithm(String algorithm)
+                throws NoSuchAlgorithmException {
+            // Check availability
+            Signature.getInstance(Objects.requireNonNull(algorithm));
+            AlgorithmId.checkKeyAndSigAlgMatch(
+                    privateKey.getAlgorithm(), algorithm);
+            this.sigalg = algorithm;
+            this.sigProvider = null;
+            return this;
+        }
+
+        /**
+         * Sets the signature algorithm from the specified provider. If no
+         * signature algorithm is specified, the default signature algorithm
+         * returned by {@link #getDefaultSignatureAlgorithm} for the private
+         * key will be used.
+         *
+         * @param algorithm the standard name of the algorithm. See
+         *      the {@code Signature} section in the <a href=
+         *      "{@docRoot}/../technotes/guides/security/StandardNames.html#Signature">
+         *      Java Cryptography Architecture Standard Algorithm Name
+         *      Documentation</a> for information about standard algorithm names.
+         * @param provider  the provider.
+         * @return the {@code JarSigner.Builder} itself.
+         * @throws NoSuchAlgorithmException if {@code algorithm} is not
+         *      available in the specified provider.
+         * @throws IllegalArgumentException if {@code algorithm} is not
+         *      compatible with the algorithm of the signer's private key.
+         */
+        public Builder signatureAlgorithm(String algorithm, Provider provider)
+                throws NoSuchAlgorithmException {
+            // Check availability
+            Signature.getInstance(
+                    Objects.requireNonNull(algorithm),
+                    Objects.requireNonNull(provider));
+            AlgorithmId.checkKeyAndSigAlgMatch(
+                    privateKey.getAlgorithm(), algorithm);
+            this.sigalg = algorithm;
+            this.sigProvider = provider;
+            return this;
+        }
+
+        /**
+         * Sets the URI of the Time Stamping Authority (TSA).
+         *
+         * @param uri the URI.
+         * @return the {@code JarSigner.Builder} itself.
+         */
+        public Builder tsa(URI uri) {
+            this.tsaUrl = Objects.requireNonNull(uri);
+            return this;
+        }
+
+        /**
+         * Sets the signer name. The name will be used as the base name for
+         * the signature files. All lowercase characters will be converted to
+         * uppercase for signature file names. If a signer name is not
+         * specified, the string "SIGNER" will be used.
+         *
+         * @param name the signer name.
+         * @return the {@code JarSigner.Builder} itself.
+         * @throws IllegalArgumentException if {@code name} is empty or has
+         *      a size bigger than 8, or it contains characters not from the
+         *      set "a-zA-Z0-9_-".
+         */
+        public Builder signerName(String name) {
+            if (name.isEmpty() || name.length() > 8) {
+                throw new IllegalArgumentException("Name too long");
+            }
+
+            name = name.toUpperCase(Locale.ENGLISH);
+
+            for (int j = 0; j < name.length(); j++) {
+                char c = name.charAt(j);
+                if (!
+                        ((c >= 'A' && c <= 'Z') ||
+                                (c >= '0' && c <= '9') ||
+                                (c == '-') ||
+                                (c == '_'))) {
+                    throw new IllegalArgumentException(
+                            "Invalid characters in name");
+                }
+            }
+            this.signerName = name;
+            return this;
+        }
+
+        /**
+         * Sets en event handler that will be triggered when a {@link JarEntry}
+         * is to be added, signed, or updated during the signing process.
+         * <p>
+         * The handler can be used to display signing progress. The first
+         * argument of the handler can be "adding", "signing", or "updating",
+         * and the second argument is the name of the {@link JarEntry}
+         * being processed.
+         *
+         * @param handler the event handler.
+         * @return the {@code JarSigner.Builder} itself.
+         */
+        public Builder eventHandler(BiConsumer<String,String> handler) {
+            this.handler = Objects.requireNonNull(handler);
+            return this;
+        }
+
+        /**
+         * Sets an additional implementation-specific property indicated by
+         * the specified key.
+         *
+         * @implNote This implementation supports the following properties:
+         * <ul>
+         * <li>"tsaDigestAlg": algorithm of digest data in the timestamping
+         * request. The default value is the same as the result of
+         * {@link #getDefaultDigestAlgorithm}.
+         * <li>"tsaPolicyId": TSAPolicyID for Timestamping Authority.
+         * No default value.
+         * <li>"internalsf": "true" if the .SF file is included inside the
+         * signature block, "false" otherwise. Default "false".
+         * <li>"sectionsonly": "true" if the .SF file only contains the hash
+         * value for each section of the manifest and not for the whole
+         * manifest, "false" otherwise. Default "false".
+         * </ul>
+         * All property names are case-insensitive.
+         *
+         * @param key the name of the property.
+         * @param value the value of the property.
+         * @return the {@code JarSigner.Builder} itself.
+         * @throws UnsupportedOperationException if the key is not supported
+         *      by this implementation.
+         * @throws IllegalArgumentException if the value is not accepted as
+         *      a legal value for this key.
+         */
+        public Builder setProperty(String key, String value) {
+            Objects.requireNonNull(key);
+            Objects.requireNonNull(value);
+            switch (key.toLowerCase(Locale.US)) {
+                case "tsadigestalg":
+                    try {
+                        MessageDigest.getInstance(value);
+                    } catch (NoSuchAlgorithmException nsae) {
+                        throw new IllegalArgumentException(
+                                "Invalid tsadigestalg", nsae);
+                    }
+                    this.tSADigestAlg = value;
+                    break;
+                case "tsapolicyid":
+                    this.tSAPolicyID = value;
+                    break;
+                case "internalsf":
+                    switch (value) {
+                        case "true":
+                            externalSF = false;
+                            break;
+                        case "false":
+                            externalSF = true;
+                            break;
+                        default:
+                            throw new IllegalArgumentException(
+                                "Invalid internalsf value");
+                    }
+                    break;
+                case "sectionsonly":
+                    switch (value) {
+                        case "true":
+                            signManifest = false;
+                            break;
+                        case "false":
+                            signManifest = true;
+                            break;
+                        default:
+                            throw new IllegalArgumentException(
+                                "Invalid signManifest value");
+                    }
+                    break;
+                case "altsignerpath":
+                    altSignerPath = value;
+                    break;
+                case "altsigner":
+                    altSigner = value;
+                    break;
+                default:
+                    throw new UnsupportedOperationException(
+                            "Unsupported key " + key);
+            }
+            return this;
+        }
+
+        /**
+         * Gets the default digest algorithm.
+         *
+         * @implNote This implementation returns "SHA-256". The value may
+         * change in the future.
+         *
+         * @return the default digest algorithm.
+         */
+        public static String getDefaultDigestAlgorithm() {
+            return "SHA-256";
+        }
+
+        /**
+         * Gets the default signature algorithm for a private key.
+         * For example, SHA256withRSA for a 2048-bit RSA key, and
+         * SHA384withECDSA for a 384-bit EC key.
+         *
+         * @implNote This implementation makes use of comparable strengths
+         * as defined in Tables 2 and 3 of NIST SP 800-57 Part 1-Rev.3.
+         * Specifically, if a DSA or RSA key with a key size greater than 7680
+         * bits, or an EC key with a key size greater than or equal to 512 bits,
+         * SHA-512 will be used as the hash function for the signature.
+         * If a DSA or RSA key has a key size greater than 3072 bits, or an
+         * EC key has a key size greater than or equal to 384 bits, SHA-384 will
+         * be used. Otherwise, SHA-256 will be used. The value may
+         * change in the future.
+         *
+         * @param key the private key.
+         * @return the default signature algorithm. Returns null if a default
+         *      signature algorithm cannot be found. In this case,
+         *      {@link #signatureAlgorithm} must be called to specify a
+         *      signature algorithm. Otherwise, the {@link #build} method
+         *      will throw an {@link IllegalArgumentException}.
+         */
+        public static String getDefaultSignatureAlgorithm(PrivateKey key) {
+            return AlgorithmId.getDefaultSigAlgForKey(Objects.requireNonNull(key));
+        }
+
+        /**
+         * Builds a {@code JarSigner} object from the parameters set by the
+         * setter methods.
+         * <p>
+         * This method does not modify internal state of this {@code Builder}
+         * object and can be called multiple times to generate multiple
+         * {@code JarSigner} objects. After this method is called, calling
+         * any method on this {@code Builder} will have no effect on
+         * the newly built {@code JarSigner} object.
+         *
+         * @return the {@code JarSigner} object.
+         * @throws IllegalArgumentException if a signature algorithm is not
+         *      set and cannot be derived from the private key using the
+         *      {@link #getDefaultSignatureAlgorithm} method.
+         */
+        public JarSigner build() {
+            return new JarSigner(this);
+        }
+    }
+
+    private static final String META_INF = "META-INF/";
+
+    // All fields in Builder are duplicated here as final. Those not
+    // provided but has a default value will be filled with default value.
+
+    // Precisely, a final array field can still be modified if only
+    // reference is copied, no clone is done because we are concerned about
+    // casual change instead of malicious attack.
+
+    // Signer materials:
+    private final PrivateKey privateKey;
+    private final X509Certificate[] certChain;
+
+    // JarSigner options:
+    private final String[] digestalg;
+    private final String sigalg;
+    private final Provider digestProvider;
+    private final Provider sigProvider;
+    private final URI tsaUrl;
+    private final String signerName;
+    private final BiConsumer<String,String> handler;
+
+    // Implementation-specific properties:
+    private final String tSAPolicyID;
+    private final String tSADigestAlg;
+    private final boolean signManifest; // "sign" the whole manifest
+    private final boolean externalSF; // leave the .SF out of the PKCS7 block
+    private final String altSignerPath;
+    private final String altSigner;
+
+    private JarSigner(JarSigner.Builder builder) {
+
+        this.privateKey = builder.privateKey;
+        this.certChain = builder.certChain;
+        if (builder.digestalg != null) {
+            // No need to clone because builder only accepts one alg now
+            this.digestalg = builder.digestalg;
+        } else {
+            this.digestalg = new String[] {
+                    Builder.getDefaultDigestAlgorithm() };
+        }
+        this.digestProvider = builder.digestProvider;
+        if (builder.sigalg != null) {
+            this.sigalg = builder.sigalg;
+        } else {
+            this.sigalg = JarSigner.Builder
+                    .getDefaultSignatureAlgorithm(privateKey);
+            if (this.sigalg == null) {
+                throw new IllegalArgumentException(
+                        "No signature alg for " + privateKey.getAlgorithm());
+            }
+        }
+        this.sigProvider = builder.sigProvider;
+        this.tsaUrl = builder.tsaUrl;
+
+        if (builder.signerName == null) {
+            this.signerName = "SIGNER";
+        } else {
+            this.signerName = builder.signerName;
+        }
+        this.handler = builder.handler;
+
+        if (builder.tSADigestAlg != null) {
+            this.tSADigestAlg = builder.tSADigestAlg;
+        } else {
+            this.tSADigestAlg = Builder.getDefaultDigestAlgorithm();
+        }
+        this.tSAPolicyID = builder.tSAPolicyID;
+        this.signManifest = builder.signManifest;
+        this.externalSF = builder.externalSF;
+        this.altSigner = builder.altSigner;
+        this.altSignerPath = builder.altSignerPath;
+    }
+
+    /**
+     * Signs a file into an {@link OutputStream}. This method will not close
+     * {@code file} or {@code os}.
+     *
+     * @param file the file to sign.
+     * @param os the output stream.
+     * @throws JarSignerException if the signing fails.
+     */
+    public void sign(ZipFile file, OutputStream os) {
+        try {
+            sign0(Objects.requireNonNull(file),
+                    Objects.requireNonNull(os));
+        } catch (SocketTimeoutException | CertificateException e) {
+            // CertificateException is thrown when the received cert from TSA
+            // has no id-kp-timeStamping in its Extended Key Usages extension.
+            throw new JarSignerException("Error applying timestamp", e);
+        } catch (IOException ioe) {
+            throw new JarSignerException("I/O error", ioe);
+        } catch (NoSuchAlgorithmException | InvalidKeyException e) {
+            throw new JarSignerException("Error in signer materials", e);
+        } catch (SignatureException se) {
+            throw new JarSignerException("Error creating signature", se);
+        }
+    }
+
+    /**
+     * Returns the digest algorithm for this {@code JarSigner}.
+     * <p>
+     * The return value is never null.
+     *
+     * @return the digest algorithm.
+     */
+    public String getDigestAlgorithm() {
+        return digestalg[0];
+    }
+
+    /**
+     * Returns the signature algorithm for this {@code JarSigner}.
+     * <p>
+     * The return value is never null.
+     *
+     * @return the signature algorithm.
+     */
+    public String getSignatureAlgorithm() {
+        return sigalg;
+    }
+
+    /**
+     * Returns the URI of the Time Stamping Authority (TSA).
+     *
+     * @return the URI of the TSA.
+     */
+    public URI getTsa() {
+        return tsaUrl;
+    }
+
+    /**
+     * Returns the signer name of this {@code JarSigner}.
+     * <p>
+     * The return value is never null.
+     *
+     * @return the signer name.
+     */
+    public String getSignerName() {
+        return signerName;
+    }
+
+    /**
+     * Returns the value of an additional implementation-specific property
+     * indicated by the specified key. If a property is not set but has a
+     * default value, the default value will be returned.
+     *
+     * @implNote See {@link JarSigner.Builder#setProperty} for a list of
+     * properties this implementation supports. All property names are
+     * case-insensitive.
+     *
+     * @param key the name of the property.
+     * @return the value for the property.
+     * @throws UnsupportedOperationException if the key is not supported
+     *      by this implementation.
+     */
+    public String getProperty(String key) {
+        Objects.requireNonNull(key);
+        switch (key.toLowerCase(Locale.US)) {
+            case "tsadigestalg":
+                return tSADigestAlg;
+            case "tsapolicyid":
+                return tSAPolicyID;
+            case "internalsf":
+                return Boolean.toString(!externalSF);
+            case "sectionsonly":
+                return Boolean.toString(!signManifest);
+            case "altsignerpath":
+                return altSignerPath;
+            case "altsigner":
+                return altSigner;
+            default:
+                throw new UnsupportedOperationException(
+                        "Unsupported key " + key);
+        }
+    }
+
+    private void sign0(ZipFile zipFile, OutputStream os)
+            throws IOException, CertificateException, NoSuchAlgorithmException,
+            SignatureException, InvalidKeyException {
+        MessageDigest[] digests;
+        try {
+            digests = new MessageDigest[digestalg.length];
+            for (int i = 0; i < digestalg.length; i++) {
+                if (digestProvider == null) {
+                    digests[i] = MessageDigest.getInstance(digestalg[i]);
+                } else {
+                    digests[i] = MessageDigest.getInstance(
+                            digestalg[i], digestProvider);
+                }
+            }
+        } catch (NoSuchAlgorithmException asae) {
+            // Should not happen. User provided alg were checked, and default
+            // alg should always be available.
+            throw new AssertionError(asae);
+        }
+
+        PrintStream ps = new PrintStream(os);
+        ZipOutputStream zos = new ZipOutputStream(ps);
+
+        Manifest manifest = new Manifest();
+        Map<String, Attributes> mfEntries = manifest.getEntries();
+
+        // The Attributes of manifest before updating
+        Attributes oldAttr = null;
+
+        boolean mfModified = false;
+        boolean mfCreated = false;
+        byte[] mfRawBytes = null;
+
+        // Check if manifest exists
+        ZipEntry mfFile;
+        if ((mfFile = getManifestFile(zipFile)) != null) {
+            // Manifest exists. Read its raw bytes.
+            mfRawBytes = zipFile.getInputStream(mfFile).readAllBytes();
+            manifest.read(new ByteArrayInputStream(mfRawBytes));
+            oldAttr = (Attributes) (manifest.getMainAttributes().clone());
+        } else {
+            // Create new manifest
+            Attributes mattr = manifest.getMainAttributes();
+            mattr.putValue(Attributes.Name.MANIFEST_VERSION.toString(),
+                    "1.0");
+            String javaVendor = System.getProperty("java.vendor");
+            String jdkVersion = System.getProperty("java.version");
+            mattr.putValue("Created-By", jdkVersion + " (" + javaVendor
+                    + ")");
+            mfFile = new ZipEntry(JarFile.MANIFEST_NAME);
+            mfCreated = true;
+        }
+
+        /*
+         * For each entry in jar
+         * (except for signature-related META-INF entries),
+         * do the following:
+         *
+         * - if entry is not contained in manifest, add it to manifest;
+         * - if entry is contained in manifest, calculate its hash and
+         *   compare it with the one in the manifest; if they are
+         *   different, replace the hash in the manifest with the newly
+         *   generated one. (This may invalidate existing signatures!)
+         */
+        Vector<ZipEntry> mfFiles = new Vector<>();
+
+        boolean wasSigned = false;
+
+        for (Enumeration<? extends ZipEntry> enum_ = zipFile.entries();
+             enum_.hasMoreElements(); ) {
+            ZipEntry ze = enum_.nextElement();
+
+            if (ze.getName().startsWith(META_INF)) {
+                // Store META-INF files in vector, so they can be written
+                // out first
+                mfFiles.addElement(ze);
+
+                if (SignatureFileVerifier.isBlockOrSF(
+                        ze.getName().toUpperCase(Locale.ENGLISH))) {
+                    wasSigned = true;
+                }
+
+                if (SignatureFileVerifier.isSigningRelated(ze.getName())) {
+                    // ignore signature-related and manifest files
+                    continue;
+                }
+            }
+
+            if (manifest.getAttributes(ze.getName()) != null) {
+                // jar entry is contained in manifest, check and
+                // possibly update its digest attributes
+                if (updateDigests(ze, zipFile, digests,
+                        manifest)) {
+                    mfModified = true;
+                }
+            } else if (!ze.isDirectory()) {
+                // Add entry to manifest
+                Attributes attrs = getDigestAttributes(ze, zipFile, digests);
+                mfEntries.put(ze.getName(), attrs);
+                mfModified = true;
+            }
+        }
+
+        // Recalculate the manifest raw bytes if necessary
+        if (mfModified) {
+            ByteArrayOutputStream baos = new ByteArrayOutputStream();
+            manifest.write(baos);
+            if (wasSigned) {
+                byte[] newBytes = baos.toByteArray();
+                if (mfRawBytes != null
+                        && oldAttr.equals(manifest.getMainAttributes())) {
+
+                    /*
+                     * Note:
+                     *
+                     * The Attributes object is based on HashMap and can handle
+                     * continuation columns. Therefore, even if the contents are
+                     * not changed (in a Map view), the bytes that it write()
+                     * may be different from the original bytes that it read()
+                     * from. Since the signature on the main attributes is based
+                     * on raw bytes, we must retain the exact bytes.
+                     */
+
+                    int newPos = findHeaderEnd(newBytes);
+                    int oldPos = findHeaderEnd(mfRawBytes);
+
+                    if (newPos == oldPos) {
+                        System.arraycopy(mfRawBytes, 0, newBytes, 0, oldPos);
+                    } else {
+                        // cat oldHead newTail > newBytes
+                        byte[] lastBytes = new byte[oldPos +
+                                newBytes.length - newPos];
+                        System.arraycopy(mfRawBytes, 0, lastBytes, 0, oldPos);
+                        System.arraycopy(newBytes, newPos, lastBytes, oldPos,
+                                newBytes.length - newPos);
+                        newBytes = lastBytes;
+                    }
+                }
+                mfRawBytes = newBytes;
+            } else {
+                mfRawBytes = baos.toByteArray();
+            }
+        }
+
+        // Write out the manifest
+        if (mfModified) {
+            // manifest file has new length
+            mfFile = new ZipEntry(JarFile.MANIFEST_NAME);
+        }
+        if (handler != null) {
+            if (mfCreated) {
+                handler.accept("adding", mfFile.getName());
+            } else if (mfModified) {
+                handler.accept("updating", mfFile.getName());
+            }
+        }
+
+        zos.putNextEntry(mfFile);
+        zos.write(mfRawBytes);
+
+        // Calculate SignatureFile (".SF") and SignatureBlockFile
+        ManifestDigester manDig = new ManifestDigester(mfRawBytes);
+        SignatureFile sf = new SignatureFile(digests, manifest, manDig,
+                signerName, signManifest);
+
+        byte[] block;
+
+        Signature signer;
+        if (sigProvider == null ) {
+            signer = Signature.getInstance(sigalg);
+        } else {
+            signer = Signature.getInstance(sigalg, sigProvider);
+        }
+        signer.initSign(privateKey);
+
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        sf.write(baos);
+
+        byte[] content = baos.toByteArray();
+
+        signer.update(content);
+        byte[] signature = signer.sign();
+
+        @SuppressWarnings("deprecation")
+        ContentSigner signingMechanism = null;
+        if (altSigner != null) {
+            signingMechanism = loadSigningMechanism(altSigner,
+                    altSignerPath);
+        }
+
+        @SuppressWarnings("deprecation")
+        ContentSignerParameters params =
+                new JarSignerParameters(null, tsaUrl, tSAPolicyID,
+                        tSADigestAlg, signature,
+                        signer.getAlgorithm(), certChain, content, zipFile);
+        block = sf.generateBlock(params, externalSF, signingMechanism);
+
+        String sfFilename = sf.getMetaName();
+        String bkFilename = sf.getBlockName(privateKey);
+
+        ZipEntry sfFile = new ZipEntry(sfFilename);
+        ZipEntry bkFile = new ZipEntry(bkFilename);
+
+        long time = System.currentTimeMillis();
+        sfFile.setTime(time);
+        bkFile.setTime(time);
+
+        // signature file
+        zos.putNextEntry(sfFile);
+        sf.write(zos);
+
+        if (handler != null) {
+            if (zipFile.getEntry(sfFilename) != null) {
+                handler.accept("updating", sfFilename);
+            } else {
+                handler.accept("adding", sfFilename);
+            }
+        }
+
+        // signature block file
+        zos.putNextEntry(bkFile);
+        zos.write(block);
+
+        if (handler != null) {
+            if (zipFile.getEntry(bkFilename) != null) {
+                handler.accept("updating", bkFilename);
+            } else {
+                handler.accept("adding", bkFilename);
+            }
+        }
+
+        // Write out all other META-INF files that we stored in the
+        // vector
+        for (int i = 0; i < mfFiles.size(); i++) {
+            ZipEntry ze = mfFiles.elementAt(i);
+            if (!ze.getName().equalsIgnoreCase(JarFile.MANIFEST_NAME)
+                    && !ze.getName().equalsIgnoreCase(sfFilename)
+                    && !ze.getName().equalsIgnoreCase(bkFilename)) {
+                if (handler != null) {
+                    if (manifest.getAttributes(ze.getName()) != null) {
+                        handler.accept("signing", ze.getName());
+                    } else if (!ze.isDirectory()) {
+                        handler.accept("adding", ze.getName());
+                    }
+                }
+                writeEntry(zipFile, zos, ze);
+            }
+        }
+
+        // Write out all other files
+        for (Enumeration<? extends ZipEntry> enum_ = zipFile.entries();
+             enum_.hasMoreElements(); ) {
+            ZipEntry ze = enum_.nextElement();
+
+            if (!ze.getName().startsWith(META_INF)) {
+                if (handler != null) {
+                    if (manifest.getAttributes(ze.getName()) != null) {
+                        handler.accept("signing", ze.getName());
+                    } else {
+                        handler.accept("adding", ze.getName());
+                    }
+                }
+                writeEntry(zipFile, zos, ze);
+            }
+        }
+        zipFile.close();
+        zos.close();
+    }
+
+    private void writeEntry(ZipFile zf, ZipOutputStream os, ZipEntry ze)
+            throws IOException {
+        ZipEntry ze2 = new ZipEntry(ze.getName());
+        ze2.setMethod(ze.getMethod());
+        ze2.setTime(ze.getTime());
+        ze2.setComment(ze.getComment());
+        ze2.setExtra(ze.getExtra());
+        if (ze.getMethod() == ZipEntry.STORED) {
+            ze2.setSize(ze.getSize());
+            ze2.setCrc(ze.getCrc());
+        }
+        os.putNextEntry(ze2);
+        writeBytes(zf, ze, os);
+    }
+
+    private void writeBytes
+            (ZipFile zf, ZipEntry ze, ZipOutputStream os) throws IOException {
+        try (InputStream is = zf.getInputStream(ze)) {
+            is.transferTo(os);
+        }
+    }
+
+    private boolean updateDigests(ZipEntry ze, ZipFile zf,
+                                  MessageDigest[] digests,
+                                  Manifest mf) throws IOException {
+        boolean update = false;
+
+        Attributes attrs = mf.getAttributes(ze.getName());
+        String[] base64Digests = getDigests(ze, zf, digests);
+
+        for (int i = 0; i < digests.length; i++) {
+            // The entry name to be written into attrs
+            String name = null;
+            try {
+                // Find if the digest already exists. An algorithm could have
+                // different names. For example, last time it was SHA, and this
+                // time it's SHA-1.
+                AlgorithmId aid = AlgorithmId.get(digests[i].getAlgorithm());
+                for (Object key : attrs.keySet()) {
+                    if (key instanceof Attributes.Name) {
+                        String n = key.toString();
+                        if (n.toUpperCase(Locale.ENGLISH).endsWith("-DIGEST")) {
+                            String tmp = n.substring(0, n.length() - 7);
+                            if (AlgorithmId.get(tmp).equals(aid)) {
+                                name = n;
+                                break;
+                            }
+                        }
+                    }
+                }
+            } catch (NoSuchAlgorithmException nsae) {
+                // Ignored. Writing new digest entry.
+            }
+
+            if (name == null) {
+                name = digests[i].getAlgorithm() + "-Digest";
+                attrs.putValue(name, base64Digests[i]);
+                update = true;
+            } else {
+                // compare digests, and replace the one in the manifest
+                // if they are different
+                String mfDigest = attrs.getValue(name);
+                if (!mfDigest.equalsIgnoreCase(base64Digests[i])) {
+                    attrs.putValue(name, base64Digests[i]);
+                    update = true;
+                }
+            }
+        }
+        return update;
+    }
+
+    private Attributes getDigestAttributes(
+            ZipEntry ze, ZipFile zf, MessageDigest[] digests)
+            throws IOException {
+
+        String[] base64Digests = getDigests(ze, zf, digests);
+        Attributes attrs = new Attributes();
+
+        for (int i = 0; i < digests.length; i++) {
+            attrs.putValue(digests[i].getAlgorithm() + "-Digest",
+                    base64Digests[i]);
+        }
+        return attrs;
+    }
+
+    /*
+     * Returns manifest entry from given jar file, or null if given jar file
+     * does not have a manifest entry.
+     */
+    private ZipEntry getManifestFile(ZipFile zf) {
+        ZipEntry ze = zf.getEntry(JarFile.MANIFEST_NAME);
+        if (ze == null) {
+            // Check all entries for matching name
+            Enumeration<? extends ZipEntry> enum_ = zf.entries();
+            while (enum_.hasMoreElements() && ze == null) {
+                ze = enum_.nextElement();
+                if (!JarFile.MANIFEST_NAME.equalsIgnoreCase
+                        (ze.getName())) {
+                    ze = null;
+                }
+            }
+        }
+        return ze;
+    }
+
+    private String[] getDigests(
+            ZipEntry ze, ZipFile zf, MessageDigest[] digests)
+            throws IOException {
+
+        int n, i;
+        try (InputStream is = zf.getInputStream(ze)) {
+            long left = ze.getSize();
+            byte[] buffer = new byte[8192];
+            while ((left > 0)
+                    && (n = is.read(buffer, 0, buffer.length)) != -1) {
+                for (i = 0; i < digests.length; i++) {
+                    digests[i].update(buffer, 0, n);
+                }
+                left -= n;
+            }
+        }
+
+        // complete the digests
+        String[] base64Digests = new String[digests.length];
+        for (i = 0; i < digests.length; i++) {
+            base64Digests[i] = Base64.getEncoder()
+                    .encodeToString(digests[i].digest());
+        }
+        return base64Digests;
+    }
+
+    @SuppressWarnings("fallthrough")
+    private int findHeaderEnd(byte[] bs) {
+        // Initial state true to deal with empty header
+        boolean newline = true;     // just met a newline
+        int len = bs.length;
+        for (int i = 0; i < len; i++) {
+            switch (bs[i]) {
+                case '\r':
+                    if (i < len - 1 && bs[i + 1] == '\n') i++;
+                    // fallthrough
+                case '\n':
+                    if (newline) return i + 1;    //+1 to get length
+                    newline = true;
+                    break;
+                default:
+                    newline = false;
+            }
+        }
+        // If header end is not found, it means the MANIFEST.MF has only
+        // the main attributes section and it does not end with 2 newlines.
+        // Returns the whole length so that it can be completely replaced.
+        return len;
+    }
+
+    /*
+     * Try to load the specified signing mechanism.
+     * The URL class loader is used.
+     */
+    @SuppressWarnings("deprecation")
+    private ContentSigner loadSigningMechanism(String signerClassName,
+                                               String signerClassPath) {
+
+        // construct class loader
+        String cpString;   // make sure env.class.path defaults to dot
+
+        // do prepends to get correct ordering
+        cpString = PathList.appendPath(
+                System.getProperty("env.class.path"), null);
+        cpString = PathList.appendPath(
+                System.getProperty("java.class.path"), cpString);
+        cpString = PathList.appendPath(signerClassPath, cpString);
+        URL[] urls = PathList.pathToURLs(cpString);
+        ClassLoader appClassLoader = new URLClassLoader(urls);
+
+        try {
+            // attempt to find signer
+            Class<?> signerClass = appClassLoader.loadClass(signerClassName);
+            Object signer = signerClass.newInstance();
+            return (ContentSigner) signer;
+        } catch (ClassNotFoundException|InstantiationException|
+                IllegalAccessException|ClassCastException e) {
+            throw new IllegalArgumentException(
+                    "Invalid altSigner or altSignerPath", e);
+        }
+    }
+
+    static class SignatureFile {
+
+        /**
+         * SignatureFile
+         */
+        Manifest sf;
+
+        /**
+         * .SF base name
+         */
+        String baseName;
+
+        public SignatureFile(MessageDigest digests[],
+                             Manifest mf,
+                             ManifestDigester md,
+                             String baseName,
+                             boolean signManifest) {
+
+            this.baseName = baseName;
+
+            String version = System.getProperty("java.version");
+            String javaVendor = System.getProperty("java.vendor");
+
+            sf = new Manifest();
+            Attributes mattr = sf.getMainAttributes();
+
+            mattr.putValue(Attributes.Name.SIGNATURE_VERSION.toString(), "1.0");
+            mattr.putValue("Created-By", version + " (" + javaVendor + ")");
+
+            if (signManifest) {
+                for (MessageDigest digest: digests) {
+                    mattr.putValue(digest.getAlgorithm() + "-Digest-Manifest",
+                            Base64.getEncoder().encodeToString(
+                                    md.manifestDigest(digest)));
+                }
+            }
+
+            // create digest of the manifest main attributes
+            ManifestDigester.Entry mde =
+                    md.get(ManifestDigester.MF_MAIN_ATTRS, false);
+            if (mde != null) {
+                for (MessageDigest digest: digests) {
+                    mattr.putValue(digest.getAlgorithm() +
+                                    "-Digest-" + ManifestDigester.MF_MAIN_ATTRS,
+                            Base64.getEncoder().encodeToString(
+                                    mde.digest(digest)));
+                }
+            } else {
+                throw new IllegalStateException
+                        ("ManifestDigester failed to create " +
+                                "Manifest-Main-Attribute entry");
+            }
+
+            // go through the manifest entries and create the digests
+            Map<String, Attributes> entries = sf.getEntries();
+            for (String name: mf.getEntries().keySet()) {
+                mde = md.get(name, false);
+                if (mde != null) {
+                    Attributes attr = new Attributes();
+                    for (MessageDigest digest: digests) {
+                        attr.putValue(digest.getAlgorithm() + "-Digest",
+                                Base64.getEncoder().encodeToString(
+                                        mde.digest(digest)));
+                    }
+                    entries.put(name, attr);
+                }
+            }
+        }
+
+        // Write .SF file
+        public void write(OutputStream out) throws IOException {
+            sf.write(out);
+        }
+
+        // get .SF file name
+        public String getMetaName() {
+            return "META-INF/" + baseName + ".SF";
+        }
+
+        // get .DSA (or .DSA, .EC) file name
+        public String getBlockName(PrivateKey privateKey) {
+            String keyAlgorithm = privateKey.getAlgorithm();
+            return "META-INF/" + baseName + "." + keyAlgorithm;
+        }
+
+        // Generates the PKCS#7 content of block file
+        @SuppressWarnings("deprecation")
+        public byte[] generateBlock(ContentSignerParameters params,
+                                    boolean externalSF,
+                                    ContentSigner signingMechanism)
+                throws NoSuchAlgorithmException,
+                       IOException, CertificateException {
+
+            if (signingMechanism == null) {
+                signingMechanism = new TimestampedSigner();
+            }
+            return signingMechanism.generateSignedData(
+                    params,
+                    externalSF,
+                    params.getTimestampingAuthority() != null
+                        || params.getTimestampingAuthorityCertificate() != null);
+        }
+    }
+
+    @SuppressWarnings("deprecation")
+    class JarSignerParameters implements ContentSignerParameters {
+
+        private String[] args;
+        private URI tsa;
+        private byte[] signature;
+        private String signatureAlgorithm;
+        private X509Certificate[] signerCertificateChain;
+        private byte[] content;
+        private ZipFile source;
+        private String tSAPolicyID;
+        private String tSADigestAlg;
+
+        JarSignerParameters(String[] args, URI tsa,
+                            String tSAPolicyID, String tSADigestAlg,
+                            byte[] signature, String signatureAlgorithm,
+                            X509Certificate[] signerCertificateChain,
+                            byte[] content, ZipFile source) {
+
+            Objects.requireNonNull(signature);
+            Objects.requireNonNull(signatureAlgorithm);
+            Objects.requireNonNull(signerCertificateChain);
+
+            this.args = args;
+            this.tsa = tsa;
+            this.tSAPolicyID = tSAPolicyID;
+            this.tSADigestAlg = tSADigestAlg;
+            this.signature = signature;
+            this.signatureAlgorithm = signatureAlgorithm;
+            this.signerCertificateChain = signerCertificateChain;
+            this.content = content;
+            this.source = source;
+        }
+
+        public String[] getCommandLine() {
+            return args;
+        }
+
+        public URI getTimestampingAuthority() {
+            return tsa;
+        }
+
+        public X509Certificate getTimestampingAuthorityCertificate() {
+            // We don't use this param. Always provide tsaURI.
+            return null;
+        }
+
+        public String getTSAPolicyID() {
+            return tSAPolicyID;
+        }
+
+        public String getTSADigestAlg() {
+            return tSADigestAlg;
+        }
+
+        public byte[] getSignature() {
+            return signature;
+        }
+
+        public String getSignatureAlgorithm() {
+            return signatureAlgorithm;
+        }
+
+        public X509Certificate[] getSignerCertificateChain() {
+            return signerCertificateChain;
+        }
+
+        public byte[] getContent() {
+            return content;
+        }
+
+        public ZipFile getSource() {
+            return source;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/jdk.jartool/share/classes/jdk/security/jarsigner/JarSignerException.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.security.jarsigner;
+
+/**
+ * This exception is thrown when {@link JarSigner#sign} fails.
+ *
+ * @since 1.9
+ */
+@jdk.Exported
+public class JarSignerException extends RuntimeException {
+
+    private static final long serialVersionUID = -4732217075689309530L;
+
+    /**
+     * Constructs a new {@code JarSignerException} with the specified detail
+     * message and cause.
+     * <p>
+     * Note that the detail message associated with
+     * {@code cause} is <i>not</i> automatically incorporated in
+     * this {@code JarSignerException}'s detail message.
+     *
+     * @param message the detail message (which is saved for later retrieval
+     *      by the {@link #getMessage()} method).
+     * @param cause the cause (which is saved for later retrieval by the
+     *      {@link #getCause()} method).  (A {@code null} value is permitted,
+     *      and indicates that the cause is nonexistent or unknown.)
+     */
+    public JarSignerException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
+
--- a/jdk/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/Main.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/src/jdk.jartool/share/classes/sun/security/tools/jarsigner/Main.java	Mon Nov 23 14:37:04 2015 -0500
@@ -29,22 +29,16 @@
 import java.util.*;
 import java.util.zip.*;
 import java.util.jar.*;
-import java.math.BigInteger;
 import java.net.URI;
-import java.net.URISyntaxException;
 import java.text.Collator;
 import java.text.MessageFormat;
 import java.security.cert.Certificate;
 import java.security.cert.X509Certificate;
 import java.security.cert.CertificateException;
 import java.security.*;
-import java.lang.reflect.Constructor;
 
-import com.sun.jarsigner.ContentSigner;
-import com.sun.jarsigner.ContentSignerParameters;
 import java.net.SocketTimeoutException;
 import java.net.URL;
-import java.net.URLClassLoader;
 import java.security.cert.CertPath;
 import java.security.cert.CertPathValidator;
 import java.security.cert.CertificateExpiredException;
@@ -53,11 +47,12 @@
 import java.security.cert.PKIXParameters;
 import java.security.cert.TrustAnchor;
 import java.util.Map.Entry;
+
+import jdk.security.jarsigner.JarSigner;
+import jdk.security.jarsigner.JarSignerException;
 import sun.security.tools.KeyStoreUtil;
-import sun.security.tools.PathList;
 import sun.security.x509.*;
 import sun.security.util.*;
-import java.util.Base64;
 
 
 /**
@@ -88,10 +83,6 @@
         collator.setStrength(Collator.PRIMARY);
     }
 
-    private static final String META_INF = "META-INF/";
-
-    private static final Class<?>[] PARAM_STRING = { String.class };
-
     private static final String NONE = "NONE";
     private static final String P11KEYSTORE = "PKCS11";
 
@@ -133,13 +124,13 @@
     char[] keypass; // private key password
     String sigfile; // name of .SF file
     String sigalg; // name of signature algorithm
-    String digestalg = "SHA-256"; // name of digest algorithm
+    String digestalg; // name of digest algorithm
     String signedjar; // output filename
     String tsaUrl; // location of the Timestamping Authority
     String tsaAlias; // alias for the Timestamping Authority's certificate
     String altCertChain; // file to read alternative cert chain from
     String tSAPolicyID;
-    String tSADigestAlg = "SHA-256";
+    String tSADigestAlg;
     boolean verify = false; // verify the jar
     String verbose = null; // verbose output when signing/verifying
     boolean showcerts = false; // show certs when verifying
@@ -149,9 +140,6 @@
     boolean strict = false;  // treat warnings as error
 
     // read zip entry raw bytes
-    private ByteArrayOutputStream baos = new ByteArrayOutputStream(2048);
-    private byte[] buffer = new byte[8192];
-    private ContentSigner signingMechanism = null;
     private String altSignerClass = null;
     private String altSignerClasspath = null;
     private ZipFile zipFile = null;
@@ -216,6 +204,9 @@
                     if ((keystore != null) || (storepass != null)) {
                         System.out.println(rb.getString("jarsigner.error.") +
                                         e.getMessage());
+                        if (debug) {
+                            e.printStackTrace();
+                        }
                         System.exit(1);
                     }
                 }
@@ -229,12 +220,7 @@
                 loadKeyStore(keystore, true);
                 getAliasInfo(alias);
 
-                // load the alternative signing mechanism
-                if (altSignerClass != null) {
-                    signingMechanism = loadSigningMechanism(altSignerClass,
-                        altSignerClasspath);
-                }
-                signJar(jarfile, alias, args);
+                signJar(jarfile, alias);
             }
         } catch (Exception e) {
             System.out.println(rb.getString("jarsigner.error.") + e);
@@ -626,8 +612,7 @@
                 InputStream is = null;
                 try {
                     is = jf.getInputStream(je);
-                    int n;
-                    while ((n = is.read(buffer, 0, buffer.length)) != -1) {
+                    while (is.read(buffer, 0, buffer.length) != -1) {
                         // we just read. this will throw a SecurityException
                         // if  a signature/digest check fails.
                     }
@@ -1035,7 +1020,6 @@
             return cacheForInKS.get(signer);
         }
 
-        boolean found = false;
         int result = 0;
         List<? extends Certificate> certs = signer.getSignerCertPath().getCertificates();
         for (Certificate c : certs) {
@@ -1058,7 +1042,6 @@
                     }
                     if (alias != null) {
                         storeHash.put(c, "(" + alias + ")");
-                        found = true;
                         result |= IN_KEYSTORE;
                     }
                 }
@@ -1090,7 +1073,7 @@
         return output;
     }
 
-    void signJar(String jarName, String alias, String[] args)
+    void signJar(String jarName, String alias)
         throws Exception {
         boolean aliasUsed = false;
         X509Certificate tsaCert = null;
@@ -1110,17 +1093,17 @@
         for (int j = 0; j < sigfile.length(); j++) {
             char c = sigfile.charAt(j);
             if (!
-                ((c>= 'A' && c<= 'Z') ||
-                (c>= '0' && c<= '9') ||
-                (c == '-') ||
-                (c == '_'))) {
+                    ((c>= 'A' && c<= 'Z') ||
+                            (c>= '0' && c<= '9') ||
+                            (c == '-') ||
+                            (c == '_'))) {
                 if (aliasUsed) {
                     // convert illegal characters from the alias to be _'s
                     c = '_';
                 } else {
-                 throw new
-                   RuntimeException(rb.getString
-                        ("signature.filename.must.consist.of.the.following.characters.A.Z.0.9.or."));
+                    throw new
+                            RuntimeException(rb.getString
+                            ("signature.filename.must.consist.of.the.following.characters.A.Z.0.9.or."));
                 }
             }
             tmpSigFile.append(c);
@@ -1149,275 +1132,88 @@
             error(rb.getString("unable.to.create.")+tmpJarName, ioe);
         }
 
-        PrintStream ps = new PrintStream(fos);
-        ZipOutputStream zos = new ZipOutputStream(ps);
-
-        /* First guess at what they might be - we don't xclude RSA ones. */
-        String sfFilename = (META_INF + sigfile + ".SF").toUpperCase(Locale.ENGLISH);
-        String bkFilename = (META_INF + sigfile + ".DSA").toUpperCase(Locale.ENGLISH);
-
-        Manifest manifest = new Manifest();
-        Map<String,Attributes> mfEntries = manifest.getEntries();
-
-        // The Attributes of manifest before updating
-        Attributes oldAttr = null;
-
-        boolean mfModified = false;
-        boolean mfCreated = false;
-        byte[] mfRawBytes = null;
-
-        try {
-            MessageDigest digests[] = { MessageDigest.getInstance(digestalg) };
+        CertPath cp = CertificateFactory.getInstance("X.509")
+                .generateCertPath(Arrays.asList(certChain));
+        JarSigner.Builder builder = new JarSigner.Builder(privateKey, cp);
 
-            // Check if manifest exists
-            ZipEntry mfFile;
-            if ((mfFile = getManifestFile(zipFile)) != null) {
-                // Manifest exists. Read its raw bytes.
-                mfRawBytes = getBytes(zipFile, mfFile);
-                manifest.read(new ByteArrayInputStream(mfRawBytes));
-                oldAttr = (Attributes)(manifest.getMainAttributes().clone());
-            } else {
-                // Create new manifest
-                Attributes mattr = manifest.getMainAttributes();
-                mattr.putValue(Attributes.Name.MANIFEST_VERSION.toString(),
-                               "1.0");
-                String javaVendor = System.getProperty("java.vendor");
-                String jdkVersion = System.getProperty("java.version");
-                mattr.putValue("Created-By", jdkVersion + " (" +javaVendor
-                               + ")");
-                mfFile = new ZipEntry(JarFile.MANIFEST_NAME);
-                mfCreated = true;
-            }
+        if (verbose != null) {
+            builder.eventHandler((action, file) -> {
+                System.out.println(rb.getString("." + action + ".") + file);
+            });
+        }
+
+        if (digestalg != null) {
+            builder.digestAlgorithm(digestalg);
+        }
+        if (sigalg != null) {
+            builder.signatureAlgorithm(sigalg);
+        }
 
-            /*
-             * For each entry in jar
-             * (except for signature-related META-INF entries),
-             * do the following:
-             *
-             * - if entry is not contained in manifest, add it to manifest;
-             * - if entry is contained in manifest, calculate its hash and
-             *   compare it with the one in the manifest; if they are
-             *   different, replace the hash in the manifest with the newly
-             *   generated one. (This may invalidate existing signatures!)
-             */
-            Vector<ZipEntry> mfFiles = new Vector<>();
+        URI tsaURI = null;
 
-            boolean wasSigned = false;
-
-            for (Enumeration<? extends ZipEntry> enum_=zipFile.entries();
-                        enum_.hasMoreElements();) {
-                ZipEntry ze = enum_.nextElement();
-
-                if (ze.getName().startsWith(META_INF)) {
-                    // Store META-INF files in vector, so they can be written
-                    // out first
-                    mfFiles.addElement(ze);
+        if (tsaUrl != null) {
+            tsaURI = new URI(tsaUrl);
+        } else if (tsaAlias != null) {
+            tsaCert = getTsaCert(tsaAlias);
+            tsaURI = TimestampedSigner.getTimestampingURI(tsaCert);
+        }
 
-                    if (SignatureFileVerifier.isBlockOrSF(
-                            ze.getName().toUpperCase(Locale.ENGLISH))) {
-                        wasSigned = true;
-                    }
-
-                    if (signatureRelated(ze.getName())) {
-                        // ignore signature-related and manifest files
-                        continue;
-                    }
-                }
-
-                if (manifest.getAttributes(ze.getName()) != null) {
-                    // jar entry is contained in manifest, check and
-                    // possibly update its digest attributes
-                    if (updateDigests(ze, zipFile, digests,
-                                      manifest) == true) {
-                        mfModified = true;
-                    }
-                } else if (!ze.isDirectory()) {
-                    // Add entry to manifest
-                    Attributes attrs = getDigestAttributes(ze, zipFile,
-                                                           digests);
-                    mfEntries.put(ze.getName(), attrs);
-                    mfModified = true;
+        if (tsaURI != null) {
+            if (verbose != null) {
+                System.out.println(
+                        rb.getString("requesting.a.signature.timestamp"));
+                if (tsaUrl != null) {
+                    System.out.println(rb.getString("TSA.location.") + tsaUrl);
+                } else if (tsaCert != null) {
+                    System.out.println(rb.getString("TSA.certificate.") +
+                            printCert("", tsaCert, false, null, false));
                 }
             }
-
-            // Recalculate the manifest raw bytes if necessary
-            if (mfModified) {
-                ByteArrayOutputStream baos = new ByteArrayOutputStream();
-                manifest.write(baos);
-                if (wasSigned) {
-                    byte[] newBytes = baos.toByteArray();
-                    if (mfRawBytes != null
-                            && oldAttr.equals(manifest.getMainAttributes())) {
-
-                        /*
-                         * Note:
-                         *
-                         * The Attributes object is based on HashMap and can handle
-                         * continuation columns. Therefore, even if the contents are
-                         * not changed (in a Map view), the bytes that it write()
-                         * may be different from the original bytes that it read()
-                         * from. Since the signature on the main attributes is based
-                         * on raw bytes, we must retain the exact bytes.
-                         */
-
-                        int newPos = findHeaderEnd(newBytes);
-                        int oldPos = findHeaderEnd(mfRawBytes);
-
-                        if (newPos == oldPos) {
-                            System.arraycopy(mfRawBytes, 0, newBytes, 0, oldPos);
-                        } else {
-                            // cat oldHead newTail > newBytes
-                            byte[] lastBytes = new byte[oldPos +
-                                    newBytes.length - newPos];
-                            System.arraycopy(mfRawBytes, 0, lastBytes, 0, oldPos);
-                            System.arraycopy(newBytes, newPos, lastBytes, oldPos,
-                                    newBytes.length - newPos);
-                            newBytes = lastBytes;
-                        }
-                    }
-                    mfRawBytes = newBytes;
-                } else {
-                    mfRawBytes = baos.toByteArray();
-                }
+            builder.tsa(tsaURI);
+            if (tSADigestAlg != null) {
+                builder.setProperty("tsaDigestAlg", tSADigestAlg);
             }
 
-            // Write out the manifest
-            if (mfModified) {
-                // manifest file has new length
-                mfFile = new ZipEntry(JarFile.MANIFEST_NAME);
+            if (tSAPolicyID != null) {
+                builder.setProperty("tsaPolicyId", tSAPolicyID);
             }
+        } else {
+            noTimestamp = true;
+        }
+
+        if (altSignerClass != null) {
+            builder.setProperty("altSigner", altSignerClass);
             if (verbose != null) {
-                if (mfCreated) {
-                    System.out.println(rb.getString(".adding.") +
-                                        mfFile.getName());
-                } else if (mfModified) {
-                    System.out.println(rb.getString(".updating.") +
-                                        mfFile.getName());
-                }
+                System.out.println(
+                        rb.getString("using.an.alternative.signing.mechanism"));
             }
-            zos.putNextEntry(mfFile);
-            zos.write(mfRawBytes);
+        }
 
-            // Calculate SignatureFile (".SF") and SignatureBlockFile
-            ManifestDigester manDig = new ManifestDigester(mfRawBytes);
-            SignatureFile sf = new SignatureFile(digests, manifest, manDig,
-                                                 sigfile, signManifest);
+        if (altSignerClasspath != null) {
+            builder.setProperty("altSignerPath", altSignerClasspath);
+        }
 
-            if (tsaAlias != null) {
-                tsaCert = getTsaCert(tsaAlias);
-            }
+        builder.signerName(sigfile);
 
-            if (tsaUrl == null && tsaCert == null) {
-                noTimestamp = true;
-            }
-
-            SignatureFile.Block block = null;
+        builder.setProperty("sectionsOnly", Boolean.toString(!signManifest));
+        builder.setProperty("internalSF", Boolean.toString(!externalSF));
 
-            try {
-                block =
-                    sf.generateBlock(privateKey, sigalg, certChain,
-                        externalSF, tsaUrl, tsaCert, tSAPolicyID, tSADigestAlg,
-                        signingMechanism, args, zipFile);
-            } catch (SocketTimeoutException e) {
+        try {
+            builder.build().sign(zipFile, fos);
+        } catch (JarSignerException e) {
+            Throwable cause = e.getCause();
+            if (cause != null && cause instanceof SocketTimeoutException) {
                 // Provide a helpful message when TSA is beyond a firewall
                 error(rb.getString("unable.to.sign.jar.") +
-                rb.getString("no.response.from.the.Timestamping.Authority.") +
-                "\n  -J-Dhttp.proxyHost=<hostname>" +
-                "\n  -J-Dhttp.proxyPort=<portnumber>\n" +
-                rb.getString("or") +
-                "\n  -J-Dhttps.proxyHost=<hostname> " +
-                "\n  -J-Dhttps.proxyPort=<portnumber> ", e);
-            }
-
-            sfFilename = sf.getMetaName();
-            bkFilename = block.getMetaName();
-
-            ZipEntry sfFile = new ZipEntry(sfFilename);
-            ZipEntry bkFile = new ZipEntry(bkFilename);
-
-            long time = System.currentTimeMillis();
-            sfFile.setTime(time);
-            bkFile.setTime(time);
-
-            // signature file
-            zos.putNextEntry(sfFile);
-            sf.write(zos);
-            if (verbose != null) {
-                if (zipFile.getEntry(sfFilename) != null) {
-                    System.out.println(rb.getString(".updating.") +
-                                sfFilename);
-                } else {
-                    System.out.println(rb.getString(".adding.") +
-                                sfFilename);
-                }
+                        rb.getString("no.response.from.the.Timestamping.Authority.") +
+                        "\n  -J-Dhttp.proxyHost=<hostname>" +
+                        "\n  -J-Dhttp.proxyPort=<portnumber>\n" +
+                        rb.getString("or") +
+                        "\n  -J-Dhttps.proxyHost=<hostname> " +
+                        "\n  -J-Dhttps.proxyPort=<portnumber> ", e);
+            } else {
+                error(rb.getString("unable.to.sign.jar.")+e.getCause(), e.getCause());
             }
-
-            if (verbose != null) {
-                if (tsaUrl != null || tsaCert != null) {
-                    System.out.println(
-                        rb.getString("requesting.a.signature.timestamp"));
-                }
-                if (tsaUrl != null) {
-                    System.out.println(rb.getString("TSA.location.") + tsaUrl);
-                }
-                if (tsaCert != null) {
-                    URI tsaURI = TimestampedSigner.getTimestampingURI(tsaCert);
-                    if (tsaURI != null) {
-                        System.out.println(rb.getString("TSA.location.") +
-                            tsaURI);
-                    }
-                    System.out.println(rb.getString("TSA.certificate.") +
-                        printCert("", tsaCert, false, null, false));
-                }
-                if (signingMechanism != null) {
-                    System.out.println(
-                        rb.getString("using.an.alternative.signing.mechanism"));
-                }
-            }
-
-            // signature block file
-            zos.putNextEntry(bkFile);
-            block.write(zos);
-            if (verbose != null) {
-                if (zipFile.getEntry(bkFilename) != null) {
-                    System.out.println(rb.getString(".updating.") +
-                        bkFilename);
-                } else {
-                    System.out.println(rb.getString(".adding.") +
-                        bkFilename);
-                }
-            }
-
-            // Write out all other META-INF files that we stored in the
-            // vector
-            for (int i=0; i<mfFiles.size(); i++) {
-                ZipEntry ze = mfFiles.elementAt(i);
-                if (!ze.getName().equalsIgnoreCase(JarFile.MANIFEST_NAME)
-                    && !ze.getName().equalsIgnoreCase(sfFilename)
-                    && !ze.getName().equalsIgnoreCase(bkFilename)) {
-                    writeEntry(zipFile, zos, ze);
-                }
-            }
-
-            // Write out all other files
-            for (Enumeration<? extends ZipEntry> enum_=zipFile.entries();
-                        enum_.hasMoreElements();) {
-                ZipEntry ze = enum_.nextElement();
-
-                if (!ze.getName().startsWith(META_INF)) {
-                    if (verbose != null) {
-                        if (manifest.getAttributes(ze.getName()) != null)
-                          System.out.println(rb.getString(".signing.") +
-                                ze.getName());
-                        else
-                          System.out.println(rb.getString(".adding.") +
-                                ze.getName());
-                    }
-                    writeEntry(zipFile, zos, ze);
-                }
-            }
-        } catch(IOException ioe) {
-            error(rb.getString("unable.to.sign.jar.")+ioe, ioe);
         } finally {
             // close the resouces
             if (zipFile != null) {
@@ -1425,8 +1221,8 @@
                 zipFile = null;
             }
 
-            if (zos != null) {
-                zos.close();
+            if (fos != null) {
+                fos.close();
             }
         }
 
@@ -1527,35 +1323,6 @@
     }
 
     /**
-     * Find the length of header inside bs. The header is a multiple (>=0)
-     * lines of attributes plus an empty line. The empty line is included
-     * in the header.
-     */
-    @SuppressWarnings("fallthrough")
-    private int findHeaderEnd(byte[] bs) {
-        // Initial state true to deal with empty header
-        boolean newline = true;     // just met a newline
-        int len = bs.length;
-        for (int i=0; i<len; i++) {
-            switch (bs[i]) {
-                case '\r':
-                    if (i < len - 1 && bs[i+1] == '\n') i++;
-                    // fallthrough
-                case '\n':
-                    if (newline) return i+1;    //+1 to get length
-                    newline = true;
-                    break;
-                default:
-                    newline = false;
-            }
-        }
-        // If header end is not found, it means the MANIFEST.MF has only
-        // the main attributes section and it does not end with 2 newlines.
-        // Returns the whole length so that it can be completely replaced.
-        return len;
-    }
-
-    /**
      * signature-related files include:
      * . META-INF/MANIFEST.MF
      * . META-INF/SIG-*
@@ -1619,45 +1386,6 @@
         return result;
     }
 
-    private void writeEntry(ZipFile zf, ZipOutputStream os, ZipEntry ze)
-    throws IOException
-    {
-        ZipEntry ze2 = new ZipEntry(ze.getName());
-        ze2.setMethod(ze.getMethod());
-        ze2.setTime(ze.getTime());
-        ze2.setComment(ze.getComment());
-        ze2.setExtra(ze.getExtra());
-        if (ze.getMethod() == ZipEntry.STORED) {
-            ze2.setSize(ze.getSize());
-            ze2.setCrc(ze.getCrc());
-        }
-        os.putNextEntry(ze2);
-        writeBytes(zf, ze, os);
-    }
-
-    /**
-     * Writes all the bytes for a given entry to the specified output stream.
-     */
-    private synchronized void writeBytes
-        (ZipFile zf, ZipEntry ze, ZipOutputStream os) throws IOException {
-        int n;
-
-        InputStream is = null;
-        try {
-            is = zf.getInputStream(ze);
-            long left = ze.getSize();
-
-            while((left > 0) && (n = is.read(buffer, 0, buffer.length)) != -1) {
-                os.write(buffer, 0, n);
-                left -= n;
-            }
-        } finally {
-            if (is != null) {
-                is.close();
-            }
-        }
-    }
-
     void loadKeyStore(String keyStoreName, boolean prompt) {
 
         if (!nullStream && keyStoreName == null) {
@@ -1958,15 +1686,13 @@
         }
     }
 
-    void error(String message)
-    {
+    void error(String message) {
         System.out.println(rb.getString("jarsigner.")+message);
         System.exit(1);
     }
 
 
-    void error(String message, Exception e)
-    {
+    void error(String message, Throwable e) {
         System.out.println(rb.getString("jarsigner.")+message);
         if (debug) {
             e.printStackTrace();
@@ -1990,8 +1716,7 @@
         }
     }
 
-    char[] getPass(String prompt)
-    {
+    char[] getPass(String prompt) {
         System.err.print(prompt);
         System.err.flush();
         try {
@@ -2008,569 +1733,4 @@
         // this shouldn't happen
         return null;
     }
-
-    /*
-     * Reads all the bytes for a given zip entry.
-     */
-    private synchronized byte[] getBytes(ZipFile zf,
-                                         ZipEntry ze) throws IOException {
-        int n;
-
-        InputStream is = null;
-        try {
-            is = zf.getInputStream(ze);
-            baos.reset();
-            long left = ze.getSize();
-
-            while((left > 0) && (n = is.read(buffer, 0, buffer.length)) != -1) {
-                baos.write(buffer, 0, n);
-                left -= n;
-            }
-        } finally {
-            if (is != null) {
-                is.close();
-            }
-        }
-
-        return baos.toByteArray();
-    }
-
-    /*
-     * Returns manifest entry from given jar file, or null if given jar file
-     * does not have a manifest entry.
-     */
-    private ZipEntry getManifestFile(ZipFile zf) {
-        ZipEntry ze = zf.getEntry(JarFile.MANIFEST_NAME);
-        if (ze == null) {
-            // Check all entries for matching name
-            Enumeration<? extends ZipEntry> enum_ = zf.entries();
-            while (enum_.hasMoreElements() && ze == null) {
-                ze = enum_.nextElement();
-                if (!JarFile.MANIFEST_NAME.equalsIgnoreCase
-                    (ze.getName())) {
-                    ze = null;
-                }
-            }
-        }
-        return ze;
-    }
-
-    /*
-     * Computes the digests of a zip entry, and returns them as an array
-     * of base64-encoded strings.
-     */
-    private synchronized String[] getDigests(ZipEntry ze, ZipFile zf,
-                                             MessageDigest[] digests)
-        throws IOException {
-
-        int n, i;
-        InputStream is = null;
-        try {
-            is = zf.getInputStream(ze);
-            long left = ze.getSize();
-            while((left > 0)
-                && (n = is.read(buffer, 0, buffer.length)) != -1) {
-                for (i=0; i<digests.length; i++) {
-                    digests[i].update(buffer, 0, n);
-                }
-                left -= n;
-            }
-        } finally {
-            if (is != null) {
-                is.close();
-            }
-        }
-
-        // complete the digests
-        String[] base64Digests = new String[digests.length];
-        for (i=0; i<digests.length; i++) {
-            base64Digests[i] = Base64.getEncoder().encodeToString(digests[i].digest());
-        }
-        return base64Digests;
-    }
-
-    /*
-     * Computes the digests of a zip entry, and returns them as a list of
-     * attributes
-     */
-    private Attributes getDigestAttributes(ZipEntry ze, ZipFile zf,
-                                           MessageDigest[] digests)
-        throws IOException {
-
-        String[] base64Digests = getDigests(ze, zf, digests);
-        Attributes attrs = new Attributes();
-
-        for (int i=0; i<digests.length; i++) {
-            attrs.putValue(digests[i].getAlgorithm()+"-Digest",
-                           base64Digests[i]);
-        }
-        return attrs;
-    }
-
-    /*
-     * Updates the digest attributes of a manifest entry, by adding or
-     * replacing digest values.
-     * A digest value is added if the manifest entry does not contain a digest
-     * for that particular algorithm.
-     * A digest value is replaced if it is obsolete.
-     *
-     * Returns true if the manifest entry has been changed, and false
-     * otherwise.
-     */
-    private boolean updateDigests(ZipEntry ze, ZipFile zf,
-                                  MessageDigest[] digests,
-                                  Manifest mf) throws IOException {
-        boolean update = false;
-
-        Attributes attrs = mf.getAttributes(ze.getName());
-        String[] base64Digests = getDigests(ze, zf, digests);
-
-        for (int i=0; i<digests.length; i++) {
-            // The entry name to be written into attrs
-            String name = null;
-            try {
-                // Find if the digest already exists
-                AlgorithmId aid = AlgorithmId.get(digests[i].getAlgorithm());
-                for (Object key: attrs.keySet()) {
-                    if (key instanceof Attributes.Name) {
-                        String n = ((Attributes.Name)key).toString();
-                        if (n.toUpperCase(Locale.ENGLISH).endsWith("-DIGEST")) {
-                            String tmp = n.substring(0, n.length() - 7);
-                            if (AlgorithmId.get(tmp).equals(aid)) {
-                                name = n;
-                                break;
-                            }
-                        }
-                    }
-                }
-            } catch (NoSuchAlgorithmException nsae) {
-                // Ignored. Writing new digest entry.
-            }
-
-            if (name == null) {
-                name = digests[i].getAlgorithm()+"-Digest";
-                attrs.putValue(name, base64Digests[i]);
-                update=true;
-            } else {
-                // compare digests, and replace the one in the manifest
-                // if they are different
-                String mfDigest = attrs.getValue(name);
-                if (!mfDigest.equalsIgnoreCase(base64Digests[i])) {
-                    attrs.putValue(name, base64Digests[i]);
-                    update=true;
-                }
-            }
-        }
-        return update;
-    }
-
-    /*
-     * Try to load the specified signing mechanism.
-     * The URL class loader is used.
-     */
-    private ContentSigner loadSigningMechanism(String signerClassName,
-        String signerClassPath) throws Exception {
-
-        // construct class loader
-        String cpString = null;   // make sure env.class.path defaults to dot
-
-        // do prepends to get correct ordering
-        cpString = PathList.appendPath(System.getProperty("env.class.path"), cpString);
-        cpString = PathList.appendPath(System.getProperty("java.class.path"), cpString);
-        cpString = PathList.appendPath(signerClassPath, cpString);
-        URL[] urls = PathList.pathToURLs(cpString);
-        ClassLoader appClassLoader = new URLClassLoader(urls);
-
-        // attempt to find signer
-        Class<?> signerClass = appClassLoader.loadClass(signerClassName);
-
-        // Check that it implements ContentSigner
-        Object signer = signerClass.newInstance();
-        if (!(signer instanceof ContentSigner)) {
-            MessageFormat form = new MessageFormat(
-                rb.getString("signerClass.is.not.a.signing.mechanism"));
-            Object[] source = {signerClass.getName()};
-            throw new IllegalArgumentException(form.format(source));
-        }
-        return (ContentSigner)signer;
-    }
 }
-
-class SignatureFile {
-
-    /** SignatureFile */
-    Manifest sf;
-
-    /** .SF base name */
-    String baseName;
-
-    public SignatureFile(MessageDigest digests[],
-                         Manifest mf,
-                         ManifestDigester md,
-                         String baseName,
-                         boolean signManifest)
-
-    {
-        this.baseName = baseName;
-
-        String version = System.getProperty("java.version");
-        String javaVendor = System.getProperty("java.vendor");
-
-        sf = new Manifest();
-        Attributes mattr = sf.getMainAttributes();
-
-        mattr.putValue(Attributes.Name.SIGNATURE_VERSION.toString(), "1.0");
-        mattr.putValue("Created-By", version + " (" + javaVendor + ")");
-
-        if (signManifest) {
-            // sign the whole manifest
-            for (int i=0; i < digests.length; i++) {
-                mattr.putValue(digests[i].getAlgorithm()+"-Digest-Manifest",
-                               Base64.getEncoder().encodeToString(md.manifestDigest(digests[i])));
-            }
-        }
-
-        // create digest of the manifest main attributes
-        ManifestDigester.Entry mde =
-                md.get(ManifestDigester.MF_MAIN_ATTRS, false);
-        if (mde != null) {
-            for (int i=0; i < digests.length; i++) {
-                mattr.putValue(digests[i].getAlgorithm() +
-                        "-Digest-" + ManifestDigester.MF_MAIN_ATTRS,
-                        Base64.getEncoder().encodeToString(mde.digest(digests[i])));
-            }
-        } else {
-            throw new IllegalStateException
-                ("ManifestDigester failed to create " +
-                "Manifest-Main-Attribute entry");
-        }
-
-        /* go through the manifest entries and create the digests */
-
-        Map<String,Attributes> entries = sf.getEntries();
-        Iterator<Map.Entry<String,Attributes>> mit =
-                                mf.getEntries().entrySet().iterator();
-        while(mit.hasNext()) {
-            Map.Entry<String,Attributes> e = mit.next();
-            String name = e.getKey();
-            mde = md.get(name, false);
-            if (mde != null) {
-                Attributes attr = new Attributes();
-                for (int i=0; i < digests.length; i++) {
-                    attr.putValue(digests[i].getAlgorithm()+"-Digest",
-                                  Base64.getEncoder().encodeToString(mde.digest(digests[i])));
-                }
-                entries.put(name, attr);
-            }
-        }
-    }
-
-    /**
-     * Writes the SignatureFile to the specified OutputStream.
-     *
-     * @param out the output stream
-     * @exception IOException if an I/O error has occurred
-     */
-
-    public void write(OutputStream out) throws IOException
-    {
-        sf.write(out);
-    }
-
-    /**
-     * get .SF file name
-     */
-    public String getMetaName()
-    {
-        return "META-INF/"+ baseName + ".SF";
-    }
-
-    /**
-     * get base file name
-     */
-    public String getBaseName()
-    {
-        return baseName;
-    }
-
-    /*
-     * Generate a signed data block.
-     * If a URL or a certificate (containing a URL) for a Timestamping
-     * Authority is supplied then a signature timestamp is generated and
-     * inserted into the signed data block.
-     *
-     * @param sigalg signature algorithm to use, or null to use default
-     * @param tsaUrl The location of the Timestamping Authority. If null
-     *               then no timestamp is requested.
-     * @param tsaCert The certificate for the Timestamping Authority. If null
-     *               then no timestamp is requested.
-     * @param signingMechanism The signing mechanism to use.
-     * @param args The command-line arguments to jarsigner.
-     * @param zipFile The original source Zip file.
-     */
-    @SuppressWarnings("deprecation")
-    public Block generateBlock(PrivateKey privateKey,
-                               String sigalg,
-                               X509Certificate[] certChain,
-                               boolean externalSF, String tsaUrl,
-                               X509Certificate tsaCert,
-                               String tSAPolicyID,
-                               String tSADigestAlg,
-                               ContentSigner signingMechanism,
-                               String[] args, ZipFile zipFile)
-        throws NoSuchAlgorithmException, InvalidKeyException, IOException,
-            SignatureException, CertificateException
-    {
-        return new Block(this, privateKey, sigalg, certChain, externalSF,
-                tsaUrl, tsaCert, tSAPolicyID, tSADigestAlg, signingMechanism, args, zipFile);
-    }
-
-
-    public static class Block {
-
-        private byte[] block;
-        private String blockFileName;
-
-        /*
-         * Construct a new signature block.
-         */
-        @SuppressWarnings("deprecation")
-        Block(SignatureFile sfg, PrivateKey privateKey, String sigalg,
-            X509Certificate[] certChain, boolean externalSF, String tsaUrl,
-            X509Certificate tsaCert, String tSAPolicyID, String tSADigestAlg,
-            ContentSigner signingMechanism, String[] args, ZipFile zipFile)
-            throws NoSuchAlgorithmException, InvalidKeyException, IOException,
-            SignatureException, CertificateException {
-
-            Principal issuerName = certChain[0].getIssuerDN();
-            if (!(issuerName instanceof X500Name)) {
-                // must extract the original encoded form of DN for subsequent
-                // name comparison checks (converting to a String and back to
-                // an encoded DN could cause the types of String attribute
-                // values to be changed)
-                X509CertInfo tbsCert = new
-                    X509CertInfo(certChain[0].getTBSCertificate());
-                issuerName = (Principal)
-                    tbsCert.get(X509CertInfo.ISSUER + "." +
-                                X509CertInfo.DN_NAME);
-                }
-            BigInteger serial = certChain[0].getSerialNumber();
-
-            String signatureAlgorithm;
-            String keyAlgorithm = privateKey.getAlgorithm();
-            /*
-             * If no signature algorithm was specified, we choose a
-             * default that is compatible with the private key algorithm.
-             */
-            if (sigalg == null) {
-
-                if (keyAlgorithm.equalsIgnoreCase("DSA"))
-                    signatureAlgorithm = "SHA256withDSA";
-                else if (keyAlgorithm.equalsIgnoreCase("RSA"))
-                    signatureAlgorithm = "SHA256withRSA";
-                else if (keyAlgorithm.equalsIgnoreCase("EC"))
-                    signatureAlgorithm = "SHA256withECDSA";
-                else
-                    throw new RuntimeException("private key is not a DSA or "
-                                               + "RSA key");
-            } else {
-                signatureAlgorithm = sigalg;
-            }
-
-            // check common invalid key/signature algorithm combinations
-            String sigAlgUpperCase = signatureAlgorithm.toUpperCase(Locale.ENGLISH);
-            if ((sigAlgUpperCase.endsWith("WITHRSA") &&
-                !keyAlgorithm.equalsIgnoreCase("RSA")) ||
-                (sigAlgUpperCase.endsWith("WITHECDSA") &&
-                !keyAlgorithm.equalsIgnoreCase("EC")) ||
-                (sigAlgUpperCase.endsWith("WITHDSA") &&
-                !keyAlgorithm.equalsIgnoreCase("DSA"))) {
-                throw new SignatureException
-                    ("private key algorithm is not compatible with signature algorithm");
-            }
-
-            blockFileName = "META-INF/"+sfg.getBaseName()+"."+keyAlgorithm;
-
-            AlgorithmId sigAlg = AlgorithmId.get(signatureAlgorithm);
-            AlgorithmId digEncrAlg = AlgorithmId.get(keyAlgorithm);
-
-            Signature sig = Signature.getInstance(signatureAlgorithm);
-            sig.initSign(privateKey);
-
-            ByteArrayOutputStream baos = new ByteArrayOutputStream();
-            sfg.write(baos);
-
-            byte[] content = baos.toByteArray();
-
-            sig.update(content);
-            byte[] signature = sig.sign();
-
-            // Timestamp the signature and generate the signature block file
-            if (signingMechanism == null) {
-                signingMechanism = new TimestampedSigner();
-            }
-            URI tsaUri = null;
-            try {
-                if (tsaUrl != null) {
-                    tsaUri = new URI(tsaUrl);
-                }
-            } catch (URISyntaxException e) {
-                throw new IOException(e);
-            }
-
-            // Assemble parameters for the signing mechanism
-            ContentSignerParameters params =
-                new JarSignerParameters(args, tsaUri, tsaCert, tSAPolicyID,
-                        tSADigestAlg, signature,
-                    signatureAlgorithm, certChain, content, zipFile);
-
-            // Generate the signature block
-            block = signingMechanism.generateSignedData(
-                    params, externalSF, (tsaUrl != null || tsaCert != null));
-        }
-
-        /*
-         * get block file name.
-         */
-        public String getMetaName()
-        {
-            return blockFileName;
-        }
-
-        /**
-         * Writes the block file to the specified OutputStream.
-         *
-         * @param out the output stream
-         * @exception IOException if an I/O error has occurred
-         */
-
-        public void write(OutputStream out) throws IOException
-        {
-            out.write(block);
-        }
-    }
-}
-
-
-/*
- * This object encapsulates the parameters used to perform content signing.
- */
-@SuppressWarnings("deprecation")
-class JarSignerParameters implements ContentSignerParameters {
-
-    private String[] args;
-    private URI tsa;
-    private X509Certificate tsaCertificate;
-    private byte[] signature;
-    private String signatureAlgorithm;
-    private X509Certificate[] signerCertificateChain;
-    private byte[] content;
-    private ZipFile source;
-    private String tSAPolicyID;
-    private String tSADigestAlg;
-
-    /**
-     * Create a new object.
-     */
-    JarSignerParameters(String[] args, URI tsa, X509Certificate tsaCertificate,
-        String tSAPolicyID, String tSADigestAlg,
-        byte[] signature, String signatureAlgorithm,
-        X509Certificate[] signerCertificateChain, byte[] content,
-        ZipFile source) {
-
-        if (signature == null || signatureAlgorithm == null ||
-            signerCertificateChain == null || tSADigestAlg == null) {
-            throw new NullPointerException();
-        }
-        this.args = args;
-        this.tsa = tsa;
-        this.tsaCertificate = tsaCertificate;
-        this.tSAPolicyID = tSAPolicyID;
-        this.tSADigestAlg = tSADigestAlg;
-        this.signature = signature;
-        this.signatureAlgorithm = signatureAlgorithm;
-        this.signerCertificateChain = signerCertificateChain;
-        this.content = content;
-        this.source = source;
-    }
-
-    /**
-     * Retrieves the command-line arguments.
-     *
-     * @return The command-line arguments. May be null.
-     */
-    public String[] getCommandLine() {
-        return args;
-    }
-
-    /**
-     * Retrieves the identifier for a Timestamping Authority (TSA).
-     *
-     * @return The TSA identifier. May be null.
-     */
-    public URI getTimestampingAuthority() {
-        return tsa;
-    }
-
-    /**
-     * Retrieves the certificate for a Timestamping Authority (TSA).
-     *
-     * @return The TSA certificate. May be null.
-     */
-    public X509Certificate getTimestampingAuthorityCertificate() {
-        return tsaCertificate;
-    }
-
-    public String getTSAPolicyID() {
-        return tSAPolicyID;
-    }
-
-    public String getTSADigestAlg() {
-        return tSADigestAlg;
-    }
-
-    /**
-     * Retrieves the signature.
-     *
-     * @return The non-null signature bytes.
-     */
-    public byte[] getSignature() {
-        return signature;
-    }
-
-    /**
-     * Retrieves the name of the signature algorithm.
-     *
-     * @return The non-null string name of the signature algorithm.
-     */
-    public String getSignatureAlgorithm() {
-        return signatureAlgorithm;
-    }
-
-    /**
-     * Retrieves the signer's X.509 certificate chain.
-     *
-     * @return The non-null array of X.509 public-key certificates.
-     */
-    public X509Certificate[] getSignerCertificateChain() {
-        return signerCertificateChain;
-    }
-
-    /**
-     * Retrieves the content that was signed.
-     *
-     * @return The content bytes. May be null.
-     */
-    public byte[] getContent() {
-        return content;
-    }
-
-    /**
-     * Retrieves the original source ZIP file before it was signed.
-     *
-     * @return The original ZIP file. May be null.
-     */
-    public ZipFile getSource() {
-        return source;
-    }
-}
--- a/jdk/test/ProblemList.txt	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/test/ProblemList.txt	Mon Nov 23 14:37:04 2015 -0500
@@ -234,6 +234,7 @@
 sun/security/tools/jarsigner/warnings/BadKeyUsageTest.java      generic-all
 
 # 8077138: Some PKCS11 tests fail because NSS library is not initialized
+# 8023434: NSS initialization failed
 sun/security/pkcs11/Cipher/ReinitCipher.java                    windows-all
 sun/security/pkcs11/Cipher/TestPKCS5PaddingError.java           windows-all
 sun/security/pkcs11/Cipher/TestRSACipher.java                   windows-all
@@ -263,6 +264,7 @@
 sun/security/pkcs11/Secmod/Crypto.java                          windows-all
 sun/security/pkcs11/Secmod/GetPrivateKey.java                   windows-all
 sun/security/pkcs11/Secmod/JksSetPrivateKey.java                windows-all
+sun/security/pkcs11/Secmod/LoadKeystore.java                    windows-all
 sun/security/pkcs11/SecureRandom/Basic.java                     windows-all
 sun/security/pkcs11/SecureRandom/TestDeserialization.java       windows-all
 sun/security/pkcs11/Serialize/SerializeProvider.java            windows-all
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/awt/PrintJob/PrinterException.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+/*
+ * @test
+ * @bug 7161283
+ * @summary  Toolkit.getPrintJob throws NPE if no printer available
+ * @run main PrinterException
+ */
+import java.awt.Frame;
+import java.awt.JobAttributes;
+import java.awt.PrintJob;
+import java.awt.Toolkit;
+
+public class PrinterException {
+    public static void main(String[] args) {
+        Toolkit tk = Toolkit.getDefaultToolkit();
+        PrintJob pj = null;
+
+        int[][] pageRange = new int[][]{new int[]{1,1}};
+        JobAttributes ja = new JobAttributes(1,
+                java.awt.JobAttributes.DefaultSelectionType.ALL,
+                JobAttributes.DestinationType.FILE, JobAttributes.DialogType.NATIVE,
+                "", Integer.MAX_VALUE, 1,
+                JobAttributes.MultipleDocumentHandlingType.SEPARATE_DOCUMENTS_UNCOLLATED_COPIES,
+                 pageRange, "", JobAttributes.SidesType.ONE_SIDED);
+        Frame testFrame = new Frame("print");
+        if (tk != null) {
+            pj = tk.getPrintJob(testFrame, null, ja, null);
+        }
+    }
+}
--- a/jdk/test/java/awt/applet/Applet/AppletFlipBuffer.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/test/java/awt/applet/Applet/AppletFlipBuffer.java	Mon Nov 23 14:37:04 2015 -0500
@@ -21,35 +21,72 @@
  * questions.
  */
 
-/* @test
-   @bug 8130390
-   @summary Applet fails to launch on virtual desktop
-   @author Semyon Sadetsky
-  */
+import java.applet.Applet;
+import java.awt.AWTException;
+import java.awt.BufferCapabilities;
+import java.awt.BufferCapabilities.FlipContents;
+import java.awt.Frame;
+import java.awt.ImageCapabilities;
+import java.util.HashSet;
+import java.util.Set;
 
 import sun.awt.AWTAccessor;
+import sun.awt.AWTAccessor.ComponentAccessor;
 
-import java.applet.Applet;
-import java.awt.*;
+import static java.awt.BufferCapabilities.FlipContents.BACKGROUND;
+import static java.awt.BufferCapabilities.FlipContents.COPIED;
+import static java.awt.BufferCapabilities.FlipContents.PRIOR;
+import static java.awt.BufferCapabilities.FlipContents.UNDEFINED;
+
+/**
+ * @test
+ * @bug 8130390 8134732
+ * @summary Applet fails to launch on virtual desktop
+ * @modules java.desktop/sun.awt
+ * @author Semyon Sadetsky
+ */
+public final class AppletFlipBuffer {
 
-public class AppletFlipBuffer {
-    public static void main(String[] args) throws Exception {
+    static final ImageCapabilities[] ics = {new ImageCapabilities(true),
+                                            new ImageCapabilities(false)};
+    static final FlipContents[] cntx = {UNDEFINED, BACKGROUND, PRIOR, COPIED};
+    static final Set<BufferCapabilities> bcs = new HashSet<>();
+
+    static {
+        for (final ImageCapabilities icFront : ics) {
+            for (final ImageCapabilities icBack : ics) {
+                for (final FlipContents cnt : cntx) {
+                    bcs.add(new BufferCapabilities(icFront, icBack, cnt));
+                }
+            }
+        }
+    }
+
+    public static void main(final String[] args) throws Exception {
         Applet applet = new Applet();
-        AWTAccessor.ComponentAccessor componentAccessor
-                = AWTAccessor.getComponentAccessor();
-        BufferCapabilities caps = new BufferCapabilities(
-                new ImageCapabilities(true), new ImageCapabilities(true),
-                BufferCapabilities.FlipContents.BACKGROUND);
         Frame frame = new Frame();
         try {
+            frame.setSize(10, 10);
             frame.add(applet);
             frame.setUndecorated(true);
             frame.setVisible(true);
-            componentAccessor.createBufferStrategy(applet, 2, caps);
+            test(applet);
             System.out.println("ok");
-        }
-        finally {
+        } finally {
             frame.dispose();
         }
     }
+
+    private static void test(final Applet applet) {
+        ComponentAccessor acc = AWTAccessor.getComponentAccessor();
+        for (int i = 1; i < 10; ++i) {
+            for (final BufferCapabilities caps : bcs) {
+                try {
+                    acc.createBufferStrategy(applet, i, caps);
+                } catch (final AWTException ignored) {
+                    // this kind of buffer strategy is not supported
+                }
+            }
+        }
+    }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/awt/font/JNICheck/JNICheck.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import javax.swing.*;
+
+public class JNICheck {
+    public static void main(String[] args) throws Exception {
+    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
+ }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/awt/font/JNICheck/JNICheck.sh	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,61 @@
+#!/bin/ksh -p
+#
+# Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# This code is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License version 2 only, as
+# published by the Free Software Foundation.
+#
+# This code is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+# version 2 for more details (a copy is included in the LICENSE file that
+# accompanied this code).
+#
+# You should have received a copy of the GNU General Public License version
+# 2 along with this work; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+# or visit www.oracle.com if you need additional information or have any
+# questions.
+#
+#
+#   @test
+#   @bug        6430247 8130507 8020448
+#   @summary    Tests that there are no JNI warnings.
+#   @compile JNICheck.java
+#   @run shell/timeout=300 JNICheck.sh
+#
+OS=`uname`
+
+# pick up the compiled class files.
+if [ -z "${TESTCLASSES}" ]; then
+  CP="."
+else
+  CP="${TESTCLASSES}"
+fi
+
+if [ $OS != SunOS -a $OS != Linux ]
+then
+    exit 0
+fi
+
+if [ -z "${TESTJAVA}" ] ; then
+   JAVA_HOME=../../../../../build/solaris-sparc
+else
+   JAVA_HOME=$TESTJAVA
+fi
+
+$JAVA_HOME/bin/java ${TESTVMOPTS} \
+    -cp "${CP}" -Xcheck:jni JNICheck > "${CP}"/log.txt
+
+# any messages logged may indicate a failure.
+if [ -s "${CP}"/log.txt ]; then
+    echo "Test failed"
+    exit 1
+fi
+
+echo "Test passed"
+exit 0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/awt/font/JNICheck/LoadFontsJNICheck.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+public class LoadFontsJNICheck {
+    public static void main(String[] args) throws Exception {
+    java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts();
+ }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/awt/font/JNICheck/LoadFontsJNICheck.sh	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,62 @@
+#!/bin/sh
+#
+# Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+#
+# This code is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License version 2 only, as
+# published by the Free Software Foundation.
+#
+# This code is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+# version 2 for more details (a copy is included in the LICENSE file that
+# accompanied this code).
+#
+# You should have received a copy of the GNU General Public License version
+# 2 along with this work; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+# or visit www.oracle.com if you need additional information or have any
+# questions.
+
+#
+#   @test
+#   @bug        8138817
+#   @summary    Tests that there are no JNI warnings about local references.
+#   @compile LoadFontsJNICheck.java
+#   @run shell/timeout=300 LoadFontsJNICheck.sh
+#
+OS=`uname`
+
+# pick up the compiled class files.
+if [ -z "${TESTCLASSES}" ]; then
+  CP="."
+else
+  CP="${TESTCLASSES}"
+fi
+
+if [ $OS != Darwin ]
+then
+    exit 0
+fi
+
+if [ -z "${TESTJAVA}" ] ; then
+   JAVACMD=java
+else
+   JAVACMD=$TESTJAVA/bin/java
+fi
+
+$JAVACMD ${TESTVMOPTS} \
+    -cp "${CP}" -Xcheck:jni LoadFontsJNICheck | grep "local refs"  > "${CP}"/log.txt
+
+# any messages logged may indicate a failure.
+if [ -s "${CP}"/log.txt ]; then
+    echo "Test failed"
+    cat "${CP}"/log.txt
+    exit 1
+fi
+
+echo "Test passed"
+exit 0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/awt/im/8132503/bug8132503.html	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,38 @@
+<!--
+ Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+
+ This code is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License version 2 only, as
+ published by the Free Software Foundation.
+
+ This code is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ version 2 for more details (a copy is included in the LICENSE file that
+ accompanied this code).
+
+ You should have received a copy of the GNU General Public License version
+ 2 along with this work; if not, write to the Free Software Foundation,
+ Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+
+ Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ or visit www.oracle.com if you need additional information or have any
+ questions.
+-->
+
+<html>
+<body>
+Verify that Chinese full stop symbol can be entered in JTextArea with Pinyin input method (IM).
+
+This test is for OS X only. For other platforms please simply press "Pass".
+
+1. Go to "System Preferences -> Keyboard -> Input Sources" and add "Pinyin – Traditional" or "Pinyin – Simplified" IM from Chinese language group.
+2. Set current IM to "Pinyin".
+3. Set focus to the text area of the test and press "dot" character on the keyboard.
+4. Set current IM to the IM used before "Pinyin" was set.
+5. If "。" character is displayed in the text area, press "Pass", if "." character is displayed, press "Fail".
+
+<applet  code="bug8132503.class" width=400 height=400></applet>
+</body>
+</html>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/awt/im/8132503/bug8132503.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test
+   @bug 8132503
+   @summary [macosx] Chinese full stop symbol cannot be entered with Pinyin IM on OS X
+   @author Anton Litvinov
+   @run applet/manual=yesno bug8132503.html
+ */
+
+import javax.swing.JApplet;
+import javax.swing.JScrollPane;
+import javax.swing.JTextArea;
+import javax.swing.SwingUtilities;
+
+public class bug8132503 extends JApplet {
+    @Override
+    public void init() {
+        SwingUtilities.invokeLater(new Runnable() {
+            @Override
+            public void run() {
+                JTextArea textArea = new JTextArea("Text area of the test.", 40, 40);
+                add(new JScrollPane(textArea));
+            }
+        });
+    }
+}
--- a/jdk/test/java/awt/print/PrinterJob/PaintText.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/test/java/awt/print/PrinterJob/PaintText.java	Mon Nov 23 14:37:04 2015 -0500
@@ -75,8 +75,8 @@
         f.show();
 
         /* Non-jtreg execution will display the dialog */
-        if (System.getProperty("test.java") == null) {
-             if (!pjob.printDialog()) {
+        if (System.getProperty("test.jdk") == null) {
+            if (!pjob.printDialog()) {
                 return;
             }
         }
@@ -84,6 +84,8 @@
             pjob.print();
         } catch (PrinterException e) {
             throw new RuntimeException(e.getMessage());
+        } finally {
+            f.dispose();
         }
     }
 
--- a/jdk/test/java/beans/XMLDecoder/8028054/Task.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/test/java/beans/XMLDecoder/8028054/Task.java	Mon Nov 23 14:37:04 2015 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -22,12 +22,17 @@
  */
 
 import java.util.ArrayList;
-import java.util.Enumeration;
 import java.util.List;
-import java.util.jar.JarEntry;
-import java.util.jar.JarFile;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+import java.net.*;
+import java.io.*;
+import java.nio.file.FileSystem;
+import java.nio.file.FileSystems;
+import java.nio.file.FileSystemNotFoundException;
+import java.nio.file.ProviderNotFoundException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.function.Predicate;
 
 abstract class Task<T> implements Runnable {
     private transient boolean working = true;
@@ -74,26 +79,74 @@
     }
 
     static List<Class<?>> getClasses(int count) throws Exception {
-        String resource = ClassLoader.getSystemClassLoader().getResource("java/lang/Object.class").toString();
+        List<Class<?>> classes = new ArrayList<>();
+        FileSystem fs = null;
+
+        try {
+            fs = FileSystems.getFileSystem(URI.create("jrt:/"));
+        } catch (ProviderNotFoundException | FileSystemNotFoundException e) {
+            throw new RuntimeException("FAIL - JRT Filesystem not found");
+        }
+
+        List<String> fileNames;
+        Path modules = fs.getPath("/modules");
 
-        Pattern pattern = Pattern.compile("jar:file:(.*)!.*");
-        Matcher matcher = pattern.matcher(resource);
-        matcher.matches();
-        resource = matcher.group(1);
+        Predicate<String> startsWithJavaBase          = path -> path.toString().startsWith("java.base/java");
+        Predicate<String> startsWithJavaDesktop       = path -> path.toString().startsWith("java.desktop/java");
+        Predicate<String> startsWithJavaDataTransfer  = path -> path.toString().startsWith("java.datatransfer/java");
+        Predicate<String> startsWithJavaRMI           = path -> path.toString().startsWith("java.rmi/java");
+        Predicate<String> startsWithJavaSmartCardIO   = path -> path.toString().startsWith("java.smartcardio/java");
+        Predicate<String> startsWithJavaManagement    = path -> path.toString().startsWith("java.management/java");
+        Predicate<String> startsWithJavaXML           = path -> path.toString().startsWith("java.xml/java");
+        Predicate<String> startsWithJavaXMLBind       = path -> path.toString().startsWith("java.xml.bind/java");
+        Predicate<String> startsWithJavaScripting     = path -> path.toString().startsWith("java.scripting/java");
+        Predicate<String> startsWithJavaNaming        = path -> path.toString().startsWith("java.naming/java");
+        Predicate<String> startsWithJavaSQL           = path -> path.toString().startsWith("java.sql/java");
+        Predicate<String> startsWithJavaActivation    = path -> path.toString().startsWith("java.activation/java");
+        Predicate<String> startsWithJavaCompiler      = path -> path.toString().startsWith("java.compiler/java");
+        Predicate<String> startsWithJavaAnnotations   = path -> path.toString().startsWith("java.annotations/java");
+        Predicate<String> startsWithJavaTransaction   = path -> path.toString().startsWith("java.transaction/java");
+        Predicate<String> startsWithJavaLogging       = path -> path.toString().startsWith("java.logging/java");
+        Predicate<String> startsWithJavaCorba         = path -> path.toString().startsWith("java.corba/java");
+        Predicate<String> startsWithJavaPrefs         = path -> path.toString().startsWith("java.prefs/java");
 
-        List<Class<?>> classes = new ArrayList<>();
-        try (JarFile jarFile = new JarFile(resource)) {
-            Enumeration<JarEntry> entries = jarFile.entries();
-            while (entries.hasMoreElements()) {
-                String name = entries.nextElement().getName();
-                if (name.startsWith("java") && name.endsWith(".class")) {
-                    classes.add(Class.forName(name.substring(0, name.indexOf(".")).replace('/', '.')));
-                    if (count == classes.size()) {
-                        break;
-                    }
-                }
+        fileNames = Files.walk(modules)
+                .map(Path::toString)
+                .filter(path -> path.toString().contains("java"))
+                .map(s -> s.substring(9))  // remove /modules/ from beginning
+                .filter(startsWithJavaBase
+                    .or(startsWithJavaDesktop)
+                    .or(startsWithJavaDataTransfer)
+                    .or(startsWithJavaRMI)
+                    .or(startsWithJavaSmartCardIO)
+                    .or(startsWithJavaManagement)
+                    .or(startsWithJavaXML)
+                    .or(startsWithJavaXMLBind)
+                    .or(startsWithJavaScripting)
+                    .or(startsWithJavaNaming)
+                    .or(startsWithJavaSQL)
+                    .or(startsWithJavaActivation)
+                    .or(startsWithJavaCompiler)
+                    .or(startsWithJavaAnnotations)
+                    .or(startsWithJavaTransaction)
+                    .or(startsWithJavaLogging)
+                    .or(startsWithJavaCorba)
+                    .or(startsWithJavaPrefs))
+                .map(s -> s.replace('/', '.'))
+                .filter(path -> path.toString().endsWith(".class"))
+                .map(s -> s.substring(0, s.length() - 6))  // drop .class
+                .map(s -> s.substring(s.indexOf(".")))
+                .filter(path -> path.toString().contains("java"))
+                .map(s -> s.substring(s.indexOf("java")))
+                .collect(Collectors.toList());
+
+        for (String name : fileNames) {
+            classes.add(Class.forName(name));
+            if (count == classes.size()) {
+                break;
             }
         }
+
         return classes;
     }
 }
--- a/jdk/test/java/beans/XMLDecoder/8028054/TestConstructorFinder.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/test/java/beans/XMLDecoder/8028054/TestConstructorFinder.java	Mon Nov 23 14:37:04 2015 -0500
@@ -73,8 +73,7 @@
             }
             Task.print(working + " out of " + alive + " threads are working");
             if ((working == 0) && (++alarm == 10)) {
-                Task.print("DEADLOCK DETECTED");
-                System.exit(100);
+                throw new RuntimeException("FAIL - DEADLOCK DETECTED");
             }
             Thread.sleep(1000);
         }
--- a/jdk/test/java/beans/XMLDecoder/8028054/TestMethodFinder.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/test/java/beans/XMLDecoder/8028054/TestMethodFinder.java	Mon Nov 23 14:37:04 2015 -0500
@@ -73,8 +73,7 @@
             }
             Task.print(working + " out of " + alive + " threads are working");
             if ((working == 0) && (++alarm == 10)) {
-                Task.print("DEADLOCK DETECTED");
-                System.exit(100);
+                throw new RuntimeException("FAIL - DEADLOCK DETECTED");
             }
             Thread.sleep(1000);
         }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/ProcessBuilder/PipelineTest.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,299 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.io.Writer;
+import java.util.Arrays;
+import java.util.List;
+
+/*
+ * @test PipelineTest
+ */
+
+public class PipelineTest {
+
+    private static void realMain(String[] args) throws Throwable {
+        t1_simplePipeline();
+        t2_translatePipeline();
+        t3_redirectErrorStream();
+        t4_failStartPipeline();
+    }
+
+    /**
+     * Return a list of the varargs arguments.
+     * @param args elements to include in the list
+     * @param <T> the type of the elements
+     * @return a {@code List<T>} of the arguments
+     */
+    @SafeVarargs
+    @SuppressWarnings("varargs")
+    static <T> List<T> asList(T... args) {
+        return Arrays.asList(args);
+    }
+
+    /**
+     * T1 - simple copy between two processes
+     */
+    static void t1_simplePipeline() {
+        try {
+            String s1 = "Now is the time to check!";
+            verify(s1, s1,
+                    asList(new ProcessBuilder("cat")));
+            verify(s1, s1,
+                    asList(new ProcessBuilder("cat"),
+                            new ProcessBuilder("cat")));
+            verify(s1, s1,
+                    asList(new ProcessBuilder("cat"),
+                            new ProcessBuilder("cat"),
+                            new ProcessBuilder("cat")));
+        } catch (Throwable t) {
+            unexpected(t);
+        }
+    }
+
+    /**
+     * Pipeline that modifies the content.
+     */
+    static void t2_translatePipeline() {
+        try {
+            String s2 = "Now is the time to check!";
+            String r2 = s2.replace('e', 'E').replace('o', 'O');
+            verify(s2, r2,
+                    asList(new ProcessBuilder("tr", "e", "E"),
+                            new ProcessBuilder("tr", "o", "O")));
+        } catch (Throwable t) {
+            unexpected(t);
+        }
+    }
+
+    /**
+     * Test that redirectErrorStream sends standard error of the first process
+     * to the standard output. The standard error of the first process should be empty.
+     * The standard output of the 2nd should contain the error message including the bad file name.
+     */
+    static void t3_redirectErrorStream() {
+        try {
+            File p1err = new File("p1-test.err");
+            File p2out = new File("p2-test.out");
+
+            List<Process> processes = ProcessBuilder.startPipeline(
+                    asList(new ProcessBuilder("cat", "NON-EXISTENT-FILE")
+                                    .redirectErrorStream(true)
+                                    .redirectError(p1err),
+                            new ProcessBuilder("cat").redirectOutput(p2out)));
+            waitForAll(processes);
+
+            check("".equals(fileContents(p1err)), "The first process standard error should be empty");
+            String p2contents = fileContents(p2out);
+            check(p2contents.contains("NON-EXISTENT-FILE"),
+                    "The error from the first process should be in the output of the second: " + p2contents);
+        } catch (Throwable t) {
+            unexpected(t);
+        }
+    }
+
+    /**
+     * Test that no processes are left after a failed startPipeline.
+     * Test illegal combinations of redirects.
+     */
+    static void t4_failStartPipeline() {
+        File p1err = new File("p1-test.err");
+        File p2out = new File("p2-test.out");
+
+        THROWS(IllegalArgumentException.class,
+                () -> {
+                    // Test that output redirect != PIPE throws IAE
+                    List<Process> processes = ProcessBuilder.startPipeline(
+                            asList(new ProcessBuilder("cat", "NON-EXISTENT-FILE1")
+                                            .redirectOutput(p1err),
+                                    new ProcessBuilder("cat")));
+                },
+                () -> {
+                    // Test that input redirect != PIPE throws IAE
+                    List<Process> processes = ProcessBuilder.startPipeline(
+                            asList(new ProcessBuilder("cat", "NON-EXISTENT-FILE2"),
+                                    new ProcessBuilder("cat").redirectInput(p2out)));
+                }
+        );
+
+        THROWS(NullPointerException.class,
+                () -> {
+                    List<Process> processes = ProcessBuilder.startPipeline(
+                            asList(new ProcessBuilder("cat", "a"), null));
+                },
+                () -> {
+                    List<Process> processes = ProcessBuilder.startPipeline(
+                            asList(null, new ProcessBuilder("cat", "b")));
+                }
+        );
+
+        THROWS(IOException.class,
+                () -> {
+                    List<Process> processes = ProcessBuilder.startPipeline(
+                            asList(new ProcessBuilder("cat", "c"),
+                                    new ProcessBuilder("NON-EXISTENT-COMMAND")));
+                });
+
+        // Check no subprocess are left behind
+        ProcessHandle.current().children().forEach(PipelineTest::print);
+        ProcessHandle.current().children()
+                .filter(p -> p.info().command().orElse("").contains("cat"))
+                .forEach(p -> fail("process should have been destroyed: " + p));
+    }
+
+    static void verify(String input, String expected, List<ProcessBuilder> builders) throws IOException {
+        File infile = new File("test.in");
+        File outfile = new File("test.out");
+        setFileContents(infile, expected);
+        for (int i = 0; i < builders.size(); i++) {
+            ProcessBuilder b = builders.get(i);
+            if (i == 0) {
+                b.redirectInput(infile);
+            }
+            if (i == builders.size() - 1) {
+                b.redirectOutput(outfile);
+            }
+        }
+        List<Process> processes = ProcessBuilder.startPipeline(builders);
+        verifyProcesses(processes);
+        waitForAll(processes);
+        String result = fileContents(outfile);
+        System.out.printf(" in: %s%nout: %s%n", input, expected);
+        check(result.equals(expected), "result not as expected");
+    }
+
+    /**
+     * Wait for each of the processes to be done.
+     *
+     * @param processes the list  of processes to check
+     */
+    static void waitForAll(List<Process> processes) {
+        processes.forEach(p -> {
+            try {
+                int status = p.waitFor();
+            } catch (InterruptedException ie) {
+                unexpected(ie);
+            }
+        });
+    }
+
+    static void print(ProcessBuilder pb) {
+        if (pb != null) {
+            System.out.printf(" pb: %s%n", pb);
+            System.out.printf("    cmd: %s%n", pb.command());
+        }
+    }
+
+    static void print(ProcessHandle p) {
+        System.out.printf("process: pid: %d, info: %s%n",
+                p.getPid(), p.info());
+    }
+
+    // Check various aspects of the processes
+    static void verifyProcesses(List<Process> processes) {
+        for (int i = 0; i < processes.size(); i++) {
+            Process p = processes.get(i);
+            if (i != 0) {
+                verifyNullStream(p.getOutputStream(), "getOutputStream");
+            }
+            if (i == processes.size() - 1) {
+                verifyNullStream(p.getInputStream(), "getInputStream");
+                verifyNullStream(p.getErrorStream(), "getErrorStream");
+            }
+        }
+    }
+
+    static void verifyNullStream(OutputStream s, String msg) {
+        try {
+            s.write(0xff);
+            fail("Stream should have been a NullStream" + msg);
+        } catch (IOException ie) {
+            // expected
+        }
+    }
+
+    static void verifyNullStream(InputStream s, String msg) {
+        try {
+            int len = s.read();
+            check(len == -1, "Stream should have been a NullStream" + msg);
+        } catch (IOException ie) {
+            // expected
+        }
+    }
+
+    static void setFileContents(File file, String contents) {
+        try {
+            Writer w = new FileWriter(file);
+            w.write(contents);
+            w.close();
+        } catch (Throwable t) { unexpected(t); }
+    }
+
+    static String fileContents(File file) {
+        try {
+            Reader r = new FileReader(file);
+            StringBuilder sb = new StringBuilder();
+            char[] buffer = new char[1024];
+            int n;
+            while ((n = r.read(buffer)) != -1)
+                sb.append(buffer,0,n);
+            r.close();
+            return new String(sb);
+        } catch (Throwable t) { unexpected(t); return ""; }
+    }
+
+    //--------------------- Infrastructure ---------------------------
+    static volatile int passed = 0, failed = 0;
+    static void pass() {passed++;}
+    static void fail() {failed++; Thread.dumpStack();}
+    static void fail(String msg) {System.err.println(msg); fail();}
+    static void unexpected(Throwable t) {failed++; t.printStackTrace();}
+    static void check(boolean cond) {if (cond) pass(); else fail();}
+    static void check(boolean cond, String m) {if (cond) pass(); else fail(m);}
+    static void equal(Object x, Object y) {
+        if (x == null ? y == null : x.equals(y)) pass();
+        else fail(">'" + x + "'<" + " not equal to " + "'" + y + "'");
+    }
+
+    public static void main(String[] args) throws Throwable {
+        try {realMain(args);} catch (Throwable t) {unexpected(t);}
+        System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
+        if (failed > 0) throw new AssertionError("Some tests failed");
+    }
+    interface Fun {void f() throws Throwable;}
+    static void THROWS(Class<? extends Throwable> k, Fun... fs) {
+        for (Fun f : fs)
+            try { f.f(); fail("Expected " + k.getName() + " not thrown"); }
+            catch (Throwable t) {
+                if (k.isAssignableFrom(t.getClass())) pass();
+                else unexpected(t);}
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/Logger/Level/LoggerLevelTest.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.lang.System.Logger.Level;
+import java.util.EnumSet;
+import java.util.Objects;
+import java.util.Set;
+/**
+ * @test
+ * @bug 8140364
+ * @summary Tests System.Logger.Level names and severity.
+ * @author danielfuchs
+ */
+public class LoggerLevelTest {
+    public static void main(String[] args) {
+        Set<Level> untested = EnumSet.allOf(Level.class);
+        testLevel(untested, Level.ALL, java.util.logging.Level.ALL);
+        testLevel(untested, Level.TRACE, java.util.logging.Level.FINER);
+        testLevel(untested, Level.DEBUG, java.util.logging.Level.FINE);
+        testLevel(untested, Level.INFO, java.util.logging.Level.INFO);
+        testLevel(untested, Level.WARNING, java.util.logging.Level.WARNING);
+        testLevel(untested, Level.ERROR, java.util.logging.Level.SEVERE);
+        testLevel(untested, Level.OFF, java.util.logging.Level.OFF);
+        if (!untested.isEmpty()) {
+            throw new RuntimeException("Some level values were not tested: " + untested);
+        }
+    }
+
+    private static void testLevel(Set<Level> untested, Level systemLevel, java.util.logging.Level julLevel) {
+        untested.remove(systemLevel);
+        assertEquals(systemLevel.getName(), systemLevel.name(),
+                "System.Logger.Level." + systemLevel.name() + ".getName()");
+        assertEquals(systemLevel.getSeverity(), julLevel.intValue(),
+                "System.Logger.Level." + systemLevel.name() + ".getSeverity");
+    }
+
+    private static void assertEquals(Object actual, Object expected, String what) {
+        if (!Objects.equals(actual, expected)) {
+            throw new RuntimeException("Bad value for " + what
+                    + "\n\t expected: " + expected
+                    + "\n\t   actual: " + actual);
+        } else {
+            System.out.println("Got expected value for " + what + ": " + actual);
+        }
+    }
+
+    private static void assertEquals(int actual, int expected, String what) {
+        if (!Objects.equals(actual, expected)) {
+            throw new RuntimeException("Bad value for " + what
+                    + "\n\t expected: " + toString(expected)
+                    + "\n\t   actual: " + toString(actual));
+        } else {
+            System.out.println("Got expected value for " + what + ": " + toString(actual));
+        }
+    }
+
+    private static String toString(int value) {
+        switch (value) {
+            case Integer.MAX_VALUE: return "Integer.MAX_VALUE";
+            case Integer.MIN_VALUE: return "Integer.MIN_VALUE";
+            default:
+                return Integer.toString(value);
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/Logger/custom/AccessSystemLogger.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+import java.io.IOException;
+import java.lang.System.Logger;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+import java.util.ResourceBundle;
+
+/**
+ *
+ * @author danielfuchs
+ */
+public final class AccessSystemLogger {
+
+    public AccessSystemLogger() {
+        this(check());
+    }
+
+    private AccessSystemLogger(Void unused) {
+    }
+
+    private static Void check() {
+        if (AccessSystemLogger.class.getClassLoader() != null) {
+            throw new RuntimeException("AccessSystemLogger should be loaded by the null classloader");
+        }
+        return null;
+    }
+
+    public Logger getLogger(String name) {
+        Logger logger = System.getLogger(name);
+        System.out.println("System.getLogger(\"" + name + "\"): " + logger);
+        return logger;
+    }
+
+    public Logger getLogger(String name, ResourceBundle bundle) {
+        Logger logger = System.getLogger(name, bundle);
+        System.out.println("System.getLogger(\"" + name + "\", bundle): " + logger);
+        return logger;
+    }
+
+    // copy AccessSystemLogger.class to ./boot
+    public static void main(String[] args) throws IOException {
+        Path testDir = Paths.get(System.getProperty("user.dir", "."));
+        Path bootDir = Paths.get(testDir.toString(), "boot");
+        Path classes = Paths.get(System.getProperty("test.classes", "build/classes"));
+        Path thisClass = Paths.get(classes.toString(),
+                AccessSystemLogger.class.getSimpleName()+".class");
+        if (Files.notExists(bootDir)) {
+            Files.createDirectory(bootDir);
+        }
+        Path dest = Paths.get(bootDir.toString(),
+                AccessSystemLogger.class.getSimpleName()+".class");
+        Files.copy(thisClass, dest, StandardCopyOption.REPLACE_EXISTING);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/Logger/custom/CustomLoggerTest.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,728 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+import java.security.AccessControlException;
+import java.security.AccessController;
+import java.security.CodeSource;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.Permissions;
+import java.security.Policy;
+import java.security.PrivilegedAction;
+import java.security.ProtectionDomain;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Queue;
+import java.util.ResourceBundle;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.Supplier;
+import java.lang.System.LoggerFinder;
+import java.lang.System.Logger;
+import java.lang.System.Logger.Level;
+import java.util.stream.Stream;
+
+/**
+ * @test
+ * @bug     8140364
+ * @summary Tests loggers returned by System.getLogger with a naive implementation
+ *          of LoggerFinder, and in particular the default body of
+ *          System.Logger methods.
+ * @build CustomLoggerTest AccessSystemLogger
+ * @run driver AccessSystemLogger
+ * @run main/othervm -Xbootclasspath/a:boot CustomLoggerTest NOSECURITY
+ * @run main/othervm -Xbootclasspath/a:boot CustomLoggerTest NOPERMISSIONS
+ * @run main/othervm -Xbootclasspath/a:boot CustomLoggerTest WITHPERMISSIONS
+ * @author danielfuchs
+ */
+public class CustomLoggerTest {
+
+    final static AtomicLong sequencer = new AtomicLong();
+    final static boolean VERBOSE = false;
+    static final ThreadLocal<AtomicBoolean> allowControl = new ThreadLocal<AtomicBoolean>() {
+        @Override
+        protected AtomicBoolean initialValue() {
+            return  new AtomicBoolean(false);
+        }
+    };
+
+    public static class MyBundle extends ResourceBundle {
+
+        final ConcurrentHashMap<String,String> map = new ConcurrentHashMap<>();
+
+        @Override
+        protected Object handleGetObject(String key) {
+            if (key.contains(" (translated)")) {
+                throw new RuntimeException("Unexpected key: " + key);
+            }
+            return map.computeIfAbsent(key, k -> k + " (translated)");
+        }
+
+        @Override
+        public Enumeration<String> getKeys() {
+            return Collections.enumeration(map.keySet());
+        }
+
+    }
+    public static class MyLoggerBundle extends MyBundle {
+
+    }
+
+
+    public static class BaseLoggerFinder extends LoggerFinder {
+        final ConcurrentHashMap<String, LoggerImpl> system = new ConcurrentHashMap<>();
+        final ConcurrentHashMap<String, LoggerImpl> user = new ConcurrentHashMap<>();
+        public Queue<LogEvent> eventQueue = new ArrayBlockingQueue<>(128);
+
+        // changing this to true requires changing the logic in the
+        // test in order to load this class with a protection domain
+        // that has the CONTROL_PERMISSION (e.g. by using a custom
+        // system class loader.
+        final boolean doChecks = false;
+
+        public static final class LogEvent {
+
+            public LogEvent() {
+                this(sequencer.getAndIncrement());
+            }
+
+            LogEvent(long sequenceNumber) {
+                this.sequenceNumber = sequenceNumber;
+            }
+
+            long sequenceNumber;
+            boolean isLoggable;
+            String loggerName;
+            Level level;
+            ResourceBundle bundle;
+            Throwable thrown;
+            Object[] args;
+            Supplier<String> supplier;
+            String msg;
+
+            Object[] toArray() {
+                return new Object[] {
+                    sequenceNumber,
+                    isLoggable,
+                    loggerName,
+                    level,
+                    bundle,
+                    thrown,
+                    args,
+                    supplier,
+                    msg,
+                };
+            }
+
+            @Override
+            public String toString() {
+                return Arrays.deepToString(toArray());
+            }
+
+
+
+            @Override
+            public boolean equals(Object obj) {
+                return obj instanceof LogEvent
+                        && Objects.deepEquals(this.toArray(), ((LogEvent)obj).toArray());
+            }
+
+            @Override
+            public int hashCode() {
+                return Objects.hash(toArray());
+            }
+
+
+            public static LogEvent of(boolean isLoggable, String name,
+                    Level level, ResourceBundle bundle,
+                    String key, Throwable thrown) {
+                LogEvent evt = new LogEvent();
+                evt.isLoggable = isLoggable;
+                evt.loggerName = name;
+                evt.level = level;
+                evt.args = null;
+                evt.bundle = bundle;
+                evt.thrown = thrown;
+                evt.supplier = null;
+                evt.msg = key;
+                return evt;
+            }
+
+            public static LogEvent of(boolean isLoggable, String name,
+                    Level level, ResourceBundle bundle,
+                    String key, Object... params) {
+                LogEvent evt = new LogEvent();
+                evt.isLoggable = isLoggable;
+                evt.loggerName = name;
+                evt.level = level;
+                evt.args = params;
+                evt.bundle = bundle;
+                evt.thrown = null;
+                evt.supplier = null;
+                evt.msg = key;
+                return evt;
+            }
+
+            public static LogEvent of(long sequenceNumber,
+                    boolean isLoggable, String name,
+                    Level level, ResourceBundle bundle,
+                    String key, Supplier<String> supplier,
+                    Throwable thrown, Object... params) {
+                LogEvent evt = new LogEvent(sequenceNumber);
+                evt.loggerName = name;
+                evt.level = level;
+                evt.args = params;
+                evt.bundle = bundle;
+                evt.thrown = thrown;
+                evt.supplier = supplier;
+                evt.msg = key;
+                evt.isLoggable = isLoggable;
+                return evt;
+            }
+
+        }
+
+        public class LoggerImpl implements Logger {
+            private final String name;
+            private Level level = Level.INFO;
+
+            public LoggerImpl(String name) {
+                this.name = name;
+            }
+
+            @Override
+            public String getName() {
+                return name;
+            }
+
+            @Override
+            public boolean isLoggable(Level level) {
+                return this.level != Level.OFF && this.level.getSeverity() <= level.getSeverity();
+            }
+
+            @Override
+            public void log(Level level, ResourceBundle bundle, String key, Throwable thrown) {
+                log(LogEvent.of(isLoggable(level), this.name, level, bundle, key, thrown));
+            }
+
+            @Override
+            public void log(Level level, ResourceBundle bundle, String format, Object... params) {
+                log(LogEvent.of(isLoggable(level), name, level, bundle, format, params));
+            }
+
+            void log(LogEvent event) {
+                eventQueue.add(event);
+            }
+        }
+
+        @Override
+        public Logger getLogger(String name, Class<?> caller) {
+            // We should check the permission to obey the API contract, but
+            // what happens if we don't?
+            // This is the main difference compared with what we test in
+            // java/lang/System/LoggerFinder/BaseLoggerFinderTest
+            SecurityManager sm = System.getSecurityManager();
+            if (sm != null && doChecks) {
+                sm.checkPermission(SimplePolicy.LOGGERFINDER_PERMISSION);
+            }
+
+            PrivilegedAction<ClassLoader> pa = () -> caller.getClassLoader();
+            ClassLoader callerLoader = AccessController.doPrivileged(pa);
+            if (callerLoader == null) {
+                return system.computeIfAbsent(name, (n) -> new LoggerImpl(n));
+            } else {
+                return user.computeIfAbsent(name, (n) -> new LoggerImpl(n));
+            }
+        }
+    }
+
+    static final AccessSystemLogger accessSystemLogger = new AccessSystemLogger();
+
+    static enum TestCases {NOSECURITY, NOPERMISSIONS, WITHPERMISSIONS};
+
+    static void setSecurityManager() {
+        if (System.getSecurityManager() == null) {
+            Policy.setPolicy(new SimplePolicy(allowControl));
+            System.setSecurityManager(new SecurityManager());
+        }
+    }
+    public static void main(String[] args) {
+        if (args.length == 0)
+            args = new String[] {
+                "NOSECURITY",
+                "NOPERMISSIONS",
+                "WITHPERMISSIONS"
+            };
+
+        // 1. Obtain destination loggers directly from the LoggerFinder
+        //   - LoggerFinder.getLogger("foo", type)
+        BaseLoggerFinder provider =
+                BaseLoggerFinder.class.cast(LoggerFinder.getLoggerFinder());
+        BaseLoggerFinder.LoggerImpl appSink =
+                BaseLoggerFinder.LoggerImpl.class.cast(provider.getLogger("foo", CustomLoggerTest.class));
+        BaseLoggerFinder.LoggerImpl sysSink =
+                BaseLoggerFinder.LoggerImpl.class.cast(provider.getLogger("foo", Thread.class));
+
+
+        Stream.of(args).map(TestCases::valueOf).forEach((testCase) -> {
+            switch (testCase) {
+                case NOSECURITY:
+                    System.out.println("\n*** Without Security Manager\n");
+                    test(provider, true, appSink, sysSink);
+                    System.out.println("Tetscase count: " + sequencer.get());
+                    break;
+                case NOPERMISSIONS:
+                    System.out.println("\n*** With Security Manager, without permissions\n");
+                    setSecurityManager();
+                    test(provider, false, appSink, sysSink);
+                    System.out.println("Tetscase count: " + sequencer.get());
+                    break;
+                case WITHPERMISSIONS:
+                    System.out.println("\n*** With Security Manager, with control permission\n");
+                    setSecurityManager();
+                    final boolean control = allowControl.get().get();
+                    try {
+                        allowControl.get().set(true);
+                        test(provider, true, appSink, sysSink);
+                    } finally {
+                        allowControl.get().set(control);
+                    }
+                    break;
+                default:
+                    throw new RuntimeException("Unknown test case: " + testCase);
+            }
+        });
+        System.out.println("\nPASSED: Tested " + sequencer.get() + " cases.");
+    }
+
+    public static void test(BaseLoggerFinder provider, boolean hasRequiredPermissions,
+            BaseLoggerFinder.LoggerImpl appSink, BaseLoggerFinder.LoggerImpl sysSink) {
+
+        ResourceBundle loggerBundle = ResourceBundle.getBundle(MyLoggerBundle.class.getName());
+        final Map<Logger, String> loggerDescMap = new HashMap<>();
+
+
+        // 1. Test loggers returned by:
+        //   - System.getLogger("foo")
+        //   - and AccessSystemLogger.getLogger("foo")
+        Logger appLogger1 = System.getLogger("foo");
+        loggerDescMap.put(appLogger1, "System.getLogger(\"foo\");");
+
+        Logger sysLogger1 = null;
+        try {
+            sysLogger1 = accessSystemLogger.getLogger("foo");
+            loggerDescMap.put(sysLogger1, "AccessSystemLogger.getLogger(\"foo\")");
+        } catch (AccessControlException acx) {
+            if (hasRequiredPermissions) {
+                throw new RuntimeException("Unexpected security exception: ", acx);
+            }
+            if (!acx.getPermission().equals(SimplePolicy.LOGGERFINDER_PERMISSION)) {
+                throw new RuntimeException("Unexpected permission in exception: " + acx, acx);
+            }
+            throw new RuntimeException("unexpected exception: " + acx, acx);
+        }
+
+        if (appLogger1 == sysLogger1) {
+            throw new RuntimeException("identical loggers");
+        }
+
+        if (provider.system.contains(appLogger1)) {
+            throw new RuntimeException("app logger in system map");
+        }
+        if (provider.user.contains(sysLogger1)) {
+            throw new RuntimeException("sys logger in appplication map");
+        }
+        if (provider.system.contains(sysLogger1)) {
+            // sysLogger should be a a LazyLoggerWrapper
+            throw new RuntimeException("sys logger is in system map (should be wrapped)");
+        }
+
+
+        // 2. Test loggers returned by:
+        //   - System.getLogger(\"foo\", loggerBundle)
+        //   - and AccessSystemLogger.getLogger(\"foo\", loggerBundle)
+        Logger appLogger2 =
+                System.getLogger("foo", loggerBundle);
+        loggerDescMap.put(appLogger2, "System.getLogger(\"foo\", loggerBundle)");
+
+        Logger sysLogger2 = null;
+        try {
+            sysLogger2 = accessSystemLogger.getLogger("foo", loggerBundle);
+            loggerDescMap.put(sysLogger2, "AccessSystemLogger.getLogger(\"foo\", loggerBundle)");
+        } catch (AccessControlException acx) {
+            if (hasRequiredPermissions) {
+                throw new RuntimeException("Unexpected security exception: ", acx);
+            }
+            if (!acx.getPermission().equals(SimplePolicy.LOGGERFINDER_PERMISSION)) {
+                throw new RuntimeException("Unexpected permission in exception: " + acx, acx);
+            }
+            throw new RuntimeException("unexpected exception: " + acx, acx);
+        }
+        if (appLogger2 == sysLogger2) {
+            throw new RuntimeException("identical loggers");
+        }
+        if (appLogger2 == appSink) {
+            throw new RuntimeException("identical loggers");
+        }
+        if (sysLogger2 == sysSink) {
+            throw new RuntimeException("identical loggers");
+        }
+
+        if (provider.system.contains(appLogger2)) {
+            throw new RuntimeException("localized app logger in system map");
+        }
+        if (provider.user.contains(appLogger2)) {
+            throw new RuntimeException("localized app logger  in appplication map");
+        }
+        if (provider.user.contains(sysLogger2)) {
+            throw new RuntimeException("localized sys logger in appplication map");
+        }
+        if (provider.system.contains(sysLogger2)) {
+            throw new RuntimeException("localized sys logger not in system map");
+        }
+
+        testLogger(provider, loggerDescMap, "foo", null, appLogger1, appSink);
+        testLogger(provider, loggerDescMap, "foo", null, sysLogger1, sysSink);
+        testLogger(provider, loggerDescMap, "foo", loggerBundle, appLogger2, appSink);
+        testLogger(provider, loggerDescMap, "foo", loggerBundle, sysLogger2, sysSink);
+    }
+
+    public static class Foo {
+
+    }
+
+    static void verbose(String msg) {
+       if (VERBOSE) {
+           System.out.println(msg);
+       }
+    }
+
+    // Calls the 8 methods defined on Logger and verify the
+    // parameters received by the underlying BaseLoggerFinder.LoggerImpl
+    // logger.
+    private static void testLogger(BaseLoggerFinder provider,
+            Map<Logger, String> loggerDescMap,
+            String name,
+            ResourceBundle loggerBundle,
+            Logger logger,
+            BaseLoggerFinder.LoggerImpl sink) {
+
+        System.out.println("Testing " + loggerDescMap.get(logger));
+
+        Foo foo = new Foo();
+        String fooMsg = foo.toString();
+        for (Level loggerLevel : Level.values()) {
+            sink.level = loggerLevel;
+            for (Level messageLevel : Level.values()) {
+                String desc = "logger.log(messageLevel, foo): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                BaseLoggerFinder.LogEvent expected =
+                        BaseLoggerFinder.LogEvent.of(
+                            sequencer.get(),
+                            messageLevel.compareTo(loggerLevel) >= 0,
+                            name, messageLevel, (ResourceBundle)null,
+                            fooMsg, null, (Throwable)null, (Object[])null);
+                logger.log(messageLevel, foo);
+                if (loggerLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
+                    if (provider.eventQueue.poll() != null) {
+                        throw new RuntimeException("unexpected event in queue for " + desc);
+                    }
+                } else {
+                    BaseLoggerFinder.LogEvent actual =  provider.eventQueue.poll();
+                    if (!expected.equals(actual)) {
+                        throw new RuntimeException("mismatch for " + desc
+                                + "\n\texpected=" + expected
+                                + "\n\t  actual=" + actual);
+                    } else {
+                        verbose("Got expected results for "
+                                + desc + "\n\t" + expected);
+                    }
+                }
+            }
+        }
+
+        String msg = "blah";
+        for (Level loggerLevel : Level.values()) {
+            sink.level = loggerLevel;
+            for (Level messageLevel : Level.values()) {
+                String desc = "logger.log(messageLevel, \"blah\"): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                BaseLoggerFinder.LogEvent expected =
+                        BaseLoggerFinder.LogEvent.of(
+                            sequencer.get(),
+                            messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF,
+                            name, messageLevel, loggerBundle,
+                            msg, null, (Throwable)null, (Object[])null);
+                logger.log(messageLevel, msg);
+                BaseLoggerFinder.LogEvent actual =  provider.eventQueue.poll();
+                if (!expected.equals(actual)) {
+                    throw new RuntimeException("mismatch for " + desc
+                            + "\n\texpected=" + expected
+                            + "\n\t  actual=" + actual);
+                } else {
+                    verbose("Got expected results for "
+                            + desc + "\n\t" + expected);
+                }
+            }
+        }
+
+        Supplier<String> fooSupplier = new Supplier<String>() {
+            @Override
+            public String get() {
+                return this.toString();
+            }
+        };
+
+        for (Level loggerLevel : Level.values()) {
+            sink.level = loggerLevel;
+            for (Level messageLevel : Level.values()) {
+                String desc = "logger.log(messageLevel, fooSupplier): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                BaseLoggerFinder.LogEvent expected =
+                        BaseLoggerFinder.LogEvent.of(
+                            sequencer.get(),
+                            messageLevel.compareTo(loggerLevel) >= 0,
+                            name, messageLevel, (ResourceBundle)null,
+                            fooSupplier.get(), null,
+                            (Throwable)null, (Object[])null);
+                logger.log(messageLevel, fooSupplier);
+                if (loggerLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
+                    if (provider.eventQueue.poll() != null) {
+                        throw new RuntimeException("unexpected event in queue for " + desc);
+                    }
+                } else {
+                    BaseLoggerFinder.LogEvent actual =  provider.eventQueue.poll();
+                    if (!expected.equals(actual)) {
+                        throw new RuntimeException("mismatch for " + desc
+                                + "\n\texpected=" + expected
+                                + "\n\t  actual=" + actual);
+                    } else {
+                        verbose("Got expected results for "
+                                + desc + "\n\t" + expected);
+                    }
+                }
+            }
+        }
+
+        String format = "two params [{1} {2}]";
+        Object arg1 = foo;
+        Object arg2 = msg;
+        for (Level loggerLevel : Level.values()) {
+            sink.level = loggerLevel;
+            for (Level messageLevel : Level.values()) {
+                String desc = "logger.log(messageLevel, format, params...): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                BaseLoggerFinder.LogEvent expected =
+                        BaseLoggerFinder.LogEvent.of(
+                            sequencer.get(),
+                            messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF,
+                            name, messageLevel, loggerBundle,
+                            format, null, (Throwable)null, new Object[] {arg1, arg2});
+                logger.log(messageLevel, format, arg1, arg2);
+                BaseLoggerFinder.LogEvent actual =  provider.eventQueue.poll();
+                if (!expected.equals(actual)) {
+                    throw new RuntimeException("mismatch for " + desc
+                            + "\n\texpected=" + expected
+                            + "\n\t  actual=" + actual);
+                } else {
+                    verbose("Got expected results for "
+                            + desc + "\n\t" + expected);
+                }
+            }
+        }
+
+        Throwable thrown = new Exception("OK: log me!");
+        for (Level loggerLevel : Level.values()) {
+            sink.level = loggerLevel;
+            for (Level messageLevel : Level.values()) {
+                String desc = "logger.log(messageLevel, \"blah\", thrown): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                BaseLoggerFinder.LogEvent expected =
+                        BaseLoggerFinder.LogEvent.of(
+                            sequencer.get(),
+                            messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF,
+                            name, messageLevel, loggerBundle,
+                            msg, null, thrown, (Object[]) null);
+                logger.log(messageLevel, msg, thrown);
+                BaseLoggerFinder.LogEvent actual =  provider.eventQueue.poll();
+                if (!expected.equals(actual)) {
+                    throw new RuntimeException("mismatch for " + desc
+                            + "\n\texpected=" + expected
+                            + "\n\t  actual=" + actual);
+                } else {
+                    verbose("Got expected results for "
+                            + desc + "\n\t" + expected);
+                }
+            }
+        }
+
+
+        for (Level loggerLevel : Level.values()) {
+            sink.level = loggerLevel;
+            for (Level messageLevel : Level.values()) {
+                String desc = "logger.log(messageLevel, thrown, fooSupplier): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                BaseLoggerFinder.LogEvent expected =
+                        BaseLoggerFinder.LogEvent.of(
+                            sequencer.get(),
+                            messageLevel.compareTo(loggerLevel) >= 0,
+                            name, messageLevel, (ResourceBundle)null,
+                            fooSupplier.get(), null,
+                            (Throwable)thrown, (Object[])null);
+                logger.log(messageLevel, fooSupplier, thrown);
+                if (loggerLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
+                    if (provider.eventQueue.poll() != null) {
+                        throw new RuntimeException("unexpected event in queue for " + desc);
+                    }
+                } else {
+                    BaseLoggerFinder.LogEvent actual =  provider.eventQueue.poll();
+                    if (!expected.equals(actual)) {
+                        throw new RuntimeException("mismatch for " + desc
+                                + "\n\texpected=" + expected
+                                + "\n\t  actual=" + actual);
+                    } else {
+                        verbose("Got expected results for "
+                                + desc + "\n\t" + expected);
+                    }
+                }
+            }
+        }
+
+        ResourceBundle bundle = ResourceBundle.getBundle(MyBundle.class.getName());
+        for (Level loggerLevel : Level.values()) {
+            sink.level = loggerLevel;
+            for (Level messageLevel : Level.values()) {
+                String desc = "logger.log(messageLevel, bundle, format, params...): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                BaseLoggerFinder.LogEvent expected =
+                        BaseLoggerFinder.LogEvent.of(
+                            sequencer.get(),
+                            messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF,
+                            name, messageLevel, bundle,
+                            format, null, (Throwable)null, new Object[] {foo, msg});
+                logger.log(messageLevel, bundle, format, foo, msg);
+                BaseLoggerFinder.LogEvent actual =  provider.eventQueue.poll();
+                if (!expected.equals(actual)) {
+                    throw new RuntimeException("mismatch for " + desc
+                            + "\n\texpected=" + expected
+                            + "\n\t  actual=" + actual);
+                } else {
+                    verbose("Got expected results for "
+                            + desc + "\n\t" + expected);
+                }
+            }
+        }
+
+        for (Level loggerLevel : Level.values()) {
+            sink.level = loggerLevel;
+            for (Level messageLevel : Level.values()) {
+                String desc = "logger.log(messageLevel, bundle, \"blah\", thrown): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                BaseLoggerFinder.LogEvent expected =
+                        BaseLoggerFinder.LogEvent.of(
+                            sequencer.get(),
+                            messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF,
+                            name, messageLevel, bundle,
+                            msg, null, thrown, (Object[]) null);
+                logger.log(messageLevel, bundle, msg, thrown);
+                BaseLoggerFinder.LogEvent actual =  provider.eventQueue.poll();
+                if (!expected.equals(actual)) {
+                    throw new RuntimeException("mismatch for " + desc
+                            + "\n\texpected=" + expected
+                            + "\n\t  actual=" + actual);
+                } else {
+                    verbose("Got expected results for "
+                            + desc + "\n\t" + expected);
+                }
+            }
+        }
+    }
+
+    final static class PermissionsBuilder {
+        final Permissions perms;
+        public PermissionsBuilder() {
+            this(new Permissions());
+        }
+        public PermissionsBuilder(Permissions perms) {
+            this.perms = perms;
+        }
+        public PermissionsBuilder add(Permission p) {
+            perms.add(p);
+            return this;
+        }
+        public PermissionsBuilder addAll(PermissionCollection col) {
+            if (col != null) {
+                for (Enumeration<Permission> e = col.elements(); e.hasMoreElements(); ) {
+                    perms.add(e.nextElement());
+                }
+            }
+            return this;
+        }
+        public Permissions toPermissions() {
+            final PermissionsBuilder builder = new PermissionsBuilder();
+            builder.addAll(perms);
+            return builder.perms;
+        }
+    }
+
+    public static class SimplePolicy extends Policy {
+
+        static final RuntimePermission LOGGERFINDER_PERMISSION =
+                new RuntimePermission("loggerFinder");
+        final Permissions permissions;
+        final Permissions allPermissions;
+        final ThreadLocal<AtomicBoolean> allowControl;
+        public SimplePolicy(ThreadLocal<AtomicBoolean> allowControl) {
+            this.allowControl = allowControl;
+            permissions = new Permissions();
+
+            // these are used for configuring the test itself...
+            allPermissions = new Permissions();
+            allPermissions.add(LOGGERFINDER_PERMISSION);
+
+        }
+
+        @Override
+        public boolean implies(ProtectionDomain domain, Permission permission) {
+            if (allowControl.get().get()) return allPermissions.implies(permission);
+            return permissions.implies(permission);
+        }
+
+        @Override
+        public PermissionCollection getPermissions(CodeSource codesource) {
+            return new PermissionsBuilder().addAll(allowControl.get().get()
+                    ? allPermissions : permissions).toPermissions();
+        }
+
+        @Override
+        public PermissionCollection getPermissions(ProtectionDomain domain) {
+            return new PermissionsBuilder().addAll(allowControl.get().get()
+                    ? allPermissions : permissions).toPermissions();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/Logger/custom/META-INF/services/java.lang.System$LoggerFinder	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,1 @@
+CustomLoggerTest$BaseLoggerFinder
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/Logger/default/AccessSystemLogger.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+import java.io.IOException;
+import java.lang.System.Logger;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+import java.util.ResourceBundle;
+import java.util.logging.LogManager;
+
+/**
+ *
+ * @author danielfuchs
+ */
+public final class AccessSystemLogger {
+
+    public AccessSystemLogger() {
+        this(check());
+    }
+
+    private AccessSystemLogger(Void unused) {
+    }
+
+    private static Void check() {
+        if (AccessSystemLogger.class.getClassLoader() != null) {
+            throw new RuntimeException("AccessSystemLogger should be loaded by the null classloader");
+        }
+        return null;
+    }
+
+    public Logger getLogger(String name) {
+        Logger logger = System.getLogger(name);
+        System.out.println("System.getLogger(\"" + name + "\"): " + logger);
+        return logger;
+    }
+
+    public Logger getLogger(String name, ResourceBundle bundle) {
+        Logger logger = System.getLogger(name, bundle);
+        System.out.println("System.getLogger(\"" + name + "\", bundle): " + logger);
+        return logger;
+    }
+
+    public java.util.logging.Logger demandSystemLogger(String name) {
+        return java.util.logging.Logger.getLogger(name);
+    }
+
+    // copy AccessSystemLogger.class to ./boot
+    public static void main(String[] args) throws IOException {
+        Path testDir = Paths.get(System.getProperty("user.dir", "."));
+        Path bootDir = Paths.get(testDir.toString(), "boot");
+        Path classes = Paths.get(System.getProperty("test.classes", "build/classes"));
+        Path thisClass = Paths.get(classes.toString(),
+                AccessSystemLogger.class.getSimpleName()+".class");
+        if (Files.notExists(bootDir)) {
+            Files.createDirectory(bootDir);
+        }
+        Path dest = Paths.get(bootDir.toString(),
+                AccessSystemLogger.class.getSimpleName()+".class");
+        Files.copy(thisClass, dest, StandardCopyOption.REPLACE_EXISTING);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/Logger/default/DefaultLoggerTest.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,726 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+import java.security.AccessControlException;
+import java.security.CodeSource;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.Permissions;
+import java.security.Policy;
+import java.security.ProtectionDomain;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Queue;
+import java.util.ResourceBundle;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.Supplier;
+import java.lang.System.LoggerFinder;
+import java.lang.System.Logger;
+import java.lang.System.Logger.Level;
+import java.util.logging.Handler;
+import java.util.logging.LogRecord;
+import java.util.stream.Stream;
+
+/**
+ * @test
+ * @bug     8140364
+ * @summary Tests default loggers returned by System.getLogger, and in
+ *          particular the implementation of the the System.Logger method
+ *          performed by the default binding.
+ *
+ * @build DefaultLoggerTest AccessSystemLogger
+ * @run driver AccessSystemLogger
+ * @run main/othervm -Xbootclasspath/a:boot DefaultLoggerTest NOSECURITY
+ * @run main/othervm -Xbootclasspath/a:boot DefaultLoggerTest NOPERMISSIONS
+ * @run main/othervm -Xbootclasspath/a:boot DefaultLoggerTest WITHPERMISSIONS
+ * @author danielfuchs
+ */
+public class DefaultLoggerTest {
+
+    final static AtomicLong sequencer = new AtomicLong();
+    final static boolean VERBOSE = false;
+    static final ThreadLocal<AtomicBoolean> allowControl = new ThreadLocal<AtomicBoolean>() {
+        @Override
+        protected AtomicBoolean initialValue() {
+            return  new AtomicBoolean(false);
+        }
+    };
+    static final ThreadLocal<AtomicBoolean> allowAll = new ThreadLocal<AtomicBoolean>() {
+        @Override
+        protected AtomicBoolean initialValue() {
+            return  new AtomicBoolean(false);
+        }
+    };
+
+    public static final Queue<LogEvent> eventQueue = new ArrayBlockingQueue<>(128);
+
+    public static final class LogEvent {
+
+        public LogEvent() {
+            this(sequencer.getAndIncrement());
+        }
+
+        LogEvent(long sequenceNumber) {
+            this.sequenceNumber = sequenceNumber;
+        }
+
+        long sequenceNumber;
+        boolean isLoggable;
+        String loggerName;
+        java.util.logging.Level level;
+        ResourceBundle bundle;
+        Throwable thrown;
+        Object[] args;
+        String msg;
+        String className;
+        String methodName;
+
+        Object[] toArray() {
+            return new Object[] {
+                sequenceNumber,
+                isLoggable,
+                loggerName,
+                level,
+                bundle,
+                thrown,
+                args,
+                msg,
+                className,
+                methodName,
+            };
+        }
+
+        @Override
+        public String toString() {
+            return Arrays.deepToString(toArray());
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            return obj instanceof LogEvent
+                    && Objects.deepEquals(this.toArray(), ((LogEvent)obj).toArray());
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(toArray());
+        }
+        public static LogEvent of(long sequenceNumber,
+                boolean isLoggable, String name,
+                java.util.logging.Level level, ResourceBundle bundle,
+                String key, Throwable thrown, Object... params) {
+            return LogEvent.of(sequenceNumber, isLoggable, name,
+                    DefaultLoggerTest.class.getName(),
+                    "testLogger", level, bundle, key,
+                    thrown, params);
+        }
+        public static LogEvent of(long sequenceNumber,
+                boolean isLoggable, String name,
+                String className, String methodName,
+                java.util.logging.Level level, ResourceBundle bundle,
+                String key, Throwable thrown, Object... params) {
+            LogEvent evt = new LogEvent(sequenceNumber);
+            evt.loggerName = name;
+            evt.level = level;
+            evt.args = params;
+            evt.bundle = bundle;
+            evt.thrown = thrown;
+            evt.msg = key;
+            evt.isLoggable = isLoggable;
+            evt.className = className;
+            evt.methodName = methodName;
+            return evt;
+        }
+
+    }
+
+    static java.util.logging.Level mapToJul(Level level) {
+        switch (level) {
+            case ALL: return java.util.logging.Level.ALL;
+            case TRACE: return java.util.logging.Level.FINER;
+            case DEBUG: return java.util.logging.Level.FINE;
+            case INFO: return java.util.logging.Level.INFO;
+            case WARNING: return java.util.logging.Level.WARNING;
+            case ERROR: return java.util.logging.Level.SEVERE;
+            case OFF: return java.util.logging.Level.OFF;
+        }
+        throw new InternalError("No such level: " + level);
+    }
+
+    static void setLevel(java.util.logging.Logger sink, java.util.logging.Level loggerLevel) {
+        boolean before = allowAll.get().get();
+        try {
+            allowAll.get().set(true);
+            sink.setLevel(loggerLevel);
+        } finally {
+            allowAll.get().set(before);
+        }
+    }
+
+    public static class MyHandler extends Handler {
+
+        @Override
+        public java.util.logging.Level getLevel() {
+            return java.util.logging.Level.ALL;
+        }
+
+        @Override
+        public void publish(LogRecord record) {
+            eventQueue.add(LogEvent.of(sequencer.getAndIncrement(),
+                    true, record.getLoggerName(),
+                    record.getSourceClassName(),
+                    record.getSourceMethodName(),
+                    record.getLevel(),
+                    record.getResourceBundle(), record.getMessage(),
+                    record.getThrown(), record.getParameters()));
+        }
+        @Override
+        public void flush() {
+        }
+        @Override
+        public void close() throws SecurityException {
+        }
+
+    }
+    public static class MyBundle extends ResourceBundle {
+
+        final ConcurrentHashMap<String,String> map = new ConcurrentHashMap<>();
+
+        @Override
+        protected Object handleGetObject(String key) {
+            if (key.contains(" (translated)")) {
+                throw new RuntimeException("Unexpected key: " + key);
+            }
+            return map.computeIfAbsent(key, k -> k + " (translated)");
+        }
+
+        @Override
+        public Enumeration<String> getKeys() {
+            return Collections.enumeration(map.keySet());
+        }
+
+    }
+    public static class MyLoggerBundle extends MyBundle {
+
+    }
+
+    static final AccessSystemLogger accessSystemLogger = new AccessSystemLogger();
+
+    static enum TestCases {NOSECURITY, NOPERMISSIONS, WITHPERMISSIONS};
+
+    static void setSecurityManager() {
+        if (System.getSecurityManager() == null) {
+            Policy.setPolicy(new SimplePolicy(allowControl, allowAll));
+            System.setSecurityManager(new SecurityManager());
+        }
+    }
+    public static void main(String[] args) {
+        if (args.length == 0)
+            args = new String[] {
+                "NOSECURITY",
+                "NOPERMISSIONS",
+                "WITHPERMISSIONS"
+            };
+
+        // 1. Obtain destination loggers directly from the LoggerFinder
+        //   - LoggerFinder.getLogger("foo", type)
+
+
+        Stream.of(args).map(TestCases::valueOf).forEach((testCase) -> {
+            switch (testCase) {
+                case NOSECURITY:
+                    System.out.println("\n*** Without Security Manager\n");
+                    test(true);
+                    System.out.println("Tetscase count: " + sequencer.get());
+                    break;
+                case NOPERMISSIONS:
+                    System.out.println("\n*** With Security Manager, without permissions\n");
+                    setSecurityManager();
+                    test(false);
+                    System.out.println("Tetscase count: " + sequencer.get());
+                    break;
+                case WITHPERMISSIONS:
+                    System.out.println("\n*** With Security Manager, with control permission\n");
+                    setSecurityManager();
+                    final boolean control = allowControl.get().get();
+                    try {
+                        allowControl.get().set(true);
+                        test(true);
+                    } finally {
+                        allowControl.get().set(control);
+                    }
+                    break;
+                default:
+                    throw new RuntimeException("Unknown test case: " + testCase);
+            }
+        });
+        System.out.println("\nPASSED: Tested " + sequencer.get() + " cases.");
+    }
+
+    public static void test(boolean hasRequiredPermissions) {
+
+        ResourceBundle loggerBundle = ResourceBundle.getBundle(MyLoggerBundle.class.getName());
+        final Map<Logger, String> loggerDescMap = new HashMap<>();
+
+
+        // 1. Test loggers returned by:
+        //   - System.getLogger("foo")
+        //   - and AccessSystemLogger.getLogger("foo")
+        Logger sysLogger1 = null;
+        try {
+            sysLogger1 = accessSystemLogger.getLogger("foo");
+            loggerDescMap.put(sysLogger1, "AccessSystemLogger.getLogger(\"foo\")");
+        } catch (AccessControlException acx) {
+            if (hasRequiredPermissions) {
+                throw new RuntimeException("Unexpected security exception: ", acx);
+            }
+            if (!acx.getPermission().equals(SimplePolicy.LOGGERFINDER_PERMISSION)) {
+                throw new RuntimeException("Unexpected permission in exception: " + acx, acx);
+            }
+            throw new RuntimeException("unexpected exception: " + acx, acx);
+        }
+
+        Logger appLogger1 = System.getLogger("foo");
+        loggerDescMap.put(appLogger1, "System.getLogger(\"foo\");");
+
+        if (appLogger1 == sysLogger1) {
+            throw new RuntimeException("identical loggers");
+        }
+
+        // 2. Test loggers returned by:
+        //   - System.getLogger(\"foo\", loggerBundle)
+        //   - and AccessSystemLogger.getLogger(\"foo\", loggerBundle)
+        Logger appLogger2 =
+                System.getLogger("foo", loggerBundle);
+        loggerDescMap.put(appLogger2, "System.getLogger(\"foo\", loggerBundle)");
+
+        Logger sysLogger2 = null;
+        try {
+            sysLogger2 = accessSystemLogger.getLogger("foo", loggerBundle);
+            loggerDescMap.put(sysLogger2, "AccessSystemLogger.getLogger(\"foo\", loggerBundle)");
+        } catch (AccessControlException acx) {
+            if (hasRequiredPermissions) {
+                throw new RuntimeException("Unexpected security exception: ", acx);
+            }
+            if (!acx.getPermission().equals(SimplePolicy.LOGGERFINDER_PERMISSION)) {
+                throw new RuntimeException("Unexpected permission in exception: " + acx, acx);
+            }
+            throw new RuntimeException("unexpected exception: " + acx, acx);
+        }
+        if (appLogger2 == sysLogger2) {
+            throw new RuntimeException("identical loggers");
+        }
+
+        final java.util.logging.Logger appSink;
+        final java.util.logging.Logger sysSink;
+        final java.util.logging.Handler appHandler;
+        final java.util.logging.Handler sysHandler;
+        final  LoggerFinder provider;
+        allowAll.get().set(true);
+        try {
+            appSink = java.util.logging.Logger.getLogger("foo");
+            sysSink = accessSystemLogger.demandSystemLogger("foo");
+            appSink.addHandler(appHandler = new MyHandler());
+            sysSink.addHandler(sysHandler = new MyHandler());
+            appSink.setUseParentHandlers(false);
+            sysSink.setUseParentHandlers(false);
+            provider = LoggerFinder.getLoggerFinder();
+        } finally {
+            allowAll.get().set(false);
+        }
+        try {
+            testLogger(provider, loggerDescMap, "foo", null, sysLogger1, sysSink);
+            testLogger(provider, loggerDescMap, "foo", null, appLogger1, appSink);
+            testLogger(provider, loggerDescMap, "foo", loggerBundle, sysLogger2, sysSink);
+            testLogger(provider, loggerDescMap, "foo", loggerBundle, appLogger2, appSink);
+        } finally {
+            allowAll.get().set(true);
+            try {
+                appSink.removeHandler(appHandler);
+                sysSink.removeHandler(sysHandler);
+                sysSink.setLevel(null);
+                appSink.setLevel(null);
+            } finally {
+                allowAll.get().set(false);
+            }
+        }
+    }
+
+    public static class Foo {
+
+    }
+
+    static void verbose(String msg) {
+       if (VERBOSE) {
+           System.out.println(msg);
+       }
+    }
+
+    // Calls the 8 methods defined on Logger and verify the
+    // parameters received by the underlying BaseLoggerFinder.LoggerImpl
+    // logger.
+    private static void testLogger(LoggerFinder provider,
+            Map<Logger, String> loggerDescMap,
+            String name,
+            ResourceBundle loggerBundle,
+            Logger logger,
+            java.util.logging.Logger sink) {
+
+        System.out.println("Testing " + loggerDescMap.get(logger));
+
+        Foo foo = new Foo();
+        String fooMsg = foo.toString();
+        for (Level loggerLevel : Level.values()) {
+            setLevel(sink, mapToJul(loggerLevel));
+            for (Level messageLevel : Level.values()) {
+                String desc = "logger.log(messageLevel, foo): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+
+                LogEvent expected =
+                        LogEvent.of(
+                            sequencer.get(),
+                            messageLevel.compareTo(loggerLevel) >= 0,
+                            name, mapToJul(messageLevel), (ResourceBundle)null,
+                            fooMsg, (Throwable)null, (Object[])null);
+                logger.log(messageLevel, foo);
+                if (loggerLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
+                    if (eventQueue.poll() != null) {
+                        throw new RuntimeException("unexpected event in queue for " + desc);
+                    }
+                } else {
+                    LogEvent actual = eventQueue.poll();
+                    if (!expected.equals(actual)) {
+                        throw new RuntimeException("mismatch for " + desc
+                                + "\n\texpected=" + expected
+                                + "\n\t  actual=" + actual);
+                    } else {
+                        verbose("Got expected results for "
+                                + desc + "\n\t" + expected);
+                    }
+                }
+            }
+        }
+
+        String msg = "blah";
+        for (Level loggerLevel : Level.values()) {
+            setLevel(sink, mapToJul(loggerLevel));
+            for (Level messageLevel : Level.values()) {
+                String desc = "logger.log(messageLevel, \"blah\"): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                LogEvent expected =
+                        LogEvent.of(
+                            sequencer.get(),
+                            messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF,
+                            name, mapToJul(messageLevel), loggerBundle,
+                            msg, (Throwable)null, (Object[])null);
+                logger.log(messageLevel, msg);
+                if (loggerLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
+                    if (eventQueue.poll() != null) {
+                        throw new RuntimeException("unexpected event in queue for " + desc);
+                    }
+                } else {
+                    LogEvent actual =  eventQueue.poll();
+                    if (!expected.equals(actual)) {
+                        throw new RuntimeException("mismatch for " + desc
+                            + "\n\texpected=" + expected
+                            + "\n\t  actual=" + actual);
+                    } else {
+                        verbose("Got expected results for "
+                            + desc + "\n\t" + expected);
+                    }
+                }
+            }
+        }
+
+        Supplier<String> fooSupplier = new Supplier<String>() {
+            @Override
+            public String get() {
+                return this.toString();
+            }
+        };
+
+        for (Level loggerLevel : Level.values()) {
+            setLevel(sink, mapToJul(loggerLevel));
+            for (Level messageLevel : Level.values()) {
+                String desc = "logger.log(messageLevel, fooSupplier): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                LogEvent expected =
+                        LogEvent.of(
+                            sequencer.get(),
+                            messageLevel.compareTo(loggerLevel) >= 0,
+                            name, mapToJul(messageLevel), (ResourceBundle)null,
+                            fooSupplier.get(),
+                            (Throwable)null, (Object[])null);
+                logger.log(messageLevel, fooSupplier);
+                if (loggerLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
+                    if (eventQueue.poll() != null) {
+                        throw new RuntimeException("unexpected event in queue for " + desc);
+                    }
+                } else {
+                    LogEvent actual =  eventQueue.poll();
+                    if (!expected.equals(actual)) {
+                        throw new RuntimeException("mismatch for " + desc
+                                + "\n\texpected=" + expected
+                                + "\n\t  actual=" + actual);
+                    } else {
+                        verbose("Got expected results for "
+                                + desc + "\n\t" + expected);
+                    }
+                }
+            }
+        }
+
+        String format = "two params [{1} {2}]";
+        Object arg1 = foo;
+        Object arg2 = msg;
+        for (Level loggerLevel : Level.values()) {
+            setLevel(sink, mapToJul(loggerLevel));
+            for (Level messageLevel : Level.values()) {
+                String desc = "logger.log(messageLevel, format, params...): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                LogEvent expected =
+                        LogEvent.of(
+                            sequencer.get(),
+                            messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF,
+                            name, mapToJul(messageLevel), loggerBundle,
+                            format, (Throwable)null, new Object[] {arg1, arg2});
+                logger.log(messageLevel, format, arg1, arg2);
+                if (loggerLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
+                    if (eventQueue.poll() != null) {
+                        throw new RuntimeException("unexpected event in queue for " + desc);
+                    }
+                } else {
+                    LogEvent actual =  eventQueue.poll();
+                    if (!expected.equals(actual)) {
+                        throw new RuntimeException("mismatch for " + desc
+                                + "\n\texpected=" + expected
+                                + "\n\t  actual=" + actual);
+                    } else {
+                        verbose("Got expected results for "
+                            + desc + "\n\t" + expected);
+                    }
+                }
+            }
+        }
+
+        Throwable thrown = new Exception("OK: log me!");
+        for (Level loggerLevel : Level.values()) {
+            setLevel(sink, mapToJul(loggerLevel));
+            for (Level messageLevel : Level.values()) {
+                String desc = "logger.log(messageLevel, \"blah\", thrown): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                LogEvent expected =
+                        LogEvent.of(
+                            sequencer.get(),
+                            messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF,
+                            name, mapToJul(messageLevel), loggerBundle,
+                            msg, thrown, (Object[]) null);
+                logger.log(messageLevel, msg, thrown);
+                if (loggerLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
+                    if (eventQueue.poll() != null) {
+                        throw new RuntimeException("unexpected event in queue for " + desc);
+                    }
+                } else {
+                    LogEvent actual =  eventQueue.poll();
+                    if (!expected.equals(actual)) {
+                        throw new RuntimeException("mismatch for " + desc
+                            + "\n\texpected=" + expected
+                            + "\n\t  actual=" + actual);
+                    } else {
+                        verbose("Got expected results for "
+                                + desc + "\n\t" + expected);
+                    }
+                }
+            }
+        }
+
+
+        for (Level loggerLevel : Level.values()) {
+            setLevel(sink, mapToJul(loggerLevel));
+            for (Level messageLevel : Level.values()) {
+                String desc = "logger.log(messageLevel, thrown, fooSupplier): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                LogEvent expected =
+                        LogEvent.of(
+                            sequencer.get(),
+                            messageLevel.compareTo(loggerLevel) >= 0,
+                            name, mapToJul(messageLevel), (ResourceBundle)null,
+                            fooSupplier.get(),
+                            (Throwable)thrown, (Object[])null);
+                logger.log(messageLevel, fooSupplier, thrown);
+                if (loggerLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
+                    if (eventQueue.poll() != null) {
+                        throw new RuntimeException("unexpected event in queue for " + desc);
+                    }
+                } else {
+                    LogEvent actual =  eventQueue.poll();
+                    if (!expected.equals(actual)) {
+                        throw new RuntimeException("mismatch for " + desc
+                                + "\n\texpected=" + expected
+                                + "\n\t  actual=" + actual);
+                    } else {
+                        verbose("Got expected results for "
+                                + desc + "\n\t" + expected);
+                    }
+                }
+            }
+        }
+
+        ResourceBundle bundle = ResourceBundle.getBundle(MyBundle.class.getName());
+        for (Level loggerLevel : Level.values()) {
+            setLevel(sink, mapToJul(loggerLevel));
+            for (Level messageLevel : Level.values()) {
+                String desc = "logger.log(messageLevel, bundle, format, params...): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                LogEvent expected =
+                        LogEvent.of(
+                            sequencer.get(),
+                            messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF,
+                            name, mapToJul(messageLevel), bundle,
+                            format, (Throwable)null, new Object[] {foo, msg});
+                logger.log(messageLevel, bundle, format, foo, msg);
+                if (loggerLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
+                    if (eventQueue.poll() != null) {
+                        throw new RuntimeException("unexpected event in queue for " + desc);
+                    }
+                } else {
+                    LogEvent actual =  eventQueue.poll();
+                    if (!expected.equals(actual)) {
+                        throw new RuntimeException("mismatch for " + desc
+                            + "\n\texpected=" + expected
+                            + "\n\t  actual=" + actual);
+                    } else {
+                        verbose("Got expected results for "
+                            + desc + "\n\t" + expected);
+                    }
+                }
+            }
+        }
+
+        for (Level loggerLevel : Level.values()) {
+            setLevel(sink, mapToJul(loggerLevel));
+            for (Level messageLevel : Level.values()) {
+                String desc = "logger.log(messageLevel, bundle, \"blah\", thrown): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                LogEvent expected =
+                        LogEvent.of(
+                            sequencer.get(),
+                            messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF,
+                            name, mapToJul(messageLevel), bundle,
+                            msg, thrown, (Object[]) null);
+                logger.log(messageLevel, bundle, msg, thrown);
+                if (loggerLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
+                    if (eventQueue.poll() != null) {
+                        throw new RuntimeException("unexpected event in queue for " + desc);
+                    }
+                } else {
+                    LogEvent actual =  eventQueue.poll();
+                    if (!expected.equals(actual)) {
+                        throw new RuntimeException("mismatch for " + desc
+                            + "\n\texpected=" + expected
+                            + "\n\t  actual=" + actual);
+                    } else {
+                        verbose("Got expected results for "
+                            + desc + "\n\t" + expected);
+                    }
+                }
+            }
+        }
+    }
+
+    final static class PermissionsBuilder {
+        final Permissions perms;
+        public PermissionsBuilder() {
+            this(new Permissions());
+        }
+        public PermissionsBuilder(Permissions perms) {
+            this.perms = perms;
+        }
+        public PermissionsBuilder add(Permission p) {
+            perms.add(p);
+            return this;
+        }
+        public PermissionsBuilder addAll(PermissionCollection col) {
+            if (col != null) {
+                for (Enumeration<Permission> e = col.elements(); e.hasMoreElements(); ) {
+                    perms.add(e.nextElement());
+                }
+            }
+            return this;
+        }
+        public Permissions toPermissions() {
+            final PermissionsBuilder builder = new PermissionsBuilder();
+            builder.addAll(perms);
+            return builder.perms;
+        }
+    }
+
+    public static class SimplePolicy extends Policy {
+        static final RuntimePermission LOGGERFINDER_PERMISSION =
+                new RuntimePermission("loggerFinder");
+        final Permissions permissions;
+        final Permissions allPermissions;
+        final Permissions controlPermissions;
+        final ThreadLocal<AtomicBoolean> allowControl;
+        final ThreadLocal<AtomicBoolean> allowAll;
+        public SimplePolicy(ThreadLocal<AtomicBoolean> allowControl, ThreadLocal<AtomicBoolean> allowAll) {
+            this.allowControl = allowControl;
+            this.allowAll = allowAll;
+            permissions = new Permissions();
+
+            // these are used for configuring the test itself...
+            controlPermissions = new Permissions();
+            controlPermissions.add(LOGGERFINDER_PERMISSION);
+            allPermissions = new Permissions();
+            allPermissions.add(new java.security.AllPermission());
+
+        }
+
+        @Override
+        public boolean implies(ProtectionDomain domain, Permission permission) {
+            if (allowAll.get().get()) return allPermissions.implies(permission);
+            if (allowControl.get().get()) return controlPermissions.implies(permission);
+            return permissions.implies(permission);
+        }
+
+        @Override
+        public PermissionCollection getPermissions(CodeSource codesource) {
+            return new PermissionsBuilder().addAll(allowAll.get().get()
+                    ? allPermissions : allowControl.get().get()
+                    ? controlPermissions : permissions).toPermissions();
+        }
+
+        @Override
+        public PermissionCollection getPermissions(ProtectionDomain domain) {
+            return new PermissionsBuilder().addAll(allowAll.get().get()
+                    ? allPermissions : allowControl.get().get()
+                    ? controlPermissions : permissions).toPermissions();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/Logger/interface/LoggerInterfaceTest.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,592 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.util.ResourceBundle;
+import java.util.function.Consumer;
+import java.lang.System.Logger.Level;
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.Objects;
+import java.util.Queue;
+import java.util.function.Supplier;
+
+/**
+ * @test
+ * @bug 8140364
+ * @summary Tests the default body of the System.Logger interface.
+ * @author danielfuchs
+ */
+public class LoggerInterfaceTest {
+
+    public static class LoggerImpl implements System.Logger {
+
+        public static class LogEvent implements Cloneable {
+            Level level;
+            ResourceBundle bundle;
+            String msg;
+            Throwable thrown;
+            Object[] params;
+            StackTraceElement[] callStack;
+
+            @Override
+            protected LogEvent clone() {
+                try {
+                    return (LogEvent)super.clone();
+                } catch (CloneNotSupportedException x) {
+                    throw new RuntimeException(x);
+                }
+            }
+
+
+        }
+
+        public static class LogEventBuilder {
+            private LogEvent event = new LogEvent();
+            public LogEventBuilder level(Level level) {
+                event.level = level;
+                return this;
+            }
+            public LogEventBuilder stack(StackTraceElement... stack) {
+                event.callStack = stack;
+                return this;
+            }
+            public LogEventBuilder bundle(ResourceBundle bundle) {
+                event.bundle = bundle;
+                return this;
+            }
+            public LogEventBuilder msg(String msg) {
+                event.msg = msg;
+                return this;
+            }
+            public LogEventBuilder thrown(Throwable thrown) {
+                event.thrown = thrown;
+                return this;
+            }
+            public LogEventBuilder params(Object... params) {
+                event.params = params;
+                return this;
+            }
+            public LogEvent build() {
+                return event.clone();
+            }
+
+            public LogEventBuilder clear() {
+                event = new LogEvent();
+                return this;
+            }
+
+        }
+
+        Level level = Level.WARNING;
+        Consumer<LogEvent> consumer;
+        final LogEventBuilder builder = new LogEventBuilder();
+
+        @Override
+        public String getName() {
+            return "noname";
+        }
+
+        @Override
+        public boolean isLoggable(Level level) {
+            return level.getSeverity() >= this.level.getSeverity();
+        }
+
+        @Override
+        public void log(Level level, ResourceBundle bundle, String msg, Throwable thrown) {
+            builder.clear().level(level).bundle(bundle).msg(msg).thrown(thrown)
+                    .stack(new Exception().getStackTrace());
+            consumer.accept(builder.build());
+        }
+
+        @Override
+        public void log(Level level, ResourceBundle bundle, String format, Object... params) {
+            builder.clear().level(level).bundle(bundle).msg(format).params(params)
+                    .stack(new Exception().getStackTrace());
+            consumer.accept(builder.build());
+        }
+
+    }
+
+    static class Throwing {
+        @Override
+        public String toString() {
+            throw new RuntimeException("should not have been called");
+        }
+    }
+    static class NotTrowing {
+        private final String toString;
+        private int count = 0;
+        public NotTrowing(String toString) {
+            this.toString = toString;
+        }
+
+        @Override
+        public String toString() {
+            return toString + "[" + (++count) + "]";
+        }
+    }
+
+    public static void main(String[] args) {
+        final LoggerImpl loggerImpl = new LoggerImpl();
+        final System.Logger logger = loggerImpl;
+        final Queue<LoggerImpl.LogEvent> events = new LinkedList<>();
+        loggerImpl.consumer = (x) -> events.add(x);
+
+        System.out.println("\nlogger.isLoggable(Level)");
+        assertTrue(logger.isLoggable(Level.WARNING), "logger.isLoggable(Level.WARNING)","  ");
+        assertFalse(logger.isLoggable(Level.INFO), "logger.isLoggable(Level.INFO)", "  ");
+
+
+        System.out.println("\nlogger.log(Level, Object)");
+        for (Level l : Level.values()) {
+            boolean logged = l.compareTo(Level.WARNING) >= 0;
+            Object[][] cases = new Object[][] {
+                {null}, {"baz"}
+            };
+            for (Object[] p : cases) {
+                String msg = (String)p[0];
+                final Object obj = msg == null ? null : logged ? new NotTrowing(msg) : new Throwing();
+                String par1 = msg == null ? "(Object)null"
+                        : logged ? "new NotTrowing(\""+ msg+"\")" : "new Throwing()";
+                System.out.println("  logger.log(" + l + ", " +  par1 + ")");
+                try {
+                    logger.log(l, obj);
+                    if (obj == null) {
+                        throw new RuntimeException("Expected NullPointerException not thrown for"
+                                  + " logger.log(" + l + ", " +  par1 + ")");
+                    }
+                } catch (NullPointerException x) {
+                    if (obj == null) {
+                        System.out.println("    Got expected exception: " + x);
+                        continue;
+                    } else {
+                        throw x;
+                    }
+                }
+                LoggerImpl.LogEvent e = events.poll();
+                if (logged) {
+                    assertNonNull(e, "e", "    ");
+                    assertEquals(l, e.level, "e.level", "    ");
+                    assertToString(e.msg, msg, 1, "e.msg", "    ");
+                    assertEquals(e.bundle, null, "e.bundle", "    ");
+                    assertEquals(e.params, null, "e.params", "    ");
+                    assertEquals(e.thrown, null, "e.thrown", "    ");
+                    assertEquals(e.bundle, null, "e.bundle", "    ");
+                    assertEquals(e.callStack[0].getMethodName(), "log",
+                                 "e.callStack[0].getMethodName()", "    ");
+                    assertEquals(e.callStack[0].getClassName(),
+                                logger.getClass().getName(),
+                                "e.callStack[0].getClassName() ", "    ");
+                    assertEquals(e.callStack[1].getMethodName(), "log",
+                                 "e.callStack[1].getMethodName()", "    ");
+                    assertEquals(e.callStack[1].getClassName(),
+                                 System.Logger.class.getName(),
+                                 "e.callStack[1].getClassName() ", "    ");
+                    assertEquals(e.callStack[2].getMethodName(), "main",
+                                 "e.callStack[2].getMethodName()", "    ");
+                } else {
+                    assertEquals(e, null, "e", "    ");
+                }
+            }
+        }
+        System.out.println("  logger.log(" + null + ", " +
+                "new NotThrowing(\"foobar\")" + ")");
+        try {
+            logger.log(null, new NotTrowing("foobar"));
+            throw new RuntimeException("Expected NullPointerException not thrown for"
+                                      + " logger.log(" + null + ", "
+                                      + "new NotThrowing(\"foobar\")" + ")");
+        } catch (NullPointerException x) {
+            System.out.println("    Got expected exception: " + x);
+        }
+
+
+        System.out.println("\nlogger.log(Level, String)");
+        for (Level l : Level.values()) {
+            boolean logged = l.compareTo(Level.WARNING) >= 0;
+            String par = "bar";
+            System.out.println("  logger.log(" + l + ", \"" +  par +"\");");
+            logger.log(l, par);
+            LoggerImpl.LogEvent e = events.poll();
+            assertNonNull(e, "e", "    ");
+            assertEquals(e.level, l, "e.level", "    ");
+            assertEquals(e.msg, "bar", "e.msg", "    ");
+            assertEquals(e.bundle, null, "e.bundle", "    ");
+            assertEquals(e.params, null, "e.params", "    ");
+            assertEquals(e.thrown, null, "e.thrown", "    ");
+            assertEquals(e.bundle, null, "e.bundle", "    ");
+            assertEquals(e.callStack[0].getMethodName(), "log",
+                         "e.callStack[0].getMethodName()", "    ");
+            assertEquals(e.callStack[0].getClassName(),
+                         logger.getClass().getName(),
+                         "e.callStack[0].getClassName() ", "    ");
+            assertEquals(e.callStack[1].getMethodName(), "log",
+                         "e.callStack[1].getMethodName()", "    ");
+            assertEquals(e.callStack[1].getClassName(),
+                         System.Logger.class.getName(),
+                         "e.callStack[1].getClassName() ", "    ");
+            assertEquals(e.callStack[2].getMethodName(), "main",
+                         "e.callStack[2].getMethodName()", "    ");
+
+            System.out.println("  logger.log(" + l + ", (String)null);");
+            logger.log(l, (String)null);
+            e = events.poll();
+            assertNonNull(e, "e", "    ");
+            assertEquals(e.level, l, "e.level", "    ");
+            assertEquals(e.msg, null, "e.msg", "    ");
+            assertEquals(e.bundle, null, "e.bundle", "    ");
+            assertEquals(e.params, null, "e.params", "    ");
+            assertEquals(e.thrown, null, "e.thrown", "    ");
+            assertEquals(e.bundle, null, "e.bundle", "    ");
+            assertEquals(e.callStack[0].getMethodName(), "log",
+                         "e.callStack[0].getMethodName()", "    ");
+            assertEquals(e.callStack[0].getClassName(),
+                         logger.getClass().getName(),
+                         "e.callStack[0].getClassName() ", "    ");
+            assertEquals(e.callStack[1].getMethodName(), "log",
+                         "e.callStack[1].getMethodName()", "    ");
+            assertEquals(e.callStack[1].getClassName(),
+                         System.Logger.class.getName(),
+                         "e.callStack[1].getClassName() ", "    ");
+            assertEquals(e.callStack[2].getMethodName(), "main",
+                         "e.callStack[2].getMethodName()", "    ");
+        }
+
+        System.out.println("\nlogger.log(Level, Supplier<String>)");
+        for (Level l : Level.values()) {
+            boolean logged = l.compareTo(Level.WARNING) >= 0;
+            Object[][] cases = new Object[][] {
+                {null}, {"baz"}
+            };
+            for (Object[] p : cases) {
+                String msg = (String)p[0];
+                final Object obj = msg == null ? null : logged ? new NotTrowing(msg) : new Throwing();
+                final Supplier<String> s = msg == null ? null : () -> obj.toString();
+                String par1 = msg == null ? "(Supplier<String>)null"
+                        : logged ? "() -> new NotTrowing(\""+ msg+"\").toString()" : "new Throwing()";
+                System.out.println("  logger.log(" + l + ", " +  par1 + ")");
+                try {
+                    logger.log(l, s);
+                    if (s == null) {
+                        throw new RuntimeException("Expected NullPointerException not thrown for"
+                                  + " logger.log(" + l + ", " +  par1 + ")");
+                    }
+                } catch (NullPointerException x) {
+                    if (s == null) {
+                        System.out.println("    Got expected exception: " + x);
+                        continue;
+                    } else {
+                        throw x;
+                    }
+                }
+                LoggerImpl.LogEvent e = events.poll();
+                if (logged) {
+                    assertNonNull(e, "e", "    ");
+                    assertEquals(l, e.level, "e.level", "    ");
+                    assertToString(e.msg, msg, 1, "e.msg", "    ");
+                    assertEquals(e.bundle, null, "e.bundle", "    ");
+                    assertEquals(e.params, null, "e.params", "    ");
+                    assertEquals(e.thrown, null, "e.thrown", "    ");
+                    assertEquals(e.bundle, null, "e.bundle", "    ");
+                    assertEquals(e.callStack[0].getMethodName(), "log",
+                                 "e.callStack[0].getMethodName()", "    ");
+                    assertEquals(e.callStack[0].getClassName(),
+                                 logger.getClass().getName(),
+                                 "e.callStack[0].getClassName() ", "    ");
+                    assertEquals(e.callStack[1].getMethodName(), "log",
+                                 "e.callStack[1].getMethodName()", "    ");
+                    assertEquals(e.callStack[1].getClassName(),
+                                 System.Logger.class.getName(),
+                                 "e.callStack[1].getClassName() ", "    ");
+                    assertEquals(e.callStack[2].getMethodName(), "main",
+                                 "e.callStack[2].getMethodName()", "    ");
+                } else {
+                    assertEquals(e, null, "e", "    ");
+                }
+            }
+        }
+        System.out.println("  logger.log(" + null + ", " + "() -> \"biz\"" + ")");
+        try {
+            logger.log(null, () -> "biz");
+            throw new RuntimeException("Expected NullPointerException not thrown for"
+                                      + " logger.log(" + null + ", "
+                                      + "() -> \"biz\"" + ")");
+        } catch (NullPointerException x) {
+            System.out.println("    Got expected exception: " + x);
+        }
+
+        System.out.println("\nlogger.log(Level, String, Object...)");
+        for (Level l : Level.values()) {
+            boolean logged = l.compareTo(Level.WARNING) >= 0;
+            String par = "bam";
+            Object[] params = null;
+            System.out.println("  logger.log(" + l + ", \"" +  par +"\", null);");
+            logger.log(l, par, params);
+            LoggerImpl.LogEvent e = events.poll();
+            assertNonNull(e, "e", "    ");
+            assertEquals(l, e.level, "e.level", "    ");
+            assertEquals(e.msg, "bam", "e.msg", "    ");
+            assertEquals(e.bundle, null, "e.bundle", "    ");
+            assertEquals(e.params, null, "e.params", "    ");
+            assertEquals(e.thrown, null, "e.thrown", "    ");
+            assertEquals(e.bundle, null, "e.bundle", "    ");
+            assertEquals(e.callStack[0].getMethodName(), "log",
+                        "e.callStack[0].getMethodName()", "    ");
+            assertEquals(e.callStack[0].getClassName(),
+                         logger.getClass().getName(),
+                         "e.callStack[0].getClassName() ", "    ");
+            assertEquals(e.callStack[1].getMethodName(), "log",
+                         "e.callStack[1].getMethodName()", "    ");
+            assertEquals(e.callStack[1].getClassName(),
+                         System.Logger.class.getName(),
+                         "e.callStack[1].getClassName() ", "    ");
+            assertEquals(e.callStack[2].getMethodName(), "main",
+                         "e.callStack[2].getMethodName()", "    ");
+
+            params = new Object[] {new NotTrowing("one")};
+            par = "bam {0}";
+            System.out.println("  logger.log(" + l + ", \"" +  par
+                               + "\", new NotTrowing(\"one\"));");
+            logger.log(l, par, params[0]);
+            e = events.poll();
+            assertNonNull(e, "e", "    ");
+            assertEquals(l, e.level, "e.level", "    ");
+            assertEquals(e.msg, par, "e.msg", "    ");
+            assertEquals(e.bundle, null, "e.bundle", "    ");
+            assertArrayEquals(e.params, params, "e.params", "    ");
+            assertEquals(e.thrown, null, "e.thrown", "    ");
+            assertEquals(e.bundle, null, "e.bundle", "    ");
+            assertEquals(e.callStack[0].getMethodName(), "log",
+                         "e.callStack[0].getMethodName()", "    ");
+            assertEquals(e.callStack[0].getClassName(),
+                         logger.getClass().getName(),
+                         "e.callStack[0].getClassName() ", "    ");
+            assertEquals(e.callStack[1].getMethodName(), "log",
+                         "e.callStack[1].getMethodName()", "    ");
+            assertEquals(e.callStack[1].getClassName(),
+                         System.Logger.class.getName(),
+                         "e.callStack[1].getClassName() ", "    ");
+            assertEquals(e.callStack[2].getMethodName(), "main",
+                         "e.callStack[2].getMethodName()", "    ");
+
+            params = new Object[] {new NotTrowing("fisrt"), new NotTrowing("second")};
+            par = "bam {0} {1}";
+            System.out.println("  logger.log(" + l + ", \"" +  par
+                              + "\", new NotTrowing(\"fisrt\"),"
+                              + " new NotTrowing(\"second\"));");
+            logger.log(l, par, params[0], params[1]);
+            e = events.poll();
+            assertNonNull(e, "e", "    ");
+            assertEquals(l, e.level, "e.level", "    ");
+            assertEquals(e.msg, par, "e.msg", "    ");
+            assertEquals(e.bundle, null, "e.bundle", "    ");
+            assertArrayEquals(e.params, params, "e.params", "    ");
+            assertEquals(e.thrown, null, "e.thrown", "    ");
+            assertEquals(e.bundle, null, "e.bundle", "    ");
+            assertEquals(e.callStack[0].getMethodName(), "log",
+                         "e.callStack[0].getMethodName()", "    ");
+            assertEquals(e.callStack[0].getClassName(),
+                         logger.getClass().getName(),
+                         "e.callStack[0].getClassName() ", "    ");
+            assertEquals(e.callStack[1].getMethodName(), "log",
+                         "e.callStack[1].getMethodName()", "    ");
+            assertEquals(e.callStack[1].getClassName(),
+                         System.Logger.class.getName(),
+                         "e.callStack[1].getClassName() ", "    ");
+            assertEquals(e.callStack[2].getMethodName(), "main",
+                         "e.callStack[2].getMethodName()", "    ");
+
+            params = new Object[] {new NotTrowing("third"), new NotTrowing("fourth")};
+            par = "bam {2}";
+            System.out.println("  logger.log(" + l + ", \"" +  par
+                              + "\", new Object[] {new NotTrowing(\"third\"),"
+                              + " new NotTrowing(\"fourth\")});");
+            logger.log(l, par, params);
+            e = events.poll();
+            assertNonNull(e, "e", "    ");
+            assertEquals(l, e.level, "e.level", "    ");
+            assertEquals(e.msg, par, "e.msg", "    ");
+            assertEquals(e.bundle, null, "e.bundle", "    ");
+            assertArrayEquals(e.params, params, "e.params", "    ");
+            assertEquals(e.thrown, null, "e.thrown", "    ");
+            assertEquals(e.bundle, null, "e.bundle", "    ");
+            assertEquals(e.callStack[0].getMethodName(), "log",
+                         "e.callStack[0].getMethodName()", "    ");
+            assertEquals(e.callStack[0].getClassName(), logger.getClass().getName(),
+                         "e.callStack[0].getClassName() ", "    ");
+            assertEquals(e.callStack[1].getMethodName(), "log",
+                         "e.callStack[1].getMethodName()", "    ");
+            assertEquals(e.callStack[1].getClassName(),
+                         System.Logger.class.getName(),
+                         "e.callStack[1].getClassName() ", "    ");
+            assertEquals(e.callStack[2].getMethodName(), "main",
+                        "e.callStack[2].getMethodName()", "    ");
+        }
+
+        System.out.println("\nlogger.log(Level, String, Throwable)");
+        for (Level l : Level.values()) {
+            boolean logged = l.compareTo(Level.WARNING) >= 0;
+            Object[][] cases = new Object[][] {
+                {null, null}, {null, new Throwable()}, {"biz", null}, {"boz", new Throwable()}
+            };
+            for (Object[] p : cases) {
+                String msg = (String)p[0];
+                Throwable thrown = (Throwable)p[1];
+                String par1 = msg == null ? "(String)null" : "\"" + msg + "\"";
+                String par2 = thrown == null ? "(Throwable)null" : "new Throwable()";
+                System.out.println("  logger.log(" + l + ", " +  par1 +", " + par2 + ")");
+                logger.log(l, msg, thrown);
+                LoggerImpl.LogEvent e = events.poll();
+                assertNonNull(e, "e", "    ");
+                assertEquals(e.level, l, "e.level", "    ");
+                assertEquals(e.msg, msg, "e.msg", "    ");
+                assertEquals(e.bundle, null, "e.bundle", "    ");
+                assertEquals(e.params, null, "e.params", "    ");
+                assertEquals(e.thrown, thrown, "e.thrown", "    ");
+                assertEquals(e.bundle, null, "e.bundle", "    ");
+                assertEquals(e.callStack[0].getMethodName(),
+                             "log", "e.callStack[0].getMethodName()", "    ");
+                assertEquals(e.callStack[0].getClassName(),
+                            logger.getClass().getName(),
+                            "e.callStack[0].getClassName() ", "    ");
+                assertEquals(e.callStack[1].getMethodName(), "log",
+                             "e.callStack[1].getMethodName()", "    ");
+                assertEquals(e.callStack[1].getClassName(),
+                            System.Logger.class.getName(),
+                            "e.callStack[1].getClassName() ", "    ");
+                assertEquals(e.callStack[2].getMethodName(), "main",
+                             "e.callStack[2].getMethodName()", "    ");
+            }
+        }
+
+        System.out.println("\nlogger.log(Level, Supplier<String>, Throwable)");
+        for (Level l : Level.values()) {
+            boolean logged = l.compareTo(Level.WARNING) >= 0;
+            Object[][] cases = new Object[][] {
+                {null, null}, {null, new Throwable()}, {"biz", null}, {"boz", new Throwable()}
+            };
+            for (Object[] p : cases) {
+                String msg = (String)p[0];
+                Throwable thrown = (Throwable)p[1];
+                final Object obj = msg == null ? null : logged ? new NotTrowing(msg) : new Throwing();
+                final Supplier<String> s = msg == null ? null : () -> obj.toString();
+                String par1 = msg == null ? "(Supplier<String>)null"
+                        : logged ? "() -> new NotTrowing(\""+ msg+"\").toString()" : "new Throwing()";
+                String par2 = thrown == null ? "(Throwable)null" : "new Throwable()";
+                System.out.println("  logger.log(" + l + ", " +  par1 +", " + par2 + ")");
+                try {
+                    logger.log(l, s, thrown);
+                    if (s== null) {
+                        throw new RuntimeException("Expected NullPointerException not thrown for"
+                                  + " logger.log(" + l + ", " +  par1 +", " + par2 + ")");
+                    }
+                } catch (NullPointerException x) {
+                    if (s == null) {
+                        System.out.println("    Got expected exception: " + x);
+                        continue;
+                    } else {
+                        throw x;
+                    }
+                }
+                LoggerImpl.LogEvent e = events.poll();
+                if (logged) {
+                    assertNonNull(e, "e", "    ");
+                    assertEquals(l, e.level, "e.level", "    ");
+                    assertToString(e.msg, msg, 1, "e.msg", "    ");
+                    assertEquals(e.bundle, null, "e.bundle", "    ");
+                    assertEquals(e.params, null, "e.params", "    ");
+                    assertEquals(e.thrown, thrown, "e.thrown", "    ");
+                    assertEquals(e.bundle, null, "e.bundle", "    ");
+                    assertEquals(e.callStack[0].getMethodName(), "log",
+                                 "e.callStack[0].getMethodName()", "    ");
+                    assertEquals(e.callStack[0].getClassName(),
+                                 logger.getClass().getName(),
+                                 "e.callStack[0].getClassName() ", "    ");
+                    assertEquals(e.callStack[1].getMethodName(), "log",
+                                 "e.callStack[1].getMethodName()", "    ");
+                    assertEquals(e.callStack[1].getClassName(),
+                                 System.Logger.class.getName(),
+                                 "e.callStack[1].getClassName() ", "    ");
+                    assertEquals(e.callStack[2].getMethodName(), "main",
+                                 "e.callStack[2].getMethodName()", "    ");
+                } else {
+                    assertEquals(e, null, "e", "    ");
+                }
+            }
+        }
+        System.out.println("  logger.log(" + null + ", " + "() -> \"biz\""
+                           + ", " + "new Throwable()" + ")");
+        try {
+            logger.log(null, () -> "biz", new Throwable());
+            throw new RuntimeException("Expected NullPointerException not thrown for"
+                                      + " logger.log(" + null + ", "
+                                      + "() -> \"biz\"" + ", "
+                                      + "new Throwable()" + ")");
+        } catch (NullPointerException x) {
+            System.out.println("    Got expected exception: " + x);
+        }
+
+        System.out.println("Checking that we have no spurious events in the queue");
+        assertEquals(events.poll(), null, "events.poll()", "  ");
+    }
+
+    static void assertTrue(boolean test, String what, String prefix) {
+        if (!test) {
+            throw new RuntimeException("Expected true for " + what);
+        }
+        System.out.println(prefix + "Got expected " + what + ": " + test);
+    }
+    static void assertFalse(boolean test, String what, String prefix) {
+        if (test) {
+            throw new RuntimeException("Expected false for " + what);
+        }
+        System.out.println(prefix + "Got expected " + what + ": " + test);
+    }
+    static void assertToString(String actual, String expected, int count, String what, String prefix) {
+        assertEquals(actual, expected + "["+count+"]", what, prefix);
+    }
+    static void assertEquals(Object actual, Object expected, String what, String prefix) {
+        if (!Objects.equals(actual, expected)) {
+            throw new RuntimeException("Bad " + what + ":"
+                    + "\n\t expected: " + expected
+                    + "\n\t   actual: " + actual);
+        }
+        System.out.println(prefix + "Got expected " + what + ": " + actual);
+    }
+    static void assertArrayEquals(Object[] actual, Object[] expected, String what, String prefix) {
+        if (!Objects.deepEquals(actual, expected)) {
+            throw new RuntimeException("Bad " + what + ":"
+                    + "\n\t expected: " + expected == null ? "null" : Arrays.deepToString(expected)
+                    + "\n\t   actual: " + actual == null ? "null" : Arrays.deepToString(actual));
+        }
+        System.out.println(prefix + "Got expected " + what + ": " + Arrays.deepToString(actual));
+    }
+    static void assertNonNull(Object actual, String what, String prefix) {
+        if (Objects.equals(actual, null)) {
+            throw new RuntimeException("Bad " + what + ":"
+                    + "\n\t expected: non null"
+                    + "\n\t   actual: " + actual);
+        }
+        System.out.println(prefix + "Got expected " + what + ": " + "non null");
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/LoggerFinder/BaseLoggerFinderTest/AccessSystemLogger.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+import java.io.IOException;
+import java.lang.System.Logger;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+import java.util.ResourceBundle;
+
+/**
+ *
+ * @author danielfuchs
+ */
+public final class AccessSystemLogger {
+
+    public AccessSystemLogger() {
+        this(check());
+    }
+
+    private AccessSystemLogger(Void unused) {
+    }
+
+    private static Void check() {
+        if (AccessSystemLogger.class.getClassLoader() != null) {
+            throw new RuntimeException("AccessSystemLogger should be loaded by the null classloader");
+        }
+        return null;
+    }
+
+    public Logger getLogger(String name) {
+        Logger logger = System.getLogger(name);
+        System.out.println("System.getLogger(\"" + name + "\"): " + logger);
+        return logger;
+    }
+
+    public Logger getLogger(String name, ResourceBundle bundle) {
+        Logger logger = System.getLogger(name, bundle);
+        System.out.println("System.getLogger(\"" + name + "\", bundle): " + logger);
+        return logger;
+    }
+
+    // copy AccessSystemLogger.class to ./boot
+    public static void main(String[] args) throws IOException {
+        Path testDir = Paths.get(System.getProperty("user.dir", "."));
+        Path bootDir = Paths.get(testDir.toString(), "boot");
+        Path classes = Paths.get(System.getProperty("test.classes", "build/classes"));
+        Path thisClass = Paths.get(classes.toString(),
+                AccessSystemLogger.class.getSimpleName()+".class");
+        if (Files.notExists(bootDir)) {
+            Files.createDirectory(bootDir);
+        }
+        Path dest = Paths.get(bootDir.toString(),
+                AccessSystemLogger.class.getSimpleName()+".class");
+        Files.copy(thisClass, dest, StandardCopyOption.REPLACE_EXISTING);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/LoggerFinder/BaseLoggerFinderTest/BaseLoggerFinder.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.lang.System.LoggerFinder;
+import java.lang.System.Logger;
+
+public  class BaseLoggerFinder extends LoggerFinder implements TestLoggerFinder {
+
+    static final RuntimePermission LOGGERFINDER_PERMISSION =
+                new RuntimePermission("loggerFinder");
+    @Override
+    public Logger getLogger(String name, Class<?> caller) {
+        SecurityManager sm = System.getSecurityManager();
+        if (sm != null) {
+            sm.checkPermission(LOGGERFINDER_PERMISSION);
+        }
+        PrivilegedAction<ClassLoader> pa = () -> caller.getClassLoader();
+        ClassLoader callerLoader = AccessController.doPrivileged(pa);
+        if (callerLoader == null) {
+            return system.computeIfAbsent(name, (n) -> new LoggerImpl(n));
+        } else {
+            return user.computeIfAbsent(name, (n) -> new LoggerImpl(n));
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/LoggerFinder/BaseLoggerFinderTest/BaseLoggerFinderTest.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,694 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+import java.security.AccessControlException;
+import java.security.CodeSource;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.Permissions;
+import java.security.Policy;
+import java.security.ProtectionDomain;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.ResourceBundle;
+import java.util.stream.Stream;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.Supplier;
+import java.lang.System.LoggerFinder;
+import java.lang.System.Logger;
+import java.lang.System.Logger.Level;
+
+/**
+ * @test
+ * @bug     8140364
+ * @summary Tests a naive implementation of LoggerFinder, and in particular
+ *   the default body of System.Logger methods.
+ * @build AccessSystemLogger BaseLoggerFinderTest CustomSystemClassLoader BaseLoggerFinder TestLoggerFinder
+ * @run  driver AccessSystemLogger
+ * @run  main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader BaseLoggerFinderTest NOSECURITY
+ * @run  main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader BaseLoggerFinderTest NOPERMISSIONS
+ * @run  main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader BaseLoggerFinderTest WITHPERMISSIONS
+ * @author danielfuchs
+ */
+public class BaseLoggerFinderTest {
+
+    static final RuntimePermission LOGGERFINDER_PERMISSION =
+                new RuntimePermission("loggerFinder");
+    final static boolean VERBOSE = false;
+    static final ThreadLocal<AtomicBoolean> allowControl = new ThreadLocal<AtomicBoolean>() {
+        @Override
+        protected AtomicBoolean initialValue() {
+            return  new AtomicBoolean(false);
+        }
+    };
+    static final ThreadLocal<AtomicBoolean> allowAccess = new ThreadLocal<AtomicBoolean>() {
+        @Override
+        protected AtomicBoolean initialValue() {
+            return  new AtomicBoolean(false);
+        }
+    };
+
+    final static AccessSystemLogger accessSystemLogger = new AccessSystemLogger();
+    static final Class<?> providerClass;
+    static {
+        try {
+            providerClass = ClassLoader.getSystemClassLoader().loadClass("BaseLoggerFinder");
+        } catch (ClassNotFoundException ex) {
+            throw new ExceptionInInitializerError(ex);
+        }
+    }
+
+    public static class MyBundle extends ResourceBundle {
+
+        final ConcurrentHashMap<String,String> map = new ConcurrentHashMap<>();
+
+        @Override
+        protected Object handleGetObject(String key) {
+            if (key.contains(" (translated)")) {
+                throw new RuntimeException("Unexpected key: " + key);
+            }
+            return map.computeIfAbsent(key, k -> k + " (translated)");
+        }
+
+        @Override
+        public Enumeration<String> getKeys() {
+            return Collections.enumeration(map.keySet());
+        }
+
+    }
+    public static class MyLoggerBundle extends MyBundle {
+
+    }
+
+    static enum TestCases {NOSECURITY, NOPERMISSIONS, WITHPERMISSIONS};
+
+    static void setSecurityManager() {
+        if (System.getSecurityManager() == null) {
+            Policy.setPolicy(new SimplePolicy(allowControl, allowAccess));
+            System.setSecurityManager(new SecurityManager());
+        }
+    }
+
+    public static void main(String[] args) {
+        if (args.length == 0)
+            args = new String[] {
+                //"NOSECURITY",
+                "NOPERMISSIONS",
+                "WITHPERMISSIONS"
+            };
+
+        System.out.println("Using provider class: " + providerClass + "[" + providerClass.getClassLoader() + "]");
+
+        Stream.of(args).map(TestCases::valueOf).forEach((testCase) -> {
+            TestLoggerFinder provider;
+            switch (testCase) {
+                case NOSECURITY:
+                    System.out.println("\n*** Without Security Manager\n");
+                    provider = TestLoggerFinder.class.cast(LoggerFinder.getLoggerFinder());
+                    test(provider, true);
+                    System.out.println("Tetscase count: " + TestLoggerFinder.sequencer.get());
+                    break;
+                case NOPERMISSIONS:
+                    System.out.println("\n*** With Security Manager, without permissions\n");
+                    setSecurityManager();
+                    try {
+                        provider = TestLoggerFinder.class.cast(LoggerFinder.getLoggerFinder());
+                        throw new RuntimeException("Expected exception not raised");
+                    } catch (AccessControlException x) {
+                        if (!LOGGERFINDER_PERMISSION.equals(x.getPermission())) {
+                            throw new RuntimeException("Unexpected permission check", x);
+                        }
+                        final boolean control = allowControl.get().get();
+                        try {
+                            allowControl.get().set(true);
+                            provider = TestLoggerFinder.class.cast(LoggerFinder.getLoggerFinder());
+                        } finally {
+                            allowControl.get().set(control);
+                        }
+                    }
+                    test(provider, false);
+                    System.out.println("Tetscase count: " + TestLoggerFinder.sequencer.get());
+                    break;
+                case WITHPERMISSIONS:
+                    System.out.println("\n*** With Security Manager, with control permission\n");
+                    setSecurityManager();
+                    final boolean control = allowControl.get().get();
+                    try {
+                        allowControl.get().set(true);
+                        provider = TestLoggerFinder.class.cast(LoggerFinder.getLoggerFinder());
+                        test(provider, true);
+                    } finally {
+                        allowControl.get().set(control);
+                    }
+                    break;
+                default:
+                    throw new RuntimeException("Unknown test case: " + testCase);
+            }
+        });
+        System.out.println("\nPASSED: Tested " + TestLoggerFinder.sequencer.get() + " cases.");
+    }
+
+    public static void test(TestLoggerFinder provider, boolean hasRequiredPermissions) {
+
+        ResourceBundle loggerBundle = ResourceBundle.getBundle(MyLoggerBundle.class.getName());
+        final Map<Logger, String> loggerDescMap = new HashMap<>();
+
+
+        // 1. Test loggers returned by LoggerFinder, both for system callers
+        //    and not system callers.
+        TestLoggerFinder.LoggerImpl appLogger1 = null;
+        try {
+            appLogger1 =
+                TestLoggerFinder.LoggerImpl.class.cast(provider.getLogger("foo", BaseLoggerFinderTest.class));
+            loggerDescMap.put(appLogger1, "provider.getLogger(\"foo\", BaseLoggerFinderTest.class)");
+            if (!hasRequiredPermissions) {
+                throw new RuntimeException("Managed to obtain a logger without permission");
+            }
+        } catch (AccessControlException acx) {
+            if (hasRequiredPermissions) {
+                throw new RuntimeException("Unexpected security exception: ", acx);
+            }
+            if (!acx.getPermission().equals(LOGGERFINDER_PERMISSION)) {
+                throw new RuntimeException("Unexpected permission in exception: " + acx, acx);
+            }
+            System.out.println("Got expected exception for logger: " + acx);
+            final boolean old = allowControl.get().get();
+            allowControl.get().set(true);
+            try {
+                appLogger1 =
+                    TestLoggerFinder.LoggerImpl.class.cast(provider.getLogger("foo", BaseLoggerFinderTest.class));
+                    loggerDescMap.put(appLogger1, "provider.getLogger(\"foo\", BaseLoggerFinderTest.class)");
+            } finally {
+                allowControl.get().set(old);
+            }
+        }
+
+        TestLoggerFinder.LoggerImpl sysLogger1 = null;
+        try {
+            sysLogger1 = TestLoggerFinder.LoggerImpl.class.cast(provider.getLogger("foo", Thread.class));
+            loggerDescMap.put(sysLogger1, "provider.getLogger(\"foo\", Thread.class)");
+            if (!hasRequiredPermissions) {
+                throw new RuntimeException("Managed to obtain a system logger without permission");
+            }
+        } catch (AccessControlException acx) {
+            if (hasRequiredPermissions) {
+                throw new RuntimeException("Unexpected security exception: ", acx);
+            }
+            if (!acx.getPermission().equals(LOGGERFINDER_PERMISSION)) {
+                throw new RuntimeException("Unexpected permission in exception: " + acx, acx);
+            }
+            System.out.println("Got expected exception for system logger: " + acx);
+            final boolean old = allowControl.get().get();
+            allowControl.get().set(true);
+            try {
+                sysLogger1 = TestLoggerFinder.LoggerImpl.class.cast(provider.getLogger("foo", Thread.class));
+                loggerDescMap.put(sysLogger1, "provider.getLogger(\"foo\", Thread.class)");
+            } finally {
+                allowControl.get().set(old);
+            }
+        }
+        if (appLogger1 == sysLogger1) {
+            throw new RuntimeException("identical loggers");
+        }
+
+        if (provider.system.contains(appLogger1)) {
+            throw new RuntimeException("app logger in system map");
+        }
+        if (!provider.user.contains(appLogger1)) {
+            throw new RuntimeException("app logger not in appplication map");
+        }
+        if (provider.user.contains(sysLogger1)) {
+            throw new RuntimeException("sys logger in appplication map");
+        }
+        if (!provider.system.contains(sysLogger1)) {
+            throw new RuntimeException("sys logger not in system map");
+        }
+
+        testLogger(provider, loggerDescMap, "foo", null, appLogger1, appLogger1);
+        testLogger(provider, loggerDescMap, "foo", null, sysLogger1, sysLogger1);
+
+        // 2. Test localized loggers returned LoggerFinder, both for system
+        //   callers and non system callers
+        Logger appLogger2 = null;
+        try {
+            appLogger2 = provider.getLocalizedLogger("foo", loggerBundle, BaseLoggerFinderTest.class);
+            loggerDescMap.put(appLogger2, "provider.getLocalizedLogger(\"foo\", loggerBundle, BaseLoggerFinderTest.class)");
+            if (!hasRequiredPermissions) {
+                throw new RuntimeException("Managed to obtain a logger without permission");
+            }
+        } catch (AccessControlException acx) {
+            if (hasRequiredPermissions) {
+                throw new RuntimeException("Unexpected security exception: ", acx);
+            }
+            if (!acx.getPermission().equals(LOGGERFINDER_PERMISSION)) {
+                throw new RuntimeException("Unexpected permission in exception: " + acx, acx);
+            }
+            System.out.println("Got expected exception for logger: " + acx);
+            final boolean old = allowControl.get().get();
+            allowControl.get().set(true);
+            try {
+                appLogger2 = provider.getLocalizedLogger("foo", loggerBundle, BaseLoggerFinderTest.class);
+                loggerDescMap.put(appLogger2, "provider.getLocalizedLogger(\"foo\", loggerBundle, BaseLoggerFinderTest.class)");
+            } finally {
+                allowControl.get().set(old);
+            }
+        }
+
+        Logger sysLogger2 = null;
+        try {
+            sysLogger2 = provider.getLocalizedLogger("foo", loggerBundle, Thread.class);
+            loggerDescMap.put(sysLogger2, "provider.getLocalizedLogger(\"foo\", loggerBundle, Thread.class)");
+            if (!hasRequiredPermissions) {
+                throw new RuntimeException("Managed to obtain a system logger without permission");
+            }
+        } catch (AccessControlException acx) {
+            if (hasRequiredPermissions) {
+                throw new RuntimeException("Unexpected security exception: ", acx);
+            }
+            if (!acx.getPermission().equals(LOGGERFINDER_PERMISSION)) {
+                throw new RuntimeException("Unexpected permission in exception: " + acx, acx);
+            }
+            System.out.println("Got expected exception for localized system logger: " + acx);
+            final boolean old = allowControl.get().get();
+            allowControl.get().set(true);
+            try {
+                sysLogger2 = provider.getLocalizedLogger("foo", loggerBundle, Thread.class);
+                loggerDescMap.put(sysLogger2, "provider.getLocalizedLogger(\"foo\", loggerBundle, Thread.class))");
+            } finally {
+                allowControl.get().set(old);
+            }
+        }
+        if (appLogger2 == sysLogger2) {
+            throw new RuntimeException("identical loggers");
+        }
+        if (appLogger2 == appLogger1) {
+            throw new RuntimeException("identical loggers");
+        }
+        if (sysLogger2 == sysLogger1) {
+            throw new RuntimeException("identical loggers");
+        }
+
+        if (provider.system.contains(appLogger2)) {
+            throw new RuntimeException("localized app logger in system map");
+        }
+        if (provider.user.contains(appLogger2)) {
+            throw new RuntimeException("localized app logger  in appplication map");
+        }
+        if (provider.user.contains(sysLogger2)) {
+            throw new RuntimeException("localized sys logger in appplication map");
+        }
+        if (provider.system.contains(sysLogger2)) {
+            throw new RuntimeException("localized sys logger not in system map");
+        }
+
+        testLogger(provider, loggerDescMap, "foo", loggerBundle, appLogger2, appLogger1);
+        testLogger(provider, loggerDescMap, "foo", loggerBundle, sysLogger2, sysLogger1);
+
+        // 3 Test loggers returned by:
+        //   3.1: System.getLogger("foo")
+        Logger appLogger3 = System.getLogger("foo");
+        loggerDescMap.put(appLogger3, "System.getLogger(\"foo\")");
+        testLogger(provider, loggerDescMap, "foo", null, appLogger3, appLogger1);
+
+        //   3.2: System.getLogger("foo")
+        //        Emulate what System.getLogger() does when the caller is a
+        //        platform classes
+        Logger sysLogger3 = accessSystemLogger.getLogger("foo");
+        loggerDescMap.put(sysLogger3, "AccessSystemLogger.getLogger(\"foo\")");
+
+        if (appLogger3 == sysLogger3) {
+            throw new RuntimeException("identical loggers");
+        }
+
+        testLogger(provider, loggerDescMap, "foo", null, sysLogger3, sysLogger1);
+
+        // 4. Test loggers returned by:
+        //    4.1 System.getLogger("foo", loggerBundle)
+        Logger appLogger4 =
+                System.getLogger("foo", loggerBundle);
+        loggerDescMap.put(appLogger4, "System.getLogger(\"foo\", loggerBundle)");
+        if (appLogger4 == appLogger1) {
+            throw new RuntimeException("identical loggers");
+        }
+
+        testLogger(provider, loggerDescMap, "foo", loggerBundle, appLogger4, appLogger1);
+
+        //   4.2: System.getLogger("foo", loggerBundle)
+        //        Emulate what System.getLogger() does when the caller is a
+        //        platform classes
+        Logger sysLogger4 = accessSystemLogger.getLogger("foo", loggerBundle);
+        loggerDescMap.put(sysLogger4, "AccessSystemLogger.getLogger(\"foo\", loggerBundle)");
+        if (appLogger4 == sysLogger4) {
+            throw new RuntimeException("identical loggers");
+        }
+
+        testLogger(provider, loggerDescMap, "foo", loggerBundle, sysLogger4, sysLogger1);
+
+    }
+
+    public static class Foo {
+
+    }
+
+    static void verbose(String msg) {
+       if (VERBOSE) {
+           System.out.println(msg);
+       }
+    }
+
+    // Calls the 8 methods defined on Logger and verify the
+    // parameters received by the underlying TestProvider.LoggerImpl
+    // logger.
+    private static void testLogger(TestLoggerFinder provider,
+            Map<Logger, String> loggerDescMap,
+            String name,
+            ResourceBundle loggerBundle,
+            Logger logger,
+            TestLoggerFinder.LoggerImpl sink) {
+
+        System.out.println("Testing " + loggerDescMap.get(logger) + " [" + logger +"]");
+        AtomicLong sequencer = TestLoggerFinder.sequencer;
+
+        Foo foo = new Foo();
+        String fooMsg = foo.toString();
+        for (Level loggerLevel : Level.values()) {
+            sink.level = loggerLevel;
+            for (Level messageLevel : Level.values()) {
+                String desc = "logger.log(messageLevel, foo): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                TestLoggerFinder.LogEvent expected =
+                        TestLoggerFinder.LogEvent.of(
+                            sequencer.get(),
+                            messageLevel.compareTo(loggerLevel) >= 0,
+                            name, messageLevel, (ResourceBundle)null,
+                            fooMsg, null, (Throwable)null, (Object[])null);
+                logger.log(messageLevel, foo);
+                if (loggerLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
+                    if (provider.eventQueue.poll() != null) {
+                        throw new RuntimeException("unexpected event in queue for " + desc);
+                    }
+                } else {
+                    TestLoggerFinder.LogEvent actual =  provider.eventQueue.poll();
+                    if (!expected.equals(actual)) {
+                        throw new RuntimeException("mismatch for " + desc
+                                + "\n\texpected=" + expected
+                                + "\n\t  actual=" + actual);
+                    } else {
+                        verbose("Got expected results for "
+                                + desc + "\n\t" + expected);
+                    }
+                }
+            }
+        }
+
+        String msg = "blah";
+        for (Level loggerLevel : Level.values()) {
+            sink.level = loggerLevel;
+            for (Level messageLevel : Level.values()) {
+                String desc = "logger.log(messageLevel, \"blah\"): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                TestLoggerFinder.LogEvent expected =
+                        TestLoggerFinder.LogEvent.of(
+                            sequencer.get(),
+                            messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF,
+                            name, messageLevel, loggerBundle,
+                            msg, null, (Throwable)null, (Object[])null);
+                logger.log(messageLevel, msg);
+                TestLoggerFinder.LogEvent actual =  provider.eventQueue.poll();
+                if (!expected.equals(actual)) {
+                    throw new RuntimeException("mismatch for " + desc
+                            + "\n\texpected=" + expected
+                            + "\n\t  actual=" + actual);
+                } else {
+                    verbose("Got expected results for "
+                            + desc + "\n\t" + expected);
+                }
+            }
+        }
+
+        Supplier<String> fooSupplier = new Supplier<String>() {
+            @Override
+            public String get() {
+                return this.toString();
+            }
+        };
+
+        for (Level loggerLevel : Level.values()) {
+            sink.level = loggerLevel;
+            for (Level messageLevel : Level.values()) {
+                String desc = "logger.log(messageLevel, fooSupplier): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                TestLoggerFinder.LogEvent expected =
+                        TestLoggerFinder.LogEvent.of(
+                            sequencer.get(),
+                            messageLevel.compareTo(loggerLevel) >= 0,
+                            name, messageLevel, (ResourceBundle)null,
+                            fooSupplier.get(), null,
+                            (Throwable)null, (Object[])null);
+                logger.log(messageLevel, fooSupplier);
+                if (loggerLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
+                    if (provider.eventQueue.poll() != null) {
+                        throw new RuntimeException("unexpected event in queue for " + desc);
+                    }
+                } else {
+                    TestLoggerFinder.LogEvent actual =  provider.eventQueue.poll();
+                    if (!expected.equals(actual)) {
+                        throw new RuntimeException("mismatch for " + desc
+                                + "\n\texpected=" + expected
+                                + "\n\t  actual=" + actual);
+                    } else {
+                        verbose("Got expected results for "
+                                + desc + "\n\t" + expected);
+                    }
+                }
+            }
+        }
+
+        String format = "two params [{1} {2}]";
+        Object arg1 = foo;
+        Object arg2 = msg;
+        for (Level loggerLevel : Level.values()) {
+            sink.level = loggerLevel;
+            for (Level messageLevel : Level.values()) {
+                String desc = "logger.log(messageLevel, format, params...): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                TestLoggerFinder.LogEvent expected =
+                        TestLoggerFinder.LogEvent.of(
+                            sequencer.get(),
+                            messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF,
+                            name, messageLevel, loggerBundle,
+                            format, null, (Throwable)null, new Object[] {foo, msg});
+                logger.log(messageLevel, format, foo, msg);
+                TestLoggerFinder.LogEvent actual =  provider.eventQueue.poll();
+                if (!expected.equals(actual)) {
+                    throw new RuntimeException("mismatch for " + desc
+                            + "\n\texpected=" + expected
+                            + "\n\t  actual=" + actual);
+                } else {
+                    verbose("Got expected results for "
+                            + desc + "\n\t" + expected);
+                }
+            }
+        }
+
+        Throwable thrown = new Exception("OK: log me!");
+        for (Level loggerLevel : Level.values()) {
+            sink.level = loggerLevel;
+            for (Level messageLevel : Level.values()) {
+                String desc = "logger.log(messageLevel, \"blah\", thrown): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                TestLoggerFinder.LogEvent expected =
+                        TestLoggerFinder.LogEvent.of(
+                            sequencer.get(),
+                            messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF,
+                            name, messageLevel, loggerBundle,
+                            msg, null, thrown, (Object[]) null);
+                logger.log(messageLevel, msg, thrown);
+                TestLoggerFinder.LogEvent actual =  provider.eventQueue.poll();
+                if (!expected.equals(actual)) {
+                    throw new RuntimeException("mismatch for " + desc
+                            + "\n\texpected=" + expected
+                            + "\n\t  actual=" + actual);
+                } else {
+                    verbose("Got expected results for "
+                            + desc + "\n\t" + expected);
+                }
+            }
+        }
+
+
+        for (Level loggerLevel : Level.values()) {
+            sink.level = loggerLevel;
+            for (Level messageLevel : Level.values()) {
+                String desc = "logger.log(messageLevel, thrown, fooSupplier): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                TestLoggerFinder.LogEvent expected =
+                        TestLoggerFinder.LogEvent.of(
+                            sequencer.get(),
+                            messageLevel.compareTo(loggerLevel) >= 0,
+                            name, messageLevel, (ResourceBundle)null,
+                            fooSupplier.get(), null,
+                            (Throwable)thrown, (Object[])null);
+                logger.log(messageLevel, fooSupplier, thrown);
+                if (loggerLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
+                    if (provider.eventQueue.poll() != null) {
+                        throw new RuntimeException("unexpected event in queue for " + desc);
+                    }
+                } else {
+                    TestLoggerFinder.LogEvent actual =  provider.eventQueue.poll();
+                    if (!expected.equals(actual)) {
+                        throw new RuntimeException("mismatch for " + desc
+                                + "\n\texpected=" + expected
+                                + "\n\t  actual=" + actual);
+                    } else {
+                        verbose("Got expected results for "
+                                + desc + "\n\t" + expected);
+                    }
+                }
+            }
+        }
+
+        ResourceBundle bundle = ResourceBundle.getBundle(MyBundle.class.getName());
+        for (Level loggerLevel : Level.values()) {
+            sink.level = loggerLevel;
+            for (Level messageLevel : Level.values()) {
+                String desc = "logger.log(messageLevel, bundle, format, params...): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                TestLoggerFinder.LogEvent expected =
+                        TestLoggerFinder.LogEvent.of(
+                            sequencer.get(),
+                            messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF,
+                            name, messageLevel, bundle,
+                            format, null, (Throwable)null, new Object[] {foo, msg});
+                logger.log(messageLevel, bundle, format, foo, msg);
+                TestLoggerFinder.LogEvent actual =  provider.eventQueue.poll();
+                if (!expected.equals(actual)) {
+                    throw new RuntimeException("mismatch for " + desc
+                            + "\n\texpected=" + expected
+                            + "\n\t  actual=" + actual);
+                } else {
+                    verbose("Got expected results for "
+                            + desc + "\n\t" + expected);
+                }
+            }
+        }
+
+        for (Level loggerLevel : Level.values()) {
+            sink.level = loggerLevel;
+            for (Level messageLevel : Level.values()) {
+                String desc = "logger.log(messageLevel, bundle, \"blah\", thrown): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                TestLoggerFinder.LogEvent expected =
+                        TestLoggerFinder.LogEvent.of(
+                            sequencer.get(),
+                            messageLevel.compareTo(loggerLevel) >= 0 && loggerLevel != Level.OFF,
+                            name, messageLevel, bundle,
+                            msg, null, thrown, (Object[]) null);
+                logger.log(messageLevel, bundle, msg, thrown);
+                TestLoggerFinder.LogEvent actual =  provider.eventQueue.poll();
+                if (!expected.equals(actual)) {
+                    throw new RuntimeException("mismatch for " + desc
+                            + "\n\texpected=" + expected
+                            + "\n\t  actual=" + actual);
+                } else {
+                    verbose("Got expected results for "
+                            + desc + "\n\t" + expected);
+                }
+            }
+        }
+    }
+
+    final static class PermissionsBuilder {
+        final Permissions perms;
+        public PermissionsBuilder() {
+            this(new Permissions());
+        }
+        public PermissionsBuilder(Permissions perms) {
+            this.perms = perms;
+        }
+        public PermissionsBuilder add(Permission p) {
+            perms.add(p);
+            return this;
+        }
+        public PermissionsBuilder addAll(PermissionCollection col) {
+            if (col != null) {
+                for (Enumeration<Permission> e = col.elements(); e.hasMoreElements(); ) {
+                    perms.add(e.nextElement());
+                }
+            }
+            return this;
+        }
+        public Permissions toPermissions() {
+            final PermissionsBuilder builder = new PermissionsBuilder();
+            builder.addAll(perms);
+            return builder.perms;
+        }
+    }
+
+    public static class SimplePolicy extends Policy {
+        final static RuntimePermission CONTROL = LOGGERFINDER_PERMISSION;
+        final static RuntimePermission ACCESS = new RuntimePermission("accessClassInPackage.jdk.internal.logger");
+
+        final Permissions permissions;
+        final ThreadLocal<AtomicBoolean> allowControl;
+        final ThreadLocal<AtomicBoolean> allowAccess;
+        public SimplePolicy(ThreadLocal<AtomicBoolean> allowControl, ThreadLocal<AtomicBoolean> allowAccess) {
+            this.allowControl = allowControl;
+            this.allowAccess = allowAccess;
+            permissions = new Permissions();
+        }
+
+        Permissions getPermissions() {
+            if (allowControl.get().get() || allowAccess.get().get()) {
+                PermissionsBuilder builder =  new PermissionsBuilder()
+                        .addAll(permissions);
+                if (allowControl.get().get()) {
+                    builder.add(CONTROL);
+                }
+                if (allowAccess.get().get()) {
+                    builder.add(ACCESS);
+                }
+                return builder.toPermissions();
+            }
+            return permissions;
+        }
+
+        @Override
+        public boolean implies(ProtectionDomain domain, Permission permission) {
+            return getPermissions().implies(permission);
+        }
+
+        @Override
+        public PermissionCollection getPermissions(CodeSource codesource) {
+            return new PermissionsBuilder().addAll(getPermissions()).toPermissions();
+        }
+
+        @Override
+        public PermissionCollection getPermissions(ProtectionDomain domain) {
+            return new PermissionsBuilder().addAll(getPermissions()).toPermissions();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/LoggerFinder/BaseLoggerFinderTest/CustomSystemClassLoader.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.nio.file.Files;
+import java.security.AllPermission;
+import java.security.Permissions;
+import java.security.ProtectionDomain;
+
+
+/**
+ * A custom ClassLoader to load the concrete LoggerFinder class
+ * with all permissions.
+ *
+ * @author danielfuchs
+ */
+public class CustomSystemClassLoader extends ClassLoader {
+
+
+    Class<?> finderClass = null;
+
+    public CustomSystemClassLoader() {
+        super();
+    }
+    public CustomSystemClassLoader(ClassLoader parent) {
+        super(parent);
+    }
+
+    private Class<?> defineFinderClass(String name)
+        throws ClassNotFoundException {
+        final Object obj = getClassLoadingLock(name);
+        synchronized(obj) {
+            if (finderClass != null) return finderClass;
+
+            URL url = this.getClass().getProtectionDomain().getCodeSource().getLocation();
+            File file = new File(url.getPath(), name+".class");
+            if (file.canRead()) {
+                try {
+                    byte[] b = Files.readAllBytes(file.toPath());
+                    Permissions perms = new Permissions();
+                    perms.add(new AllPermission());
+                    finderClass = defineClass(
+                            name, b, 0, b.length, new ProtectionDomain(
+                            this.getClass().getProtectionDomain().getCodeSource(),
+                            perms));
+                    System.out.println("Loaded " + name);
+                    return finderClass;
+                } catch (Throwable ex) {
+                    ex.printStackTrace();
+                    throw new ClassNotFoundException(name, ex);
+                }
+            } else {
+                throw new ClassNotFoundException(name,
+                        new IOException(file.toPath() + ": can't read"));
+            }
+        }
+    }
+
+    @Override
+    public synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
+        if (name.equals("BaseLoggerFinder")) {
+            Class<?> c = defineFinderClass(name);
+            if (resolve) {
+                resolveClass(c);
+            }
+            return c;
+        }
+        return super.loadClass(name, resolve);
+    }
+
+    @Override
+    protected Class<?> findClass(String name) throws ClassNotFoundException {
+        if (name.equals("BaseLoggerFinder")) {
+            return defineFinderClass(name);
+        }
+        return super.findClass(name);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/LoggerFinder/BaseLoggerFinderTest/META-INF/services/java.lang.System$LoggerFinder	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,1 @@
+BaseLoggerFinder
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/LoggerFinder/BaseLoggerFinderTest/TestLoggerFinder.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,181 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.util.Arrays;
+import java.util.Objects;
+import java.util.Queue;
+import java.util.ResourceBundle;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.Supplier;
+import java.lang.System.Logger;
+
+/**
+ * What our test provider needs to implement.
+ * @author danielfuchs
+ */
+public interface TestLoggerFinder {
+    public final static AtomicLong sequencer = new AtomicLong();
+    public final ConcurrentHashMap<String, LoggerImpl> system = new ConcurrentHashMap<>();
+    public final ConcurrentHashMap<String, LoggerImpl> user = new ConcurrentHashMap<>();
+    public final Queue<LogEvent> eventQueue = new ArrayBlockingQueue<>(128);
+
+    public static final class LogEvent {
+
+        public LogEvent() {
+            this(sequencer.getAndIncrement());
+        }
+
+        LogEvent(long sequenceNumber) {
+            this.sequenceNumber = sequenceNumber;
+        }
+
+        long sequenceNumber;
+        boolean isLoggable;
+        String loggerName;
+        Logger.Level level;
+        ResourceBundle bundle;
+        Throwable thrown;
+        Object[] args;
+        Supplier<String> supplier;
+        String msg;
+
+        Object[] toArray() {
+            return new Object[] {
+                sequenceNumber,
+                isLoggable,
+                loggerName,
+                level,
+                bundle,
+                thrown,
+                args,
+                supplier,
+                msg,
+            };
+        }
+
+        @Override
+        public String toString() {
+            return Arrays.deepToString(toArray());
+        }
+
+
+
+        @Override
+        public boolean equals(Object obj) {
+            return obj instanceof LogEvent
+                    && Objects.deepEquals(this.toArray(), ((LogEvent)obj).toArray());
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(toArray());
+        }
+
+
+        public static LogEvent of(boolean isLoggable, String name,
+                Logger.Level level, ResourceBundle bundle,
+                String key, Throwable thrown) {
+            LogEvent evt = new LogEvent();
+            evt.isLoggable = isLoggable;
+            evt.loggerName = name;
+            evt.level = level;
+            evt.args = null;
+            evt.bundle = bundle;
+            evt.thrown = thrown;
+            evt.supplier = null;
+            evt.msg = key;
+            return evt;
+        }
+
+        public static LogEvent of(boolean isLoggable, String name,
+                Logger.Level level, ResourceBundle bundle,
+                String key, Object... params) {
+            LogEvent evt = new LogEvent();
+            evt.isLoggable = isLoggable;
+            evt.loggerName = name;
+            evt.level = level;
+            evt.args = params;
+            evt.bundle = bundle;
+            evt.thrown = null;
+            evt.supplier = null;
+            evt.msg = key;
+            return evt;
+        }
+
+        public static LogEvent of(long sequenceNumber,
+                boolean isLoggable, String name,
+                Logger.Level level, ResourceBundle bundle,
+                String key, Supplier<String> supplier,
+                Throwable thrown, Object... params) {
+            LogEvent evt = new LogEvent(sequenceNumber);
+            evt.loggerName = name;
+            evt.level = level;
+            evt.args = params;
+            evt.bundle = bundle;
+            evt.thrown = thrown;
+            evt.supplier = supplier;
+            evt.msg = key;
+            evt.isLoggable = isLoggable;
+            return evt;
+        }
+
+    }
+
+    public class LoggerImpl implements Logger {
+        final String name;
+        Logger.Level level = Logger.Level.INFO;
+
+        public LoggerImpl(String name) {
+            this.name = name;
+        }
+
+        @Override
+        public String getName() {
+            return name;
+        }
+
+        @Override
+        public boolean isLoggable(Logger.Level level) {
+            return this.level != Logger.Level.OFF && this.level.getSeverity() <= level.getSeverity();
+        }
+
+        @Override
+        public void log(Logger.Level level, ResourceBundle bundle, String key, Throwable thrown) {
+            log(LogEvent.of(isLoggable(level), this.name, level, bundle, key, thrown));
+        }
+
+        @Override
+        public void log(Logger.Level level, ResourceBundle bundle, String format, Object... params) {
+            log(LogEvent.of(isLoggable(level), name, level, bundle, format, params));
+        }
+
+        void log(LogEvent event) {
+            eventQueue.add(event);
+        }
+    }
+
+    public Logger getLogger(String name, Class<?> caller);
+    public Logger getLocalizedLogger(String name, ResourceBundle bundle, Class<?> caller);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/LoggerFinder/DefaultLoggerFinderTest/AccessSystemLogger.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+import java.io.IOException;
+import java.lang.System.Logger;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+import java.util.ResourceBundle;
+
+/**
+ *
+ * @author danielfuchs
+ */
+public final class AccessSystemLogger {
+
+    public AccessSystemLogger() {
+        this(check());
+    }
+
+    private AccessSystemLogger(Void unused) {
+    }
+
+    private static Void check() {
+        if (AccessSystemLogger.class.getClassLoader() != null) {
+            throw new RuntimeException("AccessSystemLogger should be loaded by the null classloader");
+        }
+        return null;
+    }
+
+    public Logger getLogger(String name) {
+        Logger logger = System.getLogger(name);
+        System.out.println("System.getLogger(\"" + name + "\"): " + logger);
+        return logger;
+    }
+
+    public Logger getLogger(String name, ResourceBundle bundle) {
+        Logger logger = System.getLogger(name, bundle);
+        System.out.println("System.getLogger(\"" + name + "\", bundle): " + logger);
+        return logger;
+    }
+
+    public java.util.logging.Logger demandSystemLogger(String name) {
+        return java.util.logging.Logger.getLogger(name);
+    }
+
+    // copy AccessSystemLogger.class to ./boot
+    public static void main(String[] args) throws IOException {
+        Path testDir = Paths.get(System.getProperty("user.dir", "."));
+        Path bootDir = Paths.get(testDir.toString(), "boot");
+        Path classes = Paths.get(System.getProperty("test.classes", "build/classes"));
+        Path thisClass = Paths.get(classes.toString(),
+                AccessSystemLogger.class.getSimpleName()+".class");
+        if (Files.notExists(bootDir)) {
+            Files.createDirectory(bootDir);
+        }
+        Path dest = Paths.get(bootDir.toString(),
+                AccessSystemLogger.class.getSimpleName()+".class");
+        Files.copy(thisClass, dest, StandardCopyOption.REPLACE_EXISTING);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/LoggerFinder/DefaultLoggerFinderTest/DefaultLoggerFinderTest.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,887 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+import java.security.AccessControlException;
+import java.security.CodeSource;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.Permissions;
+import java.security.Policy;
+import java.security.ProtectionDomain;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Queue;
+import java.util.ResourceBundle;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.Supplier;
+import java.util.logging.Handler;
+import java.util.logging.LogRecord;
+import java.lang.System.LoggerFinder;
+import java.lang.System.Logger;
+import java.lang.System.Logger.Level;
+import java.util.stream.Stream;
+
+/**
+ * @test
+ * @bug     8140364
+ * @summary Tests the default implementation of System.Logger, when
+ *          JUL is the default backend.
+ * @build AccessSystemLogger DefaultLoggerFinderTest
+ * @run  driver AccessSystemLogger
+ * @run  main/othervm -Xbootclasspath/a:boot DefaultLoggerFinderTest NOSECURITY
+ * @run  main/othervm -Xbootclasspath/a:boot DefaultLoggerFinderTest NOPERMISSIONS
+ * @run  main/othervm -Xbootclasspath/a:boot DefaultLoggerFinderTest WITHPERMISSIONS
+ * @author danielfuchs
+ */
+public class DefaultLoggerFinderTest {
+
+    static final RuntimePermission LOGGERFINDER_PERMISSION =
+                new RuntimePermission("loggerFinder");
+    final static AtomicLong sequencer = new AtomicLong();
+    final static boolean VERBOSE = false;
+    static final ThreadLocal<AtomicBoolean> allowControl = new ThreadLocal<AtomicBoolean>() {
+        @Override
+        protected AtomicBoolean initialValue() {
+            return  new AtomicBoolean(false);
+        }
+    };
+    static final ThreadLocal<AtomicBoolean> allowAll = new ThreadLocal<AtomicBoolean>() {
+        @Override
+        protected AtomicBoolean initialValue() {
+            return  new AtomicBoolean(false);
+        }
+    };
+
+    static final AccessSystemLogger accessSystemLogger = new AccessSystemLogger();
+
+    public static final Queue<LogEvent> eventQueue = new ArrayBlockingQueue<>(128);
+
+    public static final class LogEvent {
+
+        public LogEvent() {
+            this(sequencer.getAndIncrement());
+        }
+
+        LogEvent(long sequenceNumber) {
+            this.sequenceNumber = sequenceNumber;
+        }
+
+        long sequenceNumber;
+        boolean isLoggable;
+        String loggerName;
+        java.util.logging.Level level;
+        ResourceBundle bundle;
+        Throwable thrown;
+        Object[] args;
+        String msg;
+        String className;
+        String methodName;
+
+        Object[] toArray() {
+            return new Object[] {
+                sequenceNumber,
+                isLoggable,
+                loggerName,
+                level,
+                bundle,
+                thrown,
+                args,
+                msg,
+                className,
+                methodName,
+            };
+        }
+
+        @Override
+        public String toString() {
+            return Arrays.deepToString(toArray());
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            return obj instanceof LogEvent
+                    && Objects.deepEquals(this.toArray(), ((LogEvent)obj).toArray());
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(toArray());
+        }
+        public static LogEvent of(long sequenceNumber,
+                boolean isLoggable, String name,
+                java.util.logging.Level level, ResourceBundle bundle,
+                String key, Throwable thrown, Object... params) {
+            return LogEvent.of(sequenceNumber, isLoggable, name,
+                    DefaultLoggerFinderTest.class.getName(),
+                    "testLogger", level, bundle, key,
+                    thrown, params);
+        }
+        public static LogEvent of(long sequenceNumber,
+                boolean isLoggable, String name,
+                String className, String methodName,
+                java.util.logging.Level level, ResourceBundle bundle,
+                String key, Throwable thrown, Object... params) {
+            LogEvent evt = new LogEvent(sequenceNumber);
+            evt.loggerName = name;
+            evt.level = level;
+            evt.args = params;
+            evt.bundle = bundle;
+            evt.thrown = thrown;
+            evt.msg = key;
+            evt.isLoggable = isLoggable;
+            evt.className = className;
+            evt.methodName = methodName;
+            return evt;
+        }
+
+    }
+
+    static java.util.logging.Level mapToJul(Level level) {
+        switch (level) {
+            case ALL: return java.util.logging.Level.ALL;
+            case TRACE: return java.util.logging.Level.FINER;
+            case DEBUG: return java.util.logging.Level.FINE;
+            case INFO: return java.util.logging.Level.INFO;
+            case WARNING: return java.util.logging.Level.WARNING;
+            case ERROR: return java.util.logging.Level.SEVERE;
+            case OFF: return java.util.logging.Level.OFF;
+        }
+        throw new InternalError("No such level: " + level);
+    }
+
+    static final java.util.logging.Level[] julLevels = {
+        java.util.logging.Level.ALL,
+        new java.util.logging.Level("FINER_THAN_FINEST", java.util.logging.Level.FINEST.intValue() - 10) {},
+        java.util.logging.Level.FINEST,
+        new java.util.logging.Level("FINER_THAN_FINER", java.util.logging.Level.FINER.intValue() - 10) {},
+        java.util.logging.Level.FINER,
+        new java.util.logging.Level("FINER_THAN_FINE", java.util.logging.Level.FINE.intValue() - 10) {},
+        java.util.logging.Level.FINE,
+        new java.util.logging.Level("FINER_THAN_CONFIG", java.util.logging.Level.FINE.intValue() + 10) {},
+        java.util.logging.Level.CONFIG,
+        new java.util.logging.Level("FINER_THAN_INFO", java.util.logging.Level.INFO.intValue() - 10) {},
+        java.util.logging.Level.INFO,
+        new java.util.logging.Level("FINER_THAN_WARNING", java.util.logging.Level.INFO.intValue() + 10) {},
+        java.util.logging.Level.WARNING,
+        new java.util.logging.Level("FINER_THAN_SEVERE", java.util.logging.Level.SEVERE.intValue() - 10) {},
+        java.util.logging.Level.SEVERE,
+        new java.util.logging.Level("FATAL", java.util.logging.Level.SEVERE.intValue() + 10) {},
+        java.util.logging.Level.OFF,
+    };
+
+    static final Level[] mappedLevels = {
+        Level.ALL,     // ALL
+        Level.DEBUG,   // FINER_THAN_FINEST
+        Level.DEBUG,   // FINEST
+        Level.DEBUG,   // FINER_THAN_FINER
+        Level.TRACE,   // FINER
+        Level.TRACE,   // FINER_THAN_FINE
+        Level.DEBUG,   // FINE
+        Level.DEBUG,   // FINER_THAN_CONFIG
+        Level.DEBUG,   // CONFIG
+        Level.DEBUG,   // FINER_THAN_INFO
+        Level.INFO,    // INFO
+        Level.INFO,    // FINER_THAN_WARNING
+        Level.WARNING, // WARNING
+        Level.WARNING, // FINER_THAN_SEVERE
+        Level.ERROR,   // SEVERE
+        Level.ERROR,   // FATAL
+        Level.OFF,     // OFF
+    };
+
+    final static Map<java.util.logging.Level, Level> julToSpiMap;
+    static {
+        Map<java.util.logging.Level, Level> map = new HashMap<>();
+        if (mappedLevels.length != julLevels.length) {
+            throw new ExceptionInInitializerError("Array lengths differ"
+                + "\n\tjulLevels=" + Arrays.deepToString(julLevels)
+                + "\n\tmappedLevels=" + Arrays.deepToString(mappedLevels));
+        }
+        for (int i=0; i<julLevels.length; i++) {
+            map.put(julLevels[i], mappedLevels[i]);
+        }
+        julToSpiMap = Collections.unmodifiableMap(map);
+    }
+
+    public static class MyBundle extends ResourceBundle {
+
+        final ConcurrentHashMap<String,String> map = new ConcurrentHashMap<>();
+
+        @Override
+        protected Object handleGetObject(String key) {
+            if (key.contains(" (translated)")) {
+                throw new RuntimeException("Unexpected key: " + key);
+            }
+            return map.computeIfAbsent(key, k -> k + " (translated)");
+        }
+
+        @Override
+        public Enumeration<String> getKeys() {
+            return Collections.enumeration(map.keySet());
+        }
+
+    }
+
+    public static class MyHandler extends Handler {
+
+        @Override
+        public java.util.logging.Level getLevel() {
+            return java.util.logging.Level.ALL;
+        }
+
+        @Override
+        public void publish(LogRecord record) {
+            eventQueue.add(LogEvent.of(sequencer.getAndIncrement(),
+                    true, record.getLoggerName(),
+                    record.getSourceClassName(),
+                    record.getSourceMethodName(),
+                    record.getLevel(),
+                    record.getResourceBundle(), record.getMessage(),
+                    record.getThrown(), record.getParameters()));
+        }
+        @Override
+        public void flush() {
+        }
+        @Override
+        public void close() throws SecurityException {
+        }
+
+    }
+
+    public static class MyLoggerBundle extends MyBundle {
+
+    }
+
+
+    static enum TestCases {NOSECURITY, NOPERMISSIONS, WITHPERMISSIONS};
+
+    static void setSecurityManager() {
+        if (System.getSecurityManager() == null) {
+            Policy.setPolicy(new SimplePolicy(allowAll, allowControl));
+            System.setSecurityManager(new SecurityManager());
+        }
+    }
+
+    public static void main(String[] args) {
+        if (args.length == 0)
+            args = new String[] {
+                "NOSECURITY",
+                "NOPERMISSIONS",
+                "WITHPERMISSIONS"
+            };
+
+        final java.util.logging.Logger appSink = java.util.logging.Logger.getLogger("foo");
+        final java.util.logging.Logger sysSink = accessSystemLogger.demandSystemLogger("foo");
+        appSink.addHandler(new MyHandler());
+        sysSink.addHandler(new MyHandler());
+        appSink.setUseParentHandlers(VERBOSE);
+        sysSink.setUseParentHandlers(VERBOSE);
+
+        Stream.of(args).map(TestCases::valueOf).forEach((testCase) -> {
+            LoggerFinder provider;
+            switch (testCase) {
+                case NOSECURITY:
+                    System.out.println("\n*** Without Security Manager\n");
+                    provider = LoggerFinder.getLoggerFinder();
+                    test(provider, true, appSink, sysSink);
+                    System.out.println("Tetscase count: " + sequencer.get());
+                    break;
+                case NOPERMISSIONS:
+                    System.out.println("\n*** With Security Manager, without permissions\n");
+                    setSecurityManager();
+                    try {
+                        provider = LoggerFinder.getLoggerFinder();
+                        throw new RuntimeException("Expected exception not raised");
+                    } catch (AccessControlException x) {
+                        if (!LOGGERFINDER_PERMISSION.equals(x.getPermission())) {
+                            throw new RuntimeException("Unexpected permission check", x);
+                        }
+                        final boolean control = allowControl.get().get();
+                        try {
+                            allowControl.get().set(true);
+                            provider = LoggerFinder.getLoggerFinder();
+                        } finally {
+                            allowControl.get().set(control);
+                        }
+                    }
+                    test(provider, false, appSink, sysSink);
+                    System.out.println("Tetscase count: " + sequencer.get());
+                    break;
+                case WITHPERMISSIONS:
+                    System.out.println("\n*** With Security Manager, with control permission\n");
+                    setSecurityManager();
+                    final boolean control = allowControl.get().get();
+                    try {
+                        allowControl.get().set(true);
+                        provider = LoggerFinder.getLoggerFinder();
+                        test(provider, true, appSink, sysSink);
+                    } finally {
+                        allowControl.get().set(control);
+                    }
+                    break;
+                default:
+                    throw new RuntimeException("Unknown test case: " + testCase);
+            }
+        });
+        System.out.println("\nPASSED: Tested " + sequencer.get() + " cases.");
+    }
+
+    public static void test(LoggerFinder provider,
+            boolean hasRequiredPermissions,
+            java.util.logging.Logger appSink,
+            java.util.logging.Logger sysSink) {
+
+        ResourceBundle loggerBundle = ResourceBundle.getBundle(MyLoggerBundle.class.getName());
+        final Map<Logger, String> loggerDescMap = new HashMap<>();
+
+
+        Logger appLogger1 = null;
+        try {
+            appLogger1 = provider.getLogger("foo", DefaultLoggerFinderTest.class);
+            loggerDescMap.put(appLogger1, "provider.getApplicationLogger(\"foo\")");
+            if (!hasRequiredPermissions) {
+                throw new RuntimeException("Managed to obtain a logger without permission");
+            }
+        } catch (AccessControlException acx) {
+            if (hasRequiredPermissions) {
+                throw new RuntimeException("Unexpected security exception: ", acx);
+            }
+            if (!acx.getPermission().equals(LOGGERFINDER_PERMISSION)) {
+                throw new RuntimeException("Unexpected permission in exception: " + acx, acx);
+            }
+            System.out.println("Got expected exception for logger: " + acx);
+            boolean old = allowControl.get().get();
+            allowControl.get().set(true);
+            try {
+                appLogger1 =provider.getLogger("foo", DefaultLoggerFinderTest.class);
+                loggerDescMap.put(appLogger1, "provider.getApplicationLogger(\"foo\")");
+            } finally {
+                allowControl.get().set(old);
+            }
+        }
+
+        Logger sysLogger1 = null;
+        try {
+            sysLogger1 = provider.getLogger("foo", Thread.class);
+            loggerDescMap.put(sysLogger1, "provider.getSystemLogger(\"foo\")");
+            if (!hasRequiredPermissions) {
+                throw new RuntimeException("Managed to obtain a system logger without permission");
+            }
+        } catch (AccessControlException acx) {
+            if (hasRequiredPermissions) {
+                throw new RuntimeException("Unexpected security exception: ", acx);
+            }
+            if (!acx.getPermission().equals(LOGGERFINDER_PERMISSION)) {
+                throw new RuntimeException("Unexpected permission in exception: " + acx, acx);
+            }
+            System.out.println("Got expected exception for system logger: " + acx);
+            boolean old = allowControl.get().get();
+            allowControl.get().set(true);
+            try {
+                sysLogger1 = provider.getLogger("foo", Thread.class);
+                loggerDescMap.put(sysLogger1, "provider.getSystemLogger(\"foo\")");
+            } finally {
+                allowControl.get().set(old);
+            }
+        }
+        if (appLogger1 == sysLogger1) {
+            throw new RuntimeException("identical loggers");
+        }
+
+        Logger appLogger2 = null;
+        try {
+            appLogger2 = provider.getLocalizedLogger("foo", loggerBundle, DefaultLoggerFinderTest.class);
+            loggerDescMap.put(appLogger2, "provider.getLocalizedApplicationLogger(\"foo\", loggerBundle)");
+            if (!hasRequiredPermissions) {
+                throw new RuntimeException("Managed to obtain a logger without permission");
+            }
+        } catch (AccessControlException acx) {
+            if (hasRequiredPermissions) {
+                throw new RuntimeException("Unexpected security exception: ", acx);
+            }
+            if (!acx.getPermission().equals(LOGGERFINDER_PERMISSION)) {
+                throw new RuntimeException("Unexpected permission in exception: " + acx, acx);
+            }
+            System.out.println("Got expected exception for logger: " + acx);
+            boolean old = allowControl.get().get();
+            allowControl.get().set(true);
+            try {
+                appLogger2 = provider.getLocalizedLogger("foo", loggerBundle, DefaultLoggerFinderTest.class);
+                loggerDescMap.put(appLogger2, "provider.getLocalizedApplicationLogger(\"foo\", loggerBundle)");
+            } finally {
+                allowControl.get().set(old);
+            }
+        }
+
+        Logger sysLogger2 = null;
+        try {
+            sysLogger2 = provider.getLocalizedLogger("foo", loggerBundle, Thread.class);
+            loggerDescMap.put(sysLogger2, "provider.getLocalizedSystemLogger(\"foo\", loggerBundle)");
+            if (!hasRequiredPermissions) {
+                throw new RuntimeException("Managed to obtain a system logger without permission");
+            }
+        } catch (AccessControlException acx) {
+            if (hasRequiredPermissions) {
+                throw new RuntimeException("Unexpected security exception: ", acx);
+            }
+            if (!acx.getPermission().equals(LOGGERFINDER_PERMISSION)) {
+                throw new RuntimeException("Unexpected permission in exception: " + acx, acx);
+            }
+            System.out.println("Got expected exception for localized system logger: " + acx);
+            boolean old = allowControl.get().get();
+            allowControl.get().set(true);
+            try {
+                sysLogger2 = provider.getLocalizedLogger("foo", loggerBundle, Thread.class);
+                loggerDescMap.put(sysLogger2, "provider.getLocalizedSystemLogger(\"foo\", loggerBundle)");
+            } finally {
+                allowControl.get().set(old);
+            }
+        }
+        if (appLogger2 == sysLogger2) {
+            throw new RuntimeException("identical loggers");
+        }
+        if (appLogger2 == appLogger1) {
+            throw new RuntimeException("identical loggers");
+        }
+        if (sysLogger2 == sysLogger1) {
+            throw new RuntimeException("identical loggers");
+        }
+
+
+        testLogger(provider, loggerDescMap, "foo", null, appLogger1, appSink);
+        testLogger(provider, loggerDescMap, "foo", null, sysLogger1, sysSink);
+        testLogger(provider, loggerDescMap, "foo", loggerBundle, appLogger2, appSink);
+        testLogger(provider, loggerDescMap, "foo", loggerBundle, sysLogger2, sysSink);
+
+
+        Logger appLogger3 = System.getLogger("foo");
+        loggerDescMap.put(appLogger3, "System.getLogger(\"foo\")");
+
+        testLogger(provider, loggerDescMap, "foo", null, appLogger3, appSink);
+
+        Logger appLogger4 =
+                System.getLogger("foo", loggerBundle);
+        loggerDescMap.put(appLogger4, "System.getLogger(\"foo\", loggerBundle)");
+
+        if (appLogger4 == appLogger1) {
+            throw new RuntimeException("identical loggers");
+        }
+
+        testLogger(provider, loggerDescMap, "foo", loggerBundle, appLogger4, appSink);
+
+        Logger sysLogger3 = accessSystemLogger.getLogger("foo");
+        loggerDescMap.put(sysLogger3, "AccessSystemLogger.getLogger(\"foo\")");
+
+        testLogger(provider, loggerDescMap, "foo", null, sysLogger3, sysSink);
+
+        Logger sysLogger4 =
+                accessSystemLogger.getLogger("foo", loggerBundle);
+        loggerDescMap.put(appLogger4, "AccessSystemLogger.getLogger(\"foo\", loggerBundle)");
+
+        if (sysLogger4 == sysLogger1) {
+            throw new RuntimeException("identical loggers");
+        }
+
+        testLogger(provider, loggerDescMap, "foo", loggerBundle, sysLogger4, sysSink);
+
+    }
+
+    public static class Foo {
+
+    }
+
+    static void verbose(String msg) {
+       if (VERBOSE) {
+           System.out.println(msg);
+       }
+    }
+
+    static void setLevel(java.util.logging.Logger sink, java.util.logging.Level loggerLevel) {
+        boolean before = allowAll.get().get();
+        try {
+            allowAll.get().set(true);
+            sink.setLevel(loggerLevel);
+        } finally {
+            allowAll.get().set(before);
+        }
+    }
+
+
+    // Calls the 8 methods defined on Logger and verify the
+    // parameters received by the underlying Logger Impl
+    // logger.
+    private static void testLogger(LoggerFinder provider,
+            Map<Logger, String> loggerDescMap,
+            String name,
+            ResourceBundle loggerBundle,
+            Logger logger,
+            java.util.logging.Logger sink) {
+
+        System.out.println("Testing " + loggerDescMap.get(logger) + " [" + logger + "]");
+        final java.util.logging.Level OFF = java.util.logging.Level.OFF;
+
+        Foo foo = new Foo();
+        String fooMsg = foo.toString();
+        for (java.util.logging.Level loggerLevel : julLevels) {
+            setLevel(sink, loggerLevel);
+            for (Level messageLevel : Level.values()) {
+                java.util.logging.Level julLevel = mapToJul(messageLevel);
+                String desc = "logger.log(messageLevel, foo): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                LogEvent expected =
+                        LogEvent.of(
+                            sequencer.get(),
+                            julLevel.intValue() >= loggerLevel.intValue(),
+                            name, julLevel, (ResourceBundle)null,
+                            fooMsg, (Throwable)null, (Object[])null);
+                logger.log(messageLevel, foo);
+                if (loggerLevel == OFF || julLevel.intValue() < loggerLevel.intValue()) {
+                    if (eventQueue.poll() != null) {
+                        throw new RuntimeException("unexpected event in queue for " + desc);
+                    }
+                } else {
+                    LogEvent actual =  eventQueue.poll();
+                    if (!expected.equals(actual)) {
+                        throw new RuntimeException("mismatch for " + desc
+                                + "\n\texpected=" + expected
+                                + "\n\t  actual=" + actual);
+                    } else {
+                        verbose("Got expected results for "
+                                + desc + "\n\t" + expected);
+                    }
+                }
+            }
+        }
+
+        String msg = "blah";
+        for (java.util.logging.Level loggerLevel : julLevels) {
+            setLevel(sink, loggerLevel);
+            for (Level messageLevel : Level.values()) {
+                java.util.logging.Level julLevel = mapToJul(messageLevel);
+                String desc = "logger.log(messageLevel, \"blah\"): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                LogEvent expected =
+                        LogEvent.of(
+                            sequencer.get(),
+                            julLevel.intValue() >= loggerLevel.intValue(),
+                            name, julLevel, loggerBundle,
+                            msg, (Throwable)null, (Object[])null);
+                logger.log(messageLevel, msg);
+                if (loggerLevel == OFF || julLevel.intValue() < loggerLevel.intValue()) {
+                    if (eventQueue.poll() != null) {
+                        throw new RuntimeException("unexpected event in queue for " + desc);
+                    }
+                } else {
+                    LogEvent actual =  eventQueue.poll();
+                    if (!expected.equals(actual)) {
+                        throw new RuntimeException("mismatch for " + desc
+                                + "\n\texpected=" + expected
+                                + "\n\t  actual=" + actual);
+                    } else {
+                        verbose("Got expected results for "
+                                + desc + "\n\t" + expected);
+                    }
+                }
+            }
+        }
+
+        Supplier<String> fooSupplier = new Supplier<String>() {
+            @Override
+            public String get() {
+                return this.toString();
+            }
+        };
+
+        for (java.util.logging.Level loggerLevel : julLevels) {
+            setLevel(sink, loggerLevel);
+            for (Level messageLevel : Level.values()) {
+                java.util.logging.Level julLevel = mapToJul(messageLevel);
+                String desc = "logger.log(messageLevel, fooSupplier): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                LogEvent expected =
+                        LogEvent.of(
+                            sequencer.get(),
+                            julLevel.intValue() >= loggerLevel.intValue(),
+                            name, julLevel, (ResourceBundle)null,
+                            fooSupplier.get(),
+                            (Throwable)null, (Object[])null);
+                logger.log(messageLevel, fooSupplier);
+                if (loggerLevel == OFF || julLevel.intValue() < loggerLevel.intValue()) {
+                    if (eventQueue.poll() != null) {
+                        throw new RuntimeException("unexpected event in queue for " + desc);
+                    }
+                } else {
+                    LogEvent actual =  eventQueue.poll();
+                    if (!expected.equals(actual)) {
+                        throw new RuntimeException("mismatch for " + desc
+                                + "\n\texpected=" + expected
+                                + "\n\t  actual=" + actual);
+                    } else {
+                        verbose("Got expected results for "
+                                + desc + "\n\t" + expected);
+                    }
+                }
+            }
+        }
+
+        String format = "two params [{1} {2}]";
+        Object arg1 = foo;
+        Object arg2 = msg;
+        for (java.util.logging.Level loggerLevel : julLevels) {
+            setLevel(sink, loggerLevel);
+            for (Level messageLevel : Level.values()) {
+                java.util.logging.Level julLevel = mapToJul(messageLevel);
+                String desc = "logger.log(messageLevel, format, params...): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                LogEvent expected =
+                        LogEvent.of(
+                            sequencer.get(),
+                            julLevel.intValue() >= loggerLevel.intValue(),
+                            name, julLevel, loggerBundle,
+                            format, (Throwable)null, new Object[] {arg1, arg2});
+                logger.log(messageLevel, format, arg1, arg2);
+                if (loggerLevel == OFF || julLevel.intValue() < loggerLevel.intValue()) {
+                    if (eventQueue.poll() != null) {
+                        throw new RuntimeException("unexpected event in queue for " + desc);
+                    }
+                } else {
+                    LogEvent actual =  eventQueue.poll();
+                    if (!expected.equals(actual)) {
+                        throw new RuntimeException("mismatch for " + desc
+                                + "\n\texpected=" + expected
+                                + "\n\t  actual=" + actual);
+                    } else {
+                        verbose("Got expected results for "
+                                + desc + "\n\t" + expected);
+                    }
+                }
+            }
+        }
+
+        Throwable thrown = new Exception("OK: log me!");
+        for (java.util.logging.Level loggerLevel : julLevels) {
+            setLevel(sink, loggerLevel);
+            for (Level messageLevel : Level.values()) {
+                java.util.logging.Level julLevel = mapToJul(messageLevel);
+                String desc = "logger.log(messageLevel, \"blah\", thrown): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                LogEvent expected =
+                        LogEvent.of(
+                            sequencer.get(),
+                            julLevel.intValue() >= loggerLevel.intValue(),
+                            name, julLevel, loggerBundle,
+                            msg, thrown, (Object[]) null);
+                logger.log(messageLevel, msg, thrown);
+                if (loggerLevel == OFF || julLevel.intValue() < loggerLevel.intValue()) {
+                    if (eventQueue.poll() != null) {
+                        throw new RuntimeException("unexpected event in queue for " + desc);
+                    }
+                } else {
+                    LogEvent actual =  eventQueue.poll();
+                    if (!expected.equals(actual)) {
+                        throw new RuntimeException("mismatch for " + desc
+                                + "\n\texpected=" + expected
+                                + "\n\t  actual=" + actual);
+                    } else {
+                        verbose("Got expected results for "
+                                + desc + "\n\t" + expected);
+                    }
+                }
+            }
+        }
+
+
+        for (java.util.logging.Level loggerLevel : julLevels) {
+            setLevel(sink, loggerLevel);
+            for (Level messageLevel : Level.values()) {
+                java.util.logging.Level julLevel = mapToJul(messageLevel);
+                String desc = "logger.log(messageLevel, thrown, fooSupplier): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                LogEvent expected =
+                        LogEvent.of(
+                            sequencer.get(),
+                            julLevel.intValue() >= loggerLevel.intValue(),
+                            name, julLevel, (ResourceBundle)null,
+                            fooSupplier.get(),
+                            (Throwable)thrown, (Object[])null);
+                logger.log(messageLevel, fooSupplier, thrown);
+                if (loggerLevel == OFF || julLevel.intValue() < loggerLevel.intValue()) {
+                    if (eventQueue.poll() != null) {
+                        throw new RuntimeException("unexpected event in queue for " + desc);
+                    }
+                } else {
+                    LogEvent actual =  eventQueue.poll();
+                    if (!expected.equals(actual)) {
+                        throw new RuntimeException("mismatch for " + desc
+                                + "\n\texpected=" + expected
+                                + "\n\t  actual=" + actual);
+                    } else {
+                        verbose("Got expected results for "
+                                + desc + "\n\t" + expected);
+                    }
+                }
+            }
+        }
+
+        ResourceBundle bundle = ResourceBundle.getBundle(MyBundle.class.getName());
+        for (java.util.logging.Level loggerLevel : julLevels) {
+            setLevel(sink, loggerLevel);
+            for (Level messageLevel : Level.values()) {
+                java.util.logging.Level julLevel = mapToJul(messageLevel);
+                String desc = "logger.log(messageLevel, bundle, format, params...): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                LogEvent expected =
+                        LogEvent.of(
+                            sequencer.get(),
+                            julLevel.intValue() >= loggerLevel.intValue(),
+                            name, julLevel, bundle,
+                            format, (Throwable)null, new Object[] {foo, msg});
+                logger.log(messageLevel, bundle, format, foo, msg);
+                if (loggerLevel == OFF || julLevel.intValue() < loggerLevel.intValue()) {
+                    if (eventQueue.poll() != null) {
+                        throw new RuntimeException("unexpected event in queue for " + desc);
+                    }
+                } else {
+                    LogEvent actual =  eventQueue.poll();
+                    if (!expected.equals(actual)) {
+                        throw new RuntimeException("mismatch for " + desc
+                                + "\n\texpected=" + expected
+                                + "\n\t  actual=" + actual);
+                    } else {
+                        verbose("Got expected results for "
+                                + desc + "\n\t" + expected);
+                    }
+                }
+            }
+        }
+
+        for (java.util.logging.Level loggerLevel : julLevels) {
+            setLevel(sink, loggerLevel);
+            for (Level messageLevel : Level.values()) {
+                java.util.logging.Level julLevel = mapToJul(messageLevel);
+                String desc = "logger.log(messageLevel, bundle, \"blah\", thrown): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                LogEvent expected =
+                        LogEvent.of(
+                            sequencer.get(),
+                            julLevel.intValue() >= loggerLevel.intValue(),
+                            name, julLevel, bundle,
+                            msg, thrown, (Object[]) null);
+                logger.log(messageLevel, bundle, msg, thrown);
+                if (loggerLevel == OFF || julLevel.intValue() < loggerLevel.intValue()) {
+                    if (eventQueue.poll() != null) {
+                        throw new RuntimeException("unexpected event in queue for " + desc);
+                    }
+                } else {
+                    LogEvent actual =  eventQueue.poll();
+                    if (!expected.equals(actual)) {
+                        throw new RuntimeException("mismatch for " + desc
+                                + "\n\texpected=" + expected
+                                + "\n\t  actual=" + actual);
+                    } else {
+                        verbose("Got expected results for "
+                                + desc + "\n\t" + expected);
+                    }
+                }
+            }
+        }
+    }
+
+    final static class PermissionsBuilder {
+        final Permissions perms;
+        public PermissionsBuilder() {
+            this(new Permissions());
+        }
+        public PermissionsBuilder(Permissions perms) {
+            this.perms = perms;
+        }
+        public PermissionsBuilder add(Permission p) {
+            perms.add(p);
+            return this;
+        }
+        public PermissionsBuilder addAll(PermissionCollection col) {
+            if (col != null) {
+                for (Enumeration<Permission> e = col.elements(); e.hasMoreElements(); ) {
+                    perms.add(e.nextElement());
+                }
+            }
+            return this;
+        }
+        public Permissions toPermissions() {
+            final PermissionsBuilder builder = new PermissionsBuilder();
+            builder.addAll(perms);
+            return builder.perms;
+        }
+    }
+
+    public static class SimplePolicy extends Policy {
+
+        final Permissions permissions;
+        final Permissions withControlPermissions;
+        final Permissions allPermissions;
+        final ThreadLocal<AtomicBoolean> allowAll;
+        final ThreadLocal<AtomicBoolean> allowControl;
+        public SimplePolicy(ThreadLocal<AtomicBoolean> allowAll,
+                ThreadLocal<AtomicBoolean> allowControl) {
+            this.allowAll = allowAll;
+            this.allowControl = allowControl;
+            permissions = new Permissions();
+
+            withControlPermissions = new Permissions();
+            withControlPermissions.add(LOGGERFINDER_PERMISSION);
+
+            // these are used for configuring the test itself...
+            allPermissions = new Permissions();
+            allPermissions.add(new java.security.AllPermission());
+        }
+
+        @Override
+        public boolean implies(ProtectionDomain domain, Permission permission) {
+            if (allowAll.get().get()) return allPermissions.implies(permission);
+            if (allowControl.get().get()) return withControlPermissions.implies(permission);
+            return permissions.implies(permission);
+        }
+
+        @Override
+        public PermissionCollection getPermissions(CodeSource codesource) {
+            return new PermissionsBuilder().addAll(
+                    allowAll.get().get() ? allPermissions :
+                    allowControl.get().get()
+                    ? withControlPermissions : permissions).toPermissions();
+        }
+
+        @Override
+        public PermissionCollection getPermissions(ProtectionDomain domain) {
+            return new PermissionsBuilder().addAll(
+                    allowAll.get().get() ? allPermissions :
+                    allowControl.get().get()
+                    ? withControlPermissions : permissions).toPermissions();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/LoggerFinder/internal/BaseDefaultLoggerFinderTest/AccessSystemLogger.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+import java.io.IOException;
+import java.lang.System.Logger;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+import java.util.ResourceBundle;
+
+/**
+ *
+ * @author danielfuchs
+ */
+public final class AccessSystemLogger {
+
+    public AccessSystemLogger() {
+        this(check());
+    }
+
+    private AccessSystemLogger(Void unused) {
+    }
+
+    private static Void check() {
+        if (AccessSystemLogger.class.getClassLoader() != null) {
+            throw new RuntimeException("AccessSystemLogger should be loaded by the null classloader");
+        }
+        return null;
+    }
+
+    public Logger getLogger(String name) {
+        Logger logger = System.getLogger(name);
+        System.out.println("System.getLogger(\"" + name + "\"): " + logger);
+        return logger;
+    }
+
+    public Logger getLogger(String name, ResourceBundle bundle) {
+        Logger logger = System.getLogger(name, bundle);
+        System.out.println("System.getLogger(\"" + name + "\", bundle): " + logger);
+        return logger;
+    }
+
+    static final Class<?>[] toCopy = { AccessSystemLogger.class, CustomSystemClassLoader.class };
+
+    // copy AccessSystemLogger.class to ./boot
+    public static void main(String[] args) throws IOException {
+        Path testDir = Paths.get(System.getProperty("user.dir", "."));
+        Path bootDir = Paths.get(testDir.toString(), "boot");
+        Path classes = Paths.get(System.getProperty("test.classes", "build/classes"));
+        if (Files.notExists(bootDir)) {
+            Files.createDirectory(bootDir);
+        }
+        for (Class<?> c : toCopy) {
+            Path thisClass = Paths.get(classes.toString(),
+                c.getSimpleName()+".class");
+            Path dest = Paths.get(bootDir.toString(),
+                c.getSimpleName()+".class");
+            Files.copy(thisClass, dest, StandardCopyOption.REPLACE_EXISTING);
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/LoggerFinder/internal/BaseDefaultLoggerFinderTest/BaseDefaultLoggerFinderTest.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,768 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.io.UncheckedIOException;
+import java.security.AccessControlException;
+import java.security.CodeSource;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.Permissions;
+import java.security.Policy;
+import java.security.ProtectionDomain;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.ResourceBundle;
+import java.util.stream.Stream;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.Supplier;
+import java.lang.System.LoggerFinder;
+import java.lang.System.Logger;
+import java.lang.System.Logger.Level;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Locale;
+import java.util.concurrent.atomic.AtomicReference;
+import jdk.internal.logger.DefaultLoggerFinder;
+import jdk.internal.logger.SimpleConsoleLogger;
+import sun.util.logging.PlatformLogger;
+
+/**
+ * @test
+ * @bug     8140364
+ * @summary JDK implementation specific unit test for the base DefaultLoggerFinder.
+ *          Tests the behavior of DefaultLoggerFinder and SimpleConsoleLogger
+ *          implementation.
+ * @modules java.base/sun.util.logging
+ *          java.base/jdk.internal.logger
+ * @build AccessSystemLogger BaseDefaultLoggerFinderTest CustomSystemClassLoader
+ * @run  driver AccessSystemLogger
+ * @run  main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader BaseDefaultLoggerFinderTest NOSECURITY
+ * @run  main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader BaseDefaultLoggerFinderTest NOPERMISSIONS
+ * @run  main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader BaseDefaultLoggerFinderTest WITHPERMISSIONS
+ * @author danielfuchs
+ */
+public class BaseDefaultLoggerFinderTest {
+
+    static final RuntimePermission LOGGERFINDER_PERMISSION =
+                new RuntimePermission("loggerFinder");
+    final static boolean VERBOSE = false;
+    static final ThreadLocal<AtomicBoolean> allowControl = new ThreadLocal<AtomicBoolean>() {
+        @Override
+        protected AtomicBoolean initialValue() {
+            return  new AtomicBoolean(false);
+        }
+    };
+    static final ThreadLocal<AtomicBoolean> allowAccess = new ThreadLocal<AtomicBoolean>() {
+        @Override
+        protected AtomicBoolean initialValue() {
+            return  new AtomicBoolean(false);
+        }
+    };
+
+    final static AccessSystemLogger accessSystemLogger = new AccessSystemLogger();
+    static final Class<?>[] providerClass;
+    static {
+        try {
+            providerClass = new Class<?>[] {
+                ClassLoader.getSystemClassLoader().loadClass("BaseDefaultLoggerFinderTest$BaseLoggerFinder"),
+            };
+        } catch (ClassNotFoundException ex) {
+            throw new ExceptionInInitializerError(ex);
+        }
+    }
+
+    /**
+     * What our test provider needs to implement.
+     */
+    public static interface TestLoggerFinder {
+        public final static AtomicBoolean fails = new AtomicBoolean();
+        public final static AtomicReference<String> conf = new AtomicReference<>("");
+        public final static AtomicLong sequencer = new AtomicLong();
+
+
+        public Logger getLogger(String name, Class<?> caller);
+        public Logger getLocalizedLogger(String name, ResourceBundle bundle, Class<?> caller);
+        void setLevel(Logger logger, Level level, Class<?> caller);
+        void setLevel(Logger logger, PlatformLogger.Level level, Class<?> caller);
+        PlatformLogger.Bridge asPlatformLoggerBridge(Logger logger);
+    }
+
+    public static class BaseLoggerFinder extends DefaultLoggerFinder implements TestLoggerFinder {
+
+        static final RuntimePermission LOGGERFINDER_PERMISSION =
+                    new RuntimePermission("loggerFinder");
+        public BaseLoggerFinder() {
+            if (fails.get()) {
+                throw new RuntimeException("Simulate exception while loading provider");
+            }
+        }
+
+        @Override
+        public void setLevel(Logger logger, Level level, Class<?> caller) {
+            PrivilegedAction<Void> pa = () -> {
+                setLevel(logger, PlatformLogger.toPlatformLevel(level), caller);
+                return null;
+            };
+            AccessController.doPrivileged(pa);
+        }
+
+        @Override
+        public void setLevel(Logger logger, PlatformLogger.Level level, Class<?> caller) {
+            PrivilegedAction<Logger> pa = () -> demandLoggerFor(logger.getName(), caller);
+            Logger impl = AccessController.doPrivileged(pa);
+            SimpleConsoleLogger.class.cast(impl)
+                    .getLoggerConfiguration()
+                    .setPlatformLevel(level);
+        }
+
+        @Override
+        public PlatformLogger.Bridge asPlatformLoggerBridge(Logger logger) {
+            PrivilegedAction<PlatformLogger.Bridge> pa = () ->
+                PlatformLogger.Bridge.convert(logger);
+            return AccessController.doPrivileged(pa);
+        }
+
+    }
+
+    public static class MyBundle extends ResourceBundle {
+
+        final ConcurrentHashMap<String,String> map = new ConcurrentHashMap<>();
+
+        @Override
+        protected Object handleGetObject(String key) {
+            if (key.contains(" (translated)")) {
+                throw new RuntimeException("Unexpected key: " + key);
+            }
+            return map.computeIfAbsent(key, k -> k.toUpperCase(Locale.ROOT) + " (translated)");
+        }
+
+        @Override
+        public Enumeration<String> getKeys() {
+            return Collections.enumeration(map.keySet());
+        }
+
+    }
+    public static class MyLoggerBundle extends MyBundle {
+
+    }
+
+    static enum TestCases {NOSECURITY, NOPERMISSIONS, WITHPERMISSIONS};
+
+    static void setSecurityManager() {
+        if (System.getSecurityManager() == null) {
+            Policy.setPolicy(new SimplePolicy(allowControl, allowAccess));
+            System.setSecurityManager(new SecurityManager());
+        }
+    }
+
+    static TestLoggerFinder getLoggerFinder(Class<?> expectedClass) {
+        LoggerFinder provider = null;
+        try {
+            TestLoggerFinder.sequencer.incrementAndGet();
+            provider = LoggerFinder.getLoggerFinder();
+        } catch(AccessControlException a) {
+            throw a;
+        }
+        ErrorStream.errorStream.store();
+        System.out.println("*** Actual LoggerFinder class is: " + provider.getClass().getName());
+        expectedClass.cast(provider);
+        return TestLoggerFinder.class.cast(provider);
+    }
+
+
+    static class ErrorStream extends PrintStream {
+
+        static AtomicBoolean forward = new AtomicBoolean();
+        ByteArrayOutputStream out;
+        String saved = "";
+        public ErrorStream(ByteArrayOutputStream out) {
+            super(out);
+            this.out = out;
+        }
+
+        @Override
+        public void write(int b) {
+            super.write(b);
+            if (forward.get()) err.write(b);
+        }
+
+        @Override
+        public void write(byte[] b) throws IOException {
+            super.write(b);
+            if (forward.get()) err.write(b);
+        }
+
+        @Override
+        public void write(byte[] buf, int off, int len) {
+            super.write(buf, off, len);
+            if (forward.get()) err.write(buf, off, len);
+        }
+
+        public String peek() {
+            flush();
+            return out.toString();
+        }
+
+        public String drain() {
+            flush();
+            String res = out.toString();
+            out.reset();
+            return res;
+        }
+
+        public void store() {
+            flush();
+            saved = out.toString();
+            out.reset();
+        }
+
+        public void restore() {
+            out.reset();
+            try {
+                out.write(saved.getBytes());
+            } catch(IOException io) {
+                throw new UncheckedIOException(io);
+            }
+        }
+
+        static final PrintStream err = System.err;
+        static final ErrorStream errorStream = new ErrorStream(new ByteArrayOutputStream());
+    }
+
+    private static StringBuilder appendProperty(StringBuilder b, String name) {
+        String value = System.getProperty(name);
+        if (value == null) return b;
+        return b.append(name).append("=").append(value).append('\n');
+    }
+
+    public static void main(String[] args) {
+        if (args.length == 0) {
+            args = new String[] {
+                //"NOSECURITY",
+                "NOPERMISSIONS",
+                "WITHPERMISSIONS"
+            };
+        }
+        Locale.setDefault(Locale.ENGLISH);
+        System.setErr(ErrorStream.errorStream);
+        //System.setProperty("jdk.logger.finder.error", "ERROR");
+        //System.setProperty("jdk.logger.finder.singleton", "true");
+        //System.setProperty("test.fails", "true");
+        TestLoggerFinder.fails.set(Boolean.getBoolean("test.fails"));
+        StringBuilder c = new StringBuilder();
+        appendProperty(c, "jdk.logger.packages");
+        appendProperty(c, "jdk.logger.finder.error");
+        appendProperty(c, "jdk.logger.finder.singleton");
+        appendProperty(c, "test.fails");
+        TestLoggerFinder.conf.set(c.toString());
+        try {
+            test(args);
+        } finally {
+            try {
+                System.setErr(ErrorStream.err);
+            } catch (Error | RuntimeException x) {
+                x.printStackTrace(ErrorStream.err);
+            }
+        }
+    }
+
+
+    public static void test(String[] args) {
+
+        final Class<?> expectedClass = jdk.internal.logger.DefaultLoggerFinder.class;
+
+        System.out.println("Declared provider class: " + providerClass[0]
+                + "[" + providerClass[0].getClassLoader() + "]");
+
+        Stream.of(args).map(TestCases::valueOf).forEach((testCase) -> {
+            TestLoggerFinder provider;
+            ErrorStream.errorStream.restore();
+            switch (testCase) {
+                case NOSECURITY:
+                    System.out.println("\n*** Without Security Manager\n");
+                    System.out.println(TestLoggerFinder.conf.get());
+                    provider = getLoggerFinder(expectedClass);
+                    if (!provider.getClass().getName().equals("BaseDefaultLoggerFinderTest$BaseLoggerFinder")) {
+                        throw new RuntimeException("Unexpected provider: " + provider.getClass().getName());
+                    }
+                    test(provider, true);
+                    System.out.println("Tetscase count: " + TestLoggerFinder.sequencer.get());
+                    break;
+                case NOPERMISSIONS:
+                    System.out.println("\n*** With Security Manager, without permissions\n");
+                    System.out.println(TestLoggerFinder.conf.get());
+                    setSecurityManager();
+                    try {
+                        provider = getLoggerFinder(expectedClass);
+                        throw new RuntimeException("Expected exception not raised");
+                    } catch (AccessControlException x) {
+                        if (!LOGGERFINDER_PERMISSION.equals(x.getPermission())) {
+                            throw new RuntimeException("Unexpected permission check", x);
+                        }
+                        final boolean control = allowControl.get().get();
+                        try {
+                            allowControl.get().set(true);
+                            provider = getLoggerFinder(expectedClass);
+                            if (!provider.getClass().getName().equals("BaseDefaultLoggerFinderTest$BaseLoggerFinder")) {
+                                throw new RuntimeException("Unexpected provider: " + provider.getClass().getName());
+                            }
+                        } finally {
+                            allowControl.get().set(control);
+                        }
+                    }
+                    test(provider, false);
+                    System.out.println("Tetscase count: " + TestLoggerFinder.sequencer.get());
+                    break;
+                case WITHPERMISSIONS:
+                    System.out.println("\n*** With Security Manager, with control permission\n");
+                    System.out.println(TestLoggerFinder.conf.get());
+                    setSecurityManager();
+                    final boolean control = allowControl.get().get();
+                    try {
+                        allowControl.get().set(true);
+                        provider = getLoggerFinder(expectedClass);
+                        if (!provider.getClass().getName().equals("BaseDefaultLoggerFinderTest$BaseLoggerFinder")) {
+                            throw new RuntimeException("Unexpected provider: " + provider.getClass().getName());
+                        }
+                        test(provider, true);
+                    } finally {
+                        allowControl.get().set(control);
+                    }
+                    break;
+                default:
+                    throw new RuntimeException("Unknown test case: " + testCase);
+            }
+        });
+        System.out.println("\nPASSED: Tested " + TestLoggerFinder.sequencer.get() + " cases.");
+    }
+
+    public static void test(TestLoggerFinder provider, boolean hasRequiredPermissions) {
+
+        ResourceBundle loggerBundle = ResourceBundle.getBundle(MyLoggerBundle.class.getName());
+        final Map<Logger, String> loggerDescMap = new HashMap<>();
+
+        System.Logger sysLogger = accessSystemLogger.getLogger("foo");
+        loggerDescMap.put(sysLogger, "accessSystemLogger.getLogger(\"foo\")");
+        System.Logger localizedSysLogger = accessSystemLogger.getLogger("fox", loggerBundle);
+        loggerDescMap.put(localizedSysLogger, "accessSystemLogger.getLogger(\"fox\", loggerBundle)");
+        System.Logger appLogger = System.getLogger("bar");
+        loggerDescMap.put(appLogger,"System.getLogger(\"bar\")");
+        System.Logger localizedAppLogger = System.getLogger("baz", loggerBundle);
+        loggerDescMap.put(localizedAppLogger,"System.getLogger(\"baz\", loggerBundle)");
+
+        testLogger(provider, loggerDescMap, "foo", null, sysLogger, accessSystemLogger.getClass());
+        testLogger(provider, loggerDescMap, "foo", loggerBundle, localizedSysLogger, accessSystemLogger.getClass());
+        testLogger(provider, loggerDescMap, "foo", null, appLogger, BaseDefaultLoggerFinderTest.class);
+        testLogger(provider, loggerDescMap, "foo", loggerBundle, localizedAppLogger, BaseDefaultLoggerFinderTest.class);
+    }
+
+    public static class Foo {
+
+    }
+
+    static void verbose(String msg) {
+       if (VERBOSE) {
+           System.out.println(msg);
+       }
+    }
+
+    // Calls the 8 methods defined on Logger and verify the
+    // parameters received by the underlying TestProvider.LoggerImpl
+    // logger.
+    private static void testLogger(TestLoggerFinder provider,
+            Map<Logger, String> loggerDescMap,
+            String name,
+            ResourceBundle loggerBundle,
+            Logger logger,
+            Class<?> caller) {
+
+        System.out.println("Testing " + loggerDescMap.get(logger) + " [" + logger +"]");
+        AtomicLong sequencer = TestLoggerFinder.sequencer;
+
+        Foo foo = new Foo();
+        String fooMsg = foo.toString();
+        for (Level loggerLevel : Level.values()) {
+            provider.setLevel(logger, loggerLevel, caller);
+            for (Level messageLevel : Level.values()) {
+                ErrorStream.errorStream.drain();
+                String desc = "logger.log(messageLevel, foo): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                sequencer.incrementAndGet();
+                logger.log(messageLevel, foo);
+                if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
+                    if (!ErrorStream.errorStream.peek().isEmpty()) {
+                        throw new RuntimeException("unexpected event in queue for "
+                                + desc +": " + "\n\t" + ErrorStream.errorStream.drain());
+                    }
+                } else {
+                    String logged = ErrorStream.errorStream.drain();
+                    if (!logged.contains("BaseDefaultLoggerFinderTest testLogger")
+                        || !logged.contains(messageLevel.getName() + ": " + fooMsg)) {
+                        throw new RuntimeException("mismatch for " + desc
+                                + "\n\texpected:" + "\n<<<<\n"
+                                + "[date] BaseDefaultLoggerFinderTest testLogger\n"
+                                + messageLevel.getName() + " " + fooMsg
+                                + "\n>>>>"
+                                + "\n\t  actual:"
+                                + "\n<<<<\n" + logged + ">>>>\n");
+                    } else {
+                        verbose("Got expected results for "
+                                + desc + "\n<<<<\n" + logged + ">>>>\n");
+                    }
+                }
+            }
+        }
+
+        String msg = "blah";
+        for (Level loggerLevel : Level.values()) {
+            provider.setLevel(logger, loggerLevel, caller);
+            for (Level messageLevel : Level.values()) {
+                String desc = "logger.log(messageLevel, \"blah\"): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                sequencer.incrementAndGet();
+                logger.log(messageLevel, msg);
+                if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
+                    if (!ErrorStream.errorStream.peek().isEmpty()) {
+                        throw new RuntimeException("unexpected event in queue for "
+                                + desc +": " + "\n\t" + ErrorStream.errorStream.drain());
+                    }
+                } else {
+                    String logged = ErrorStream.errorStream.drain();
+                    String msgText = loggerBundle == null ? msg : loggerBundle.getString(msg);
+                    if (!logged.contains("BaseDefaultLoggerFinderTest testLogger")
+                        || !logged.contains(messageLevel.getName() + ": " + msgText)) {
+                        throw new RuntimeException("mismatch for " + desc
+                                + "\n\texpected:" + "\n<<<<\n"
+                                + "[date] BaseDefaultLoggerFinderTest testLogger\n"
+                                + messageLevel.getName() + " " + msgText
+                                + "\n>>>>"
+                                + "\n\t  actual:"
+                                + "\n<<<<\n" + logged + ">>>>\n");
+                    } else {
+                        verbose("Got expected results for "
+                                + desc + "\n<<<<\n" + logged + ">>>>\n");
+                    }
+                }
+            }
+        }
+
+        Supplier<String> fooSupplier = new Supplier<String>() {
+            @Override
+            public String get() {
+                return this.toString();
+            }
+        };
+
+        for (Level loggerLevel : Level.values()) {
+            provider.setLevel(logger, loggerLevel, caller);
+            for (Level messageLevel : Level.values()) {
+                String desc = "logger.log(messageLevel, fooSupplier): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                sequencer.incrementAndGet();
+                logger.log(messageLevel, fooSupplier);
+                if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
+                    if (!ErrorStream.errorStream.peek().isEmpty()) {
+                        throw new RuntimeException("unexpected event in queue for "
+                                + desc +": " + "\n\t" + ErrorStream.errorStream.drain());
+                    }
+                } else {
+                    String logged = ErrorStream.errorStream.drain();
+                    if (!logged.contains("BaseDefaultLoggerFinderTest testLogger")
+                        || !logged.contains(messageLevel.getName() + ": " + fooSupplier.get())) {
+                        throw new RuntimeException("mismatch for " + desc
+                                + "\n\texpected:" + "\n<<<<\n"
+                                + "[date] BaseDefaultLoggerFinderTest testLogger\n"
+                                + messageLevel.getName() + " " + fooSupplier.get()
+                                + "\n>>>>"
+                                + "\n\t  actual:"
+                                + "\n<<<<\n" + logged + ">>>>\n");
+                    } else {
+                        verbose("Got expected results for "
+                                + desc + "\n<<<<\n" + logged + ">>>>\n");
+                    }
+                }
+            }
+        }
+
+
+        String format = "two params [{1} {2}]";
+        Object arg1 = foo;
+        Object arg2 = msg;
+        for (Level loggerLevel : Level.values()) {
+            provider.setLevel(logger, loggerLevel, caller);
+            for (Level messageLevel : Level.values()) {
+                String desc = "logger.log(messageLevel, format, params...): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                sequencer.incrementAndGet();
+                logger.log(messageLevel, format, foo, msg);
+                if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
+                    if (!ErrorStream.errorStream.peek().isEmpty()) {
+                        throw new RuntimeException("unexpected event in queue for "
+                                + desc +": " + "\n\t" + ErrorStream.errorStream.drain());
+                    }
+                } else {
+                    String logged = ErrorStream.errorStream.drain();
+                    String msgFormat = loggerBundle == null ? format : loggerBundle.getString(format);
+                    String text = java.text.MessageFormat.format(msgFormat, foo, msg);
+                    if (!logged.contains("BaseDefaultLoggerFinderTest testLogger")
+                        || !logged.contains(messageLevel.getName() + ": " + text)) {
+                        throw new RuntimeException("mismatch for " + desc
+                                + "\n\texpected:" + "\n<<<<\n"
+                                + "[date] BaseDefaultLoggerFinderTest testLogger\n"
+                                + messageLevel.getName() + " " + text
+                                + "\n>>>>"
+                                + "\n\t  actual:"
+                                + "\n<<<<\n" + logged + ">>>>\n");
+                    } else {
+                        verbose("Got expected results for "
+                                + desc + "\n<<<<\n" + logged + ">>>>\n");
+                    }
+                }
+            }
+        }
+
+        Throwable thrown = new Exception("OK: log me!");
+        for (Level loggerLevel : Level.values()) {
+            provider.setLevel(logger, loggerLevel, caller);
+            for (Level messageLevel : Level.values()) {
+                String desc = "logger.log(messageLevel, \"blah\", thrown): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                sequencer.incrementAndGet();
+                logger.log(messageLevel, msg, thrown);
+                if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
+                    if (!ErrorStream.errorStream.peek().isEmpty()) {
+                        throw new RuntimeException("unexpected event in queue for "
+                                + desc +": " + "\n\t" + ErrorStream.errorStream.drain());
+                    }
+                } else {
+                    String logged = ErrorStream.errorStream.drain();
+                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
+                    thrown.printStackTrace(new PrintStream(baos));
+                    String text = baos.toString();
+                    String msgText = loggerBundle == null ? msg : loggerBundle.getString(msg);
+                    if (!logged.contains("BaseDefaultLoggerFinderTest testLogger")
+                        || !logged.contains(messageLevel.getName() + ": " + msgText)
+                        || !logged.contains(text)) {
+                        throw new RuntimeException("mismatch for " + desc
+                                + "\n\texpected:" + "\n<<<<\n"
+                                + "[date] BaseDefaultLoggerFinderTest testLogger\n"
+                                + messageLevel.getName() + " " + msgText +"\n"
+                                + text
+                                + ">>>>"
+                                + "\n\t  actual:"
+                                + "\n<<<<\n" + logged + ">>>>\n");
+                    } else {
+                        verbose("Got expected results for "
+                                + desc + "\n<<<<\n" + logged + ">>>>\n");
+                    }
+                }
+            }
+        }
+
+
+        for (Level loggerLevel : Level.values()) {
+            provider.setLevel(logger, loggerLevel, caller);
+            for (Level messageLevel : Level.values()) {
+                String desc = "logger.log(messageLevel, thrown, fooSupplier): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                sequencer.incrementAndGet();
+                logger.log(messageLevel, fooSupplier, thrown);
+                if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
+                    if (!ErrorStream.errorStream.peek().isEmpty()) {
+                        throw new RuntimeException("unexpected event in queue for "
+                                + desc +": " + "\n\t" + ErrorStream.errorStream.drain());
+                    }
+                } else {
+                    String logged = ErrorStream.errorStream.drain();
+                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
+                    thrown.printStackTrace(new PrintStream(baos));
+                    String text = baos.toString();
+                    if (!logged.contains("BaseDefaultLoggerFinderTest testLogger")
+                        || !logged.contains(messageLevel.getName() + ": " + fooSupplier.get())
+                        || !logged.contains(text)) {
+                        throw new RuntimeException("mismatch for " + desc
+                                + "\n\texpected:" + "\n<<<<\n"
+                                + "[date] BaseDefaultLoggerFinderTest testLogger\n"
+                                + messageLevel.getName() + " " + fooSupplier.get() +"\n"
+                                + text
+                                + ">>>>"
+                                + "\n\t  actual:"
+                                + "\n<<<<\n" + logged + ">>>>\n");
+                    } else {
+                        verbose("Got expected results for "
+                                + desc + "\n<<<<\n" + logged + ">>>>\n");
+                    }
+                }
+            }
+        }
+
+        ResourceBundle bundle = ResourceBundle.getBundle(MyBundle.class.getName());
+        for (Level loggerLevel : Level.values()) {
+            provider.setLevel(logger, loggerLevel, caller);
+            for (Level messageLevel : Level.values()) {
+                String desc = "logger.log(messageLevel, bundle, format, params...): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                sequencer.incrementAndGet();
+                logger.log(messageLevel, bundle, format, foo, msg);
+                if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
+                    if (!ErrorStream.errorStream.peek().isEmpty()) {
+                        throw new RuntimeException("unexpected event in queue for "
+                                + desc +": " + "\n\t" + ErrorStream.errorStream.drain());
+                    }
+                } else {
+                    String logged = ErrorStream.errorStream.drain();
+                    String text = java.text.MessageFormat.format(bundle.getString(format), foo, msg);
+                    if (!logged.contains("BaseDefaultLoggerFinderTest testLogger")
+                        || !logged.contains(messageLevel.getName() + ": " + text)) {
+                        throw new RuntimeException("mismatch for " + desc
+                                + "\n\texpected:" + "\n<<<<\n"
+                                + "[date] BaseDefaultLoggerFinderTest testLogger\n"
+                                + messageLevel.getName() + " " + text
+                                + "\n>>>>"
+                                + "\n\t  actual:"
+                                + "\n<<<<\n" + logged + ">>>>\n");
+                    } else {
+                        verbose("Got expected results for "
+                                + desc + "\n<<<<\n" + logged + ">>>>\n");
+                    }
+                }
+            }
+        }
+
+        for (Level loggerLevel : Level.values()) {
+            provider.setLevel(logger, loggerLevel, caller);
+            for (Level messageLevel : Level.values()) {
+                String desc = "logger.log(messageLevel, bundle, \"blah\", thrown): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                sequencer.incrementAndGet();
+                logger.log(messageLevel, bundle, msg, thrown);
+                if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
+                    if (!ErrorStream.errorStream.peek().isEmpty()) {
+                        throw new RuntimeException("unexpected event in queue for "
+                                + desc +": " + "\n\t" + ErrorStream.errorStream.drain());
+                    }
+                } else {
+                    String logged = ErrorStream.errorStream.drain();
+                    String textMsg = bundle.getString(msg);
+                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
+                    thrown.printStackTrace(new PrintStream(baos));
+                    String text = baos.toString();
+                    if (!logged.contains("BaseDefaultLoggerFinderTest testLogger")
+                        || !logged.contains(messageLevel.getName() + ": " + textMsg)
+                        || !logged.contains(text)) {
+                        throw new RuntimeException("mismatch for " + desc
+                                + "\n\texpected:" + "\n<<<<\n"
+                                + "[date] BaseDefaultLoggerFinderTest testLogger\n"
+                                + messageLevel.getName() + " " + textMsg +"\n"
+                                + text
+                                + ">>>>"
+                                + "\n\t  actual:"
+                                + "\n<<<<\n" + logged + ">>>>\n");
+                    } else {
+                        verbose("Got expected results for "
+                                + desc + "\n<<<<\n" + logged + ">>>>\n");
+                    }
+                }
+            }
+        }
+
+    }
+
+    final static class PermissionsBuilder {
+        final Permissions perms;
+        public PermissionsBuilder() {
+            this(new Permissions());
+        }
+        public PermissionsBuilder(Permissions perms) {
+            this.perms = perms;
+        }
+        public PermissionsBuilder add(Permission p) {
+            perms.add(p);
+            return this;
+        }
+        public PermissionsBuilder addAll(PermissionCollection col) {
+            if (col != null) {
+                for (Enumeration<Permission> e = col.elements(); e.hasMoreElements(); ) {
+                    perms.add(e.nextElement());
+                }
+            }
+            return this;
+        }
+        public Permissions toPermissions() {
+            final PermissionsBuilder builder = new PermissionsBuilder();
+            builder.addAll(perms);
+            return builder.perms;
+        }
+    }
+
+    public static class SimplePolicy extends Policy {
+        final static RuntimePermission CONTROL = LOGGERFINDER_PERMISSION;
+        final static RuntimePermission ACCESS = new RuntimePermission("accessClassInPackage.jdk.internal.logger");
+
+        final Permissions permissions;
+        final ThreadLocal<AtomicBoolean> allowControl;
+        final ThreadLocal<AtomicBoolean> allowAccess;
+        public SimplePolicy(ThreadLocal<AtomicBoolean> allowControl, ThreadLocal<AtomicBoolean> allowAccess) {
+            this.allowControl = allowControl;
+            this.allowAccess = allowAccess;
+            permissions = new Permissions();
+            permissions.add(new RuntimePermission("setIO"));
+        }
+
+        Permissions getPermissions() {
+            if (allowControl.get().get() || allowAccess.get().get()) {
+                PermissionsBuilder builder =  new PermissionsBuilder()
+                        .addAll(permissions);
+                if (allowControl.get().get()) {
+                    builder.add(CONTROL);
+                }
+                if (allowAccess.get().get()) {
+                    builder.add(ACCESS);
+                }
+                return builder.toPermissions();
+            }
+            return permissions;
+        }
+
+        @Override
+        public boolean implies(ProtectionDomain domain, Permission permission) {
+            return getPermissions().implies(permission);
+        }
+
+        @Override
+        public PermissionCollection getPermissions(CodeSource codesource) {
+            return new PermissionsBuilder().addAll(getPermissions()).toPermissions();
+        }
+
+        @Override
+        public PermissionCollection getPermissions(ProtectionDomain domain) {
+            return new PermissionsBuilder().addAll(getPermissions()).toPermissions();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/LoggerFinder/internal/BaseDefaultLoggerFinderTest/CustomSystemClassLoader.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.nio.file.Files;
+import java.security.AllPermission;
+import java.security.Permissions;
+import java.security.ProtectionDomain;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * A custom ClassLoader to load the concrete LoggerFinder class
+ * with all permissions. The CustomSystemClassLoader class must be
+ * in the BCL, otherwise when system classes - such as
+ * ZoneDateTime try to load their resource bundle a MissingResourceBundle
+ * caused by a SecurityException may be thrown, as the CustomSystemClassLoader
+ * code base will be found in the stack called by doPrivileged.
+ *
+ * @author danielfuchs
+ */
+public class CustomSystemClassLoader extends ClassLoader {
+
+
+    final List<String> finderClassNames =
+            Arrays.asList("BaseDefaultLoggerFinderTest$BaseLoggerFinder");
+    final Map<String, Class<?>> finderClasses = new HashMap<>();
+    Class<?> testLoggerFinderClass;
+
+    public CustomSystemClassLoader() {
+        super();
+    }
+    public CustomSystemClassLoader(ClassLoader parent) {
+        super(parent);
+    }
+
+    private Class<?> defineFinderClass(String name)
+        throws ClassNotFoundException {
+        final Object obj = getClassLoadingLock(name);
+        synchronized(obj) {
+            if (finderClasses.get(name) != null) return finderClasses.get(name);
+            if (testLoggerFinderClass == null) {
+                // Hack: we  load testLoggerFinderClass to get its code source.
+                //       we can't use this.getClass() since we are in the boot.
+                testLoggerFinderClass = super.loadClass("BaseDefaultLoggerFinderTest$TestLoggerFinder");
+            }
+            URL url = testLoggerFinderClass.getProtectionDomain().getCodeSource().getLocation();
+            File file = new File(url.getPath(), name+".class");
+            if (file.canRead()) {
+                try {
+                    byte[] b = Files.readAllBytes(file.toPath());
+                    Permissions perms = new Permissions();
+                    perms.add(new AllPermission());
+                    Class<?> finderClass = defineClass(
+                            name, b, 0, b.length, new ProtectionDomain(
+                            this.getClass().getProtectionDomain().getCodeSource(),
+                            perms));
+                    System.out.println("Loaded " + name);
+                    finderClasses.put(name, finderClass);
+                    return finderClass;
+                } catch (Throwable ex) {
+                    ex.printStackTrace();
+                    throw new ClassNotFoundException(name, ex);
+                }
+            } else {
+                throw new ClassNotFoundException(name,
+                        new IOException(file.toPath() + ": can't read"));
+            }
+        }
+    }
+
+    @Override
+    public synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
+        if (finderClassNames.contains(name)) {
+            Class<?> c = defineFinderClass(name);
+            if (resolve) {
+                resolveClass(c);
+            }
+            return c;
+        }
+        return super.loadClass(name, resolve);
+    }
+
+    @Override
+    protected Class<?> findClass(String name) throws ClassNotFoundException {
+        if (finderClassNames.contains(name)) {
+            return defineFinderClass(name);
+        }
+        return super.findClass(name);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/LoggerFinder/internal/BaseDefaultLoggerFinderTest/META-INF/services/java.lang.System$LoggerFinder	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,1 @@
+BaseDefaultLoggerFinderTest$BaseLoggerFinder
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/LoggerFinder/internal/BaseLoggerBridgeTest/BaseLoggerBridgeTest.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,1058 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+import java.security.AccessControlException;
+import java.security.AccessController;
+import java.security.CodeSource;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.Permissions;
+import java.security.Policy;
+import java.security.PrivilegedAction;
+import java.security.ProtectionDomain;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Queue;
+import java.util.ResourceBundle;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.Supplier;
+import sun.util.logging.PlatformLogger;
+import java.lang.System.LoggerFinder;
+import java.lang.System.Logger;
+import java.lang.System.Logger.Level;
+import java.util.stream.Stream;
+
+/**
+ * @test
+ * @bug     8140364
+ * @summary JDK implementation specific unit test for JDK internal artifacts.
+ *   Tests a naive implementation of System.Logger, and in particular
+ *   the default mapping provided by PlatformLogger.Bridge.
+ * @modules java.base/sun.util.logging java.base/jdk.internal.logger
+ * @build CustomSystemClassLoader BaseLoggerBridgeTest
+ * @run  main/othervm -Djava.system.class.loader=CustomSystemClassLoader BaseLoggerBridgeTest NOSECURITY
+ * @run  main/othervm -Djava.system.class.loader=CustomSystemClassLoader BaseLoggerBridgeTest NOPERMISSIONS
+ * @run  main/othervm -Djava.system.class.loader=CustomSystemClassLoader BaseLoggerBridgeTest WITHPERMISSIONS
+ * @author danielfuchs
+ */
+public class BaseLoggerBridgeTest {
+
+    static final RuntimePermission LOGGERFINDER_PERMISSION =
+                new RuntimePermission("loggerFinder");
+    final static AtomicLong sequencer = new AtomicLong();
+    final static boolean VERBOSE = false;
+    // whether the implementation of Logger try to do a best
+    // effort for logp... Our base logger finder stub doesn't
+    // support logp, and thus the logp() implementation comes from
+    // LoggerWrapper - which does a best effort.
+    static final boolean BEST_EFFORT_FOR_LOGP = true;
+    static final ThreadLocal<AtomicBoolean> allowControl = new ThreadLocal<AtomicBoolean>() {
+        @Override
+        protected AtomicBoolean initialValue() {
+            return  new AtomicBoolean(false);
+        }
+    };
+    static final ThreadLocal<AtomicBoolean> allowAccess = new ThreadLocal<AtomicBoolean>() {
+        @Override
+        protected AtomicBoolean initialValue() {
+            return  new AtomicBoolean(false);
+        }
+    };
+    static final ThreadLocal<AtomicBoolean> allowAll = new ThreadLocal<AtomicBoolean>() {
+        @Override
+        protected AtomicBoolean initialValue() {
+            return  new AtomicBoolean(false);
+        }
+    };
+
+    static final Class<?> providerClass;
+    static {
+        try {
+            providerClass = ClassLoader.getSystemClassLoader().loadClass("BaseLoggerBridgeTest$BaseLoggerFinder");
+        } catch (ClassNotFoundException ex) {
+            throw new ExceptionInInitializerError(ex);
+        }
+    }
+
+    static final sun.util.logging.PlatformLogger.Level[] julLevels = {
+        sun.util.logging.PlatformLogger.Level.ALL,
+        sun.util.logging.PlatformLogger.Level.FINEST,
+        sun.util.logging.PlatformLogger.Level.FINER,
+        sun.util.logging.PlatformLogger.Level.FINE,
+        sun.util.logging.PlatformLogger.Level.CONFIG,
+        sun.util.logging.PlatformLogger.Level.INFO,
+        sun.util.logging.PlatformLogger.Level.WARNING,
+        sun.util.logging.PlatformLogger.Level.SEVERE,
+        sun.util.logging.PlatformLogger.Level.OFF,
+    };
+
+    static final Level[] mappedLevels = {
+        Level.ALL,     // ALL
+        Level.TRACE,   // FINEST
+        Level.TRACE,   // FINER
+        Level.DEBUG,   // FINE
+        Level.DEBUG,   // CONFIG
+        Level.INFO,    // INFO
+        Level.WARNING, // WARNING
+        Level.ERROR,   // SEVERE
+        Level.OFF,     // OFF
+    };
+
+    final static Map<sun.util.logging.PlatformLogger.Level, Level> julToSpiMap;
+    static {
+        Map<sun.util.logging.PlatformLogger.Level, Level> map = new HashMap<>();
+        if (mappedLevels.length != julLevels.length) {
+            throw new ExceptionInInitializerError("Array lengths differ"
+                + "\n\tjulLevels=" + Arrays.deepToString(julLevels)
+                + "\n\tmappedLevels=" + Arrays.deepToString(mappedLevels));
+        }
+        for (int i=0; i<julLevels.length; i++) {
+            map.put(julLevels[i], mappedLevels[i]);
+        }
+        julToSpiMap = Collections.unmodifiableMap(map);
+    }
+
+    public static class MyBundle extends ResourceBundle {
+
+        final ConcurrentHashMap<String,String> map = new ConcurrentHashMap<>();
+
+        @Override
+        protected Object handleGetObject(String key) {
+            if (key.contains(" (translated)")) {
+                throw new RuntimeException("Unexpected key: " + key);
+            }
+            return map.computeIfAbsent(key, k -> k + " (translated)");
+        }
+
+        @Override
+        public Enumeration<String> getKeys() {
+            return Collections.enumeration(map.keySet());
+        }
+
+    }
+    public static class MyLoggerBundle extends MyBundle {
+
+    }
+
+    public static interface TestLoggerFinder {
+        final ConcurrentHashMap<String, LoggerImpl> system = new ConcurrentHashMap<>();
+        final ConcurrentHashMap<String, LoggerImpl> user = new ConcurrentHashMap<>();
+        public Queue<LogEvent> eventQueue = new ArrayBlockingQueue<>(128);
+
+        public static final class LogEvent implements Cloneable {
+
+            public LogEvent() {
+                this(sequencer.getAndIncrement());
+            }
+
+            LogEvent(long sequenceNumber) {
+                this.sequenceNumber = sequenceNumber;
+            }
+
+            boolean callSupplier = false;
+            long sequenceNumber;
+            boolean isLoggable;
+            String loggerName;
+            Level level;
+            ResourceBundle bundle;
+            Throwable thrown;
+            Object[] args;
+            Supplier<String> supplier;
+            String msg;
+
+            Object[] toArray(boolean callSupplier) {
+                return new Object[] {
+                    sequenceNumber,
+                    isLoggable,
+                    loggerName,
+                    level,
+                    bundle,
+                    thrown,
+                    args,
+                    callSupplier && supplier != null ? supplier.get() : supplier,
+                    msg,
+                };
+            }
+
+            boolean callSupplier(Object obj) {
+                return callSupplier || ((LogEvent)obj).callSupplier;
+            }
+
+            @Override
+            public String toString() {
+                return Arrays.deepToString(toArray(false));
+            }
+
+
+
+            @Override
+            public boolean equals(Object obj) {
+                return obj instanceof LogEvent
+                        && Objects.deepEquals(toArray(callSupplier(obj)), ((LogEvent)obj).toArray(callSupplier(obj)));
+            }
+
+            @Override
+            public int hashCode() {
+                return Objects.hash(toArray(true));
+            }
+
+            public LogEvent cloneWith(long sequenceNumber)
+                    throws CloneNotSupportedException {
+                LogEvent cloned = (LogEvent)super.clone();
+                cloned.sequenceNumber = sequenceNumber;
+                return cloned;
+            }
+
+            public static LogEvent of(boolean isLoggable, String name,
+                    Level level, ResourceBundle bundle,
+                    String key, Throwable thrown) {
+                LogEvent evt = new LogEvent();
+                evt.isLoggable = isLoggable;
+                evt.loggerName = name;
+                evt.level = level;
+                evt.args = null;
+                evt.bundle = bundle;
+                evt.thrown = thrown;
+                evt.supplier = null;
+                evt.msg = key;
+                return evt;
+            }
+
+            public static LogEvent of(boolean isLoggable, String name,
+                    Level level, Throwable thrown, Supplier<String> supplier) {
+                LogEvent evt = new LogEvent();
+                evt.isLoggable = isLoggable;
+                evt.loggerName = name;
+                evt.level = level;
+                evt.args = null;
+                evt.bundle = null;
+                evt.thrown = thrown;
+                evt.supplier = supplier;
+                evt.msg = null;
+                return evt;
+            }
+
+            public static LogEvent of(boolean isLoggable, String name,
+                    Level level, ResourceBundle bundle,
+                    String key, Object... params) {
+                LogEvent evt = new LogEvent();
+                evt.isLoggable = isLoggable;
+                evt.loggerName = name;
+                evt.level = level;
+                evt.args = params;
+                evt.bundle = bundle;
+                evt.thrown = null;
+                evt.supplier = null;
+                evt.msg = key;
+                return evt;
+            }
+
+            public static LogEvent of(long sequenceNumber,
+                    boolean isLoggable, String name,
+                    Level level, ResourceBundle bundle,
+                    String key, Supplier<String> supplier,
+                    Throwable thrown, Object... params) {
+                LogEvent evt = new LogEvent(sequenceNumber);
+                evt.loggerName = name;
+                evt.level = level;
+                evt.args = params;
+                evt.bundle = bundle;
+                evt.thrown = thrown;
+                evt.supplier = supplier;
+                evt.msg = key;
+                evt.isLoggable = isLoggable;
+                return evt;
+            }
+
+            public static LogEvent ofp(boolean callSupplier, LogEvent evt) {
+                evt.callSupplier = callSupplier;
+                return evt;
+            }
+        }
+
+        public class LoggerImpl implements Logger {
+            private final String name;
+            private Level level = Level.INFO;
+
+            public LoggerImpl(String name) {
+                this.name = name;
+            }
+
+            @Override
+            public String getName() {
+                return name;
+            }
+
+            @Override
+            public boolean isLoggable(Level level) {
+                return this.level != Level.OFF && this.level.getSeverity() <= level.getSeverity();
+            }
+
+            @Override
+            public void log(Level level, ResourceBundle bundle, String key, Throwable thrown) {
+                log(LogEvent.of(isLoggable(level), this.name, level, bundle, key, thrown));
+            }
+
+            @Override
+            public void log(Level level, ResourceBundle bundle, String format, Object... params) {
+                log(LogEvent.of(isLoggable(level), name, level, bundle, format, params));
+            }
+
+            void log(LogEvent event) {
+                eventQueue.add(event);
+            }
+
+            @Override
+            public void log(Level level, Supplier<String> msgSupplier) {
+                log(LogEvent.of(isLoggable(level), name, level, null, msgSupplier));
+            }
+
+            @Override
+            public void log(Level level, Supplier<String> msgSupplier, Throwable thrown) {
+                log(LogEvent.of(isLoggable(level), name, level, thrown, msgSupplier));
+            }
+
+
+
+        }
+
+        public Logger getLogger(String name, Class<?> caller);
+        public Logger getLocalizedLogger(String name, ResourceBundle bundle, Class<?> caller);
+    }
+
+    public static class BaseLoggerFinder extends LoggerFinder implements TestLoggerFinder {
+        static final RuntimePermission LOGGERFINDER_PERMISSION =
+                new RuntimePermission("loggerFinder");
+        @Override
+        public Logger getLogger(String name, Class<?> caller) {
+            SecurityManager sm = System.getSecurityManager();
+            if (sm != null) {
+                sm.checkPermission(LOGGERFINDER_PERMISSION);
+            }
+            PrivilegedAction<ClassLoader> pa = () -> caller.getClassLoader();
+            ClassLoader callerLoader = AccessController.doPrivileged(pa);
+            if (callerLoader == null) {
+                return system.computeIfAbsent(name, (n) -> new LoggerImpl(n));
+            } else {
+                return user.computeIfAbsent(name, (n) -> new LoggerImpl(n));
+            }
+        }
+    }
+
+    static PlatformLogger.Bridge convert(Logger logger) {
+        boolean old = allowAll.get().get();
+        allowAccess.get().set(true);
+        try {
+            return PlatformLogger.Bridge.convert(logger);
+        } finally {
+            allowAccess.get().set(old);
+        }
+    }
+
+    static Logger getLogger(String name, Class<?> caller) {
+        boolean old = allowAll.get().get();
+        allowAccess.get().set(true);
+        try {
+            return jdk.internal.logger.LazyLoggers.getLogger(name, caller);
+        } finally {
+            allowAccess.get().set(old);
+        }
+    }
+
+    static enum TestCases {NOSECURITY, NOPERMISSIONS, WITHPERMISSIONS};
+
+    static void setSecurityManager() {
+        if (System.getSecurityManager() == null) {
+            // Ugly test hack: preload the resources needed by String.format
+            //   We need to do that before setting the security manager
+            //   because our implementation of CustomSystemClassLoader
+            //   doesn't have the required permission.
+            System.out.println(String.format("debug: %s", "Setting security manager"));
+            Policy.setPolicy(new SimplePolicy(allowControl, allowAccess, allowAll));
+            System.setSecurityManager(new SecurityManager());
+        }
+    }
+
+    public static void main(String[] args) {
+        if (args.length == 0)
+            args = new String[] {
+                "NOSECURITY",
+                "NOPERMISSIONS",
+                "WITHPERMISSIONS"
+            };
+
+
+        Stream.of(args).map(TestCases::valueOf).forEach((testCase) -> {
+            TestLoggerFinder provider;
+            switch (testCase) {
+                case NOSECURITY:
+                    System.out.println("\n*** Without Security Manager\n");
+                    provider = TestLoggerFinder.class.cast(LoggerFinder.getLoggerFinder());
+                    test(provider, true);
+                    System.out.println("Tetscase count: " + sequencer.get());
+                    break;
+                case NOPERMISSIONS:
+                    System.out.println("\n*** With Security Manager, without permissions\n");
+                    setSecurityManager();
+                    try {
+                        provider = TestLoggerFinder.class.cast(LoggerFinder.getLoggerFinder());
+                        throw new RuntimeException("Expected exception not raised");
+                    } catch (AccessControlException x) {
+                        if (!LOGGERFINDER_PERMISSION.equals(x.getPermission())) {
+                            throw new RuntimeException("Unexpected permission check", x);
+                        }
+                        final boolean control = allowControl.get().get();
+                        try {
+                            allowControl.get().set(true);
+                            provider = TestLoggerFinder.class.cast(LoggerFinder.getLoggerFinder());
+                        } finally {
+                            allowControl.get().set(control);
+                        }
+                    }
+                    test(provider, false);
+                    System.out.println("Tetscase count: " + sequencer.get());
+                    break;
+                case WITHPERMISSIONS:
+                    System.out.println("\n*** With Security Manager, with control permission\n");
+                    setSecurityManager();
+                    final boolean control = allowControl.get().get();
+                    try {
+                        allowControl.get().set(true);
+                        provider = TestLoggerFinder.class.cast(LoggerFinder.getLoggerFinder());
+                        test(provider, true);
+                    } finally {
+                        allowControl.get().set(control);
+                    }
+                    break;
+                default:
+                    throw new RuntimeException("Unknown test case: " + testCase);
+            }
+        });
+        System.out.println("\nPASSED: Tested " + sequencer.get() + " cases.");
+    }
+
+    public static void test(TestLoggerFinder provider, boolean hasRequiredPermissions) {
+
+        ResourceBundle loggerBundle = ResourceBundle.getBundle(MyLoggerBundle.class.getName());
+        final Map<Object, String> loggerDescMap = new HashMap<>();
+
+
+        TestLoggerFinder.LoggerImpl appSink = null;
+        try {
+            appSink = TestLoggerFinder.LoggerImpl.class.cast(provider.getLogger("foo", BaseLoggerBridgeTest.class));
+            if (!hasRequiredPermissions) {
+                throw new RuntimeException("Managed to obtain a system logger without permission");
+            }
+        } catch (AccessControlException acx) {
+            if (hasRequiredPermissions) {
+                throw new RuntimeException("Unexpected security exception: ", acx);
+            }
+            if (!acx.getPermission().equals(LOGGERFINDER_PERMISSION)) {
+                throw new RuntimeException("Unexpected permission in exception: " + acx, acx);
+            }
+            System.out.println("Got expected exception for logger: " + acx);
+            boolean old = allowControl.get().get();
+            allowControl.get().set(true);
+            try {
+                appSink = TestLoggerFinder.LoggerImpl.class.cast(provider.getLogger("foo", BaseLoggerBridgeTest.class));
+            } finally {
+                allowControl.get().set(old);
+            }
+        }
+
+
+        TestLoggerFinder.LoggerImpl sysSink = null;
+        try {
+            sysSink = TestLoggerFinder.LoggerImpl.class.cast(provider.getLogger("foo", Thread.class));
+            if (!hasRequiredPermissions) {
+                throw new RuntimeException("Managed to obtain a system logger without permission");
+            }
+        } catch (AccessControlException acx) {
+            if (hasRequiredPermissions) {
+                throw new RuntimeException("Unexpected security exception: ", acx);
+            }
+            if (!acx.getPermission().equals(LOGGERFINDER_PERMISSION)) {
+                throw new RuntimeException("Unexpected permission in exception: " + acx, acx);
+            }
+            System.out.println("Got expected exception for system logger: " + acx);
+        }
+        if (hasRequiredPermissions && appSink == sysSink) {
+            throw new RuntimeException("identical loggers");
+        }
+
+        if (provider.system.contains(appSink)) {
+            throw new RuntimeException("app logger in system map");
+        }
+        if (!provider.user.contains(appSink)) {
+            throw new RuntimeException("app logger not in appplication map");
+        }
+        if (hasRequiredPermissions && provider.user.contains(sysSink)) {
+            throw new RuntimeException("sys logger in appplication map");
+        }
+        if (hasRequiredPermissions && !provider.system.contains(sysSink)) {
+            throw new RuntimeException("sys logger not in system map");
+        }
+
+        Logger appLogger1 = System.getLogger("foo");
+        loggerDescMap.put(appLogger1, "System.getLogger(\"foo\")");
+        PlatformLogger.Bridge bridge = convert(appLogger1);
+        loggerDescMap.putIfAbsent(bridge, "PlatformLogger.Bridge.convert(System.getLogger(\"foo\"))");
+        testLogger(provider, loggerDescMap, "foo", null, bridge, appSink);
+
+        Logger sysLogger1 = null;
+        try {
+            sysLogger1 = getLogger("foo", Thread.class);
+            loggerDescMap.put(sysLogger1,
+                    "jdk.internal.logger.LazyLoggers.getLogger(\"foo\", Thread.class)");
+
+            if (!hasRequiredPermissions) {
+                // check that the provider would have thrown an exception
+                provider.getLogger("foo", Thread.class);
+                throw new RuntimeException("Managed to obtain a system logger without permission");
+            }
+        } catch (AccessControlException acx) {
+            if (hasRequiredPermissions) {
+                throw new RuntimeException("Unexpected security exception: ", acx);
+            }
+            if (!acx.getPermission().equals(LOGGERFINDER_PERMISSION)) {
+                throw new RuntimeException("Unexpected permission in exception: " + acx, acx);
+            }
+            System.out.println("Got expected exception for system logger: " + acx);
+        }
+
+        if (hasRequiredPermissions) {
+            // if we don't have permissions sysSink will be null.
+            testLogger(provider, loggerDescMap, "foo", null,
+                PlatformLogger.Bridge.convert(sysLogger1), sysSink);
+        }
+
+        Logger appLogger2 =
+                System.getLogger("foo", loggerBundle);
+        loggerDescMap.put(appLogger2, "System.getLogger(\"foo\", loggerBundle)");
+
+        if (appLogger2 == appLogger1) {
+            throw new RuntimeException("identical loggers");
+        }
+
+        if (provider.system.contains(appLogger2)) {
+            throw new RuntimeException("localized app logger in system map");
+        }
+        if (provider.user.contains(appLogger2)) {
+            throw new RuntimeException("localized app logger  in appplication map");
+        }
+
+        testLogger(provider, loggerDescMap, "foo", loggerBundle,
+                PlatformLogger.Bridge.convert(appLogger2), appSink);
+
+        Logger sysLogger2 = null;
+        try {
+            sysLogger2 = provider.getLocalizedLogger("foo", loggerBundle, Thread.class);
+            loggerDescMap.put(sysLogger2, "provider.getLocalizedLogger(\"foo\", loggerBundle, Thread.class)");
+            if (!hasRequiredPermissions) {
+                throw new RuntimeException("Managed to obtain a system logger without permission");
+            }
+        } catch (AccessControlException acx) {
+            if (hasRequiredPermissions) {
+                throw new RuntimeException("Unexpected security exception: ", acx);
+            }
+            if (!acx.getPermission().equals(LOGGERFINDER_PERMISSION)) {
+                throw new RuntimeException("Unexpected permission in exception: " + acx, acx);
+            }
+            System.out.println("Got expected exception for localized system logger: " + acx);
+        }
+        if (hasRequiredPermissions && appLogger2 == sysLogger2) {
+            throw new RuntimeException("identical loggers");
+        }
+        if (hasRequiredPermissions && sysLogger2 == sysLogger1) {
+            throw new RuntimeException("identical loggers");
+        }
+        if (hasRequiredPermissions && provider.user.contains(sysLogger2)) {
+            throw new RuntimeException("localized sys logger in appplication map");
+        }
+        if (hasRequiredPermissions && provider.system.contains(sysLogger2)) {
+            throw new RuntimeException("localized sys logger not in system map");
+        }
+
+        if (hasRequiredPermissions) {
+            // if we don't have permissions sysSink will be null.
+            testLogger(provider, loggerDescMap, "foo", loggerBundle,
+                PlatformLogger.Bridge.convert(sysLogger2), sysSink);
+        }
+
+    }
+
+    public static class Foo {
+
+    }
+
+    static void verbose(String msg) {
+       if (VERBOSE) {
+           System.out.println(msg);
+       }
+    }
+
+    static void checkLogEvent(TestLoggerFinder provider, String desc,
+            TestLoggerFinder.LogEvent expected) {
+        TestLoggerFinder.LogEvent actual =  provider.eventQueue.poll();
+        if (!Objects.equals(expected, actual)) {
+            throw new RuntimeException("mismatch for " + desc
+                    + "\n\texpected=" + expected
+                    + "\n\t  actual=" + actual);
+        } else {
+            verbose("Got expected results for "
+                    + desc + "\n\t" + expected);
+        }
+    }
+
+    static void checkLogEvent(TestLoggerFinder provider, String desc,
+            TestLoggerFinder.LogEvent expected, boolean expectNotNull) {
+        TestLoggerFinder.LogEvent actual =  provider.eventQueue.poll();
+        if (actual == null && !expectNotNull) return;
+        if (actual != null && !expectNotNull) {
+            throw new RuntimeException("Unexpected log event found for " + desc
+                + "\n\tgot: " + actual);
+        }
+        if (!expected.equals(actual)) {
+            throw new RuntimeException("mismatch for " + desc
+                    + "\n\texpected=" + expected
+                    + "\n\t  actual=" + actual);
+        } else {
+            verbose("Got expected results for "
+                    + desc + "\n\t" + expected);
+        }
+    }
+
+        static Supplier<String> logpMessage(ResourceBundle bundle,
+                String className, String methodName, Supplier<String> msg) {
+            if (BEST_EFFORT_FOR_LOGP && bundle == null
+                    && (className != null || methodName != null)) {
+                final String cName = className == null ? "" :  className;
+                final String mName = methodName == null ? "" : methodName;
+                return () -> String.format("[%s %s] %s", cName, mName, msg.get());
+            } else {
+                return msg;
+            }
+        }
+
+        static String logpMessage(ResourceBundle bundle,
+                String className, String methodName, String msg) {
+            if (BEST_EFFORT_FOR_LOGP && bundle == null
+                    && (className != null || methodName != null)) {
+                final String cName = className == null ? "" :  className;
+                final String mName = methodName == null ? "" : methodName;
+                return String.format("[%s %s] %s", cName, mName, msg == null ? "" : msg);
+            } else {
+                return msg;
+            }
+        }
+
+    // Calls the methods defined on LogProducer and verify the
+    // parameters received by the underlying TestLoggerFinder.LoggerImpl
+    // logger.
+    private static void testLogger(TestLoggerFinder provider,
+            Map<Object, String> loggerDescMap,
+            String name,
+            ResourceBundle loggerBundle,
+            PlatformLogger.Bridge logger,
+            TestLoggerFinder.LoggerImpl sink) {
+
+        if (loggerDescMap.get(logger) == null) {
+            throw new RuntimeException("Test bug: Missing description");
+        }
+        System.out.println("Testing " + loggerDescMap.get(logger) +" [" + logger + "]");
+
+        Foo foo = new Foo();
+        String fooMsg = foo.toString();
+        System.out.println("\tlogger.log(messageLevel, fooMsg)");
+        System.out.println("\tlogger.<level>(fooMsg)");
+        for (Level loggerLevel : Level.values()) {
+            sink.level = loggerLevel;
+            for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
+                String desc = "logger.log(messageLevel, fooMsg): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                Level expectedMessageLevel = julToSpiMap.get(messageLevel);
+                TestLoggerFinder.LogEvent expected =
+                        TestLoggerFinder.LogEvent.of(
+                            sequencer.get(),
+                            loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
+                            name, expectedMessageLevel, loggerBundle,
+                            fooMsg, null, (Throwable)null, (Object[])null);
+                logger.log(messageLevel, fooMsg);
+                checkLogEvent(provider, desc, expected);
+            }
+        }
+
+        Supplier<String> supplier = new Supplier<String>() {
+            @Override
+            public String get() {
+                return this.toString();
+            }
+        };
+        System.out.println("\tlogger.log(messageLevel, supplier)");
+        System.out.println("\tlogger.<level>(supplier)");
+        for (Level loggerLevel : Level.values()) {
+            sink.level = loggerLevel;
+            for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
+                String desc = "logger.log(messageLevel, supplier): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                Level expectedMessageLevel = julToSpiMap.get(messageLevel);
+                TestLoggerFinder.LogEvent expected =
+                        TestLoggerFinder.LogEvent.of(
+                            sequencer.get(),
+                            loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
+                            name, expectedMessageLevel, (ResourceBundle) null,
+                            null, supplier, (Throwable)null, (Object[])null);
+                logger.log(messageLevel, supplier);
+                checkLogEvent(provider, desc, expected);
+            }
+        }
+
+        String format = "two params [{1} {2}]";
+        Object arg1 = foo;
+        Object arg2 = fooMsg;
+        System.out.println("\tlogger.log(messageLevel, format, arg1, arg2)");
+        for (Level loggerLevel : Level.values()) {
+            sink.level = loggerLevel;
+            for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
+                String desc = "logger.log(messageLevel, format, foo, fooMsg): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                Level expectedMessageLevel = julToSpiMap.get(messageLevel);
+                TestLoggerFinder.LogEvent expected =
+                        TestLoggerFinder.LogEvent.of(
+                            sequencer.get(),
+                            loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
+                            name, expectedMessageLevel, loggerBundle,
+                            format, null, (Throwable)null, arg1, arg2);
+                logger.log(messageLevel, format, arg1, arg2);
+                checkLogEvent(provider, desc, expected);
+            }
+        }
+
+        Throwable thrown = new Exception("OK: log me!");
+        System.out.println("\tlogger.log(messageLevel, fooMsg, thrown)");
+        for (Level loggerLevel : Level.values()) {
+            sink.level = loggerLevel;
+            for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
+                String desc = "logger.log(messageLevel, fooMsg, thrown): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                Level expectedMessageLevel = julToSpiMap.get(messageLevel);
+                TestLoggerFinder.LogEvent expected =
+                        TestLoggerFinder.LogEvent.of(
+                            sequencer.get(),
+                            loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
+                            name, expectedMessageLevel, loggerBundle,
+                            fooMsg, null, thrown, (Object[])null);
+                logger.log(messageLevel, fooMsg, thrown);
+                checkLogEvent(provider, desc, expected);
+            }
+        }
+
+        System.out.println("\tlogger.log(messageLevel, thrown, supplier)");
+        for (Level loggerLevel : Level.values()) {
+            sink.level = loggerLevel;
+            for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
+                String desc = "logger.log(messageLevel, thrown, supplier): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                Level expectedMessageLevel = julToSpiMap.get(messageLevel);
+                TestLoggerFinder.LogEvent expected =
+                        TestLoggerFinder.LogEvent.of(
+                            sequencer.get(),
+                            loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
+                            name, expectedMessageLevel, (ResourceBundle)null,
+                            null, supplier, thrown, (Object[])null);
+                logger.log(messageLevel, thrown, supplier);
+                checkLogEvent(provider, desc, expected);
+            }
+        }
+
+        String sourceClass = "blah.Blah";
+        String sourceMethod = "blih";
+        System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, fooMsg)");
+        for (Level loggerLevel : Level.values()) {
+            sink.level = loggerLevel;
+            for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
+                String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, fooMsg): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                Level expectedMessageLevel = julToSpiMap.get(messageLevel);
+                boolean isLoggable = loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0;
+                TestLoggerFinder.LogEvent expected =
+                    isLoggable || loggerBundle != null && BEST_EFFORT_FOR_LOGP?
+                        TestLoggerFinder.LogEvent.of(
+                            sequencer.get(),
+                            isLoggable,
+                            name, expectedMessageLevel, loggerBundle,
+                            logpMessage(loggerBundle, sourceClass, sourceMethod, fooMsg),
+                            null, (Throwable)null, (Object[]) null) : null;
+                logger.logp(messageLevel, sourceClass, sourceMethod, fooMsg);
+                checkLogEvent(provider, desc, expected);
+            }
+        }
+
+        System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, supplier)");
+        for (Level loggerLevel : Level.values()) {
+            sink.level = loggerLevel;
+            for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
+                String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, supplier): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                Level expectedMessageLevel = julToSpiMap.get(messageLevel);
+                boolean isLoggable = loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0;
+                TestLoggerFinder.LogEvent expected = isLoggable ?
+                    TestLoggerFinder.LogEvent.ofp(BEST_EFFORT_FOR_LOGP,
+                        TestLoggerFinder.LogEvent.of(
+                            sequencer.get(),
+                            isLoggable,
+                            name, expectedMessageLevel, null, null,
+                            logpMessage(null, sourceClass, sourceMethod, supplier),
+                            (Throwable)null, (Object[]) null)) : null;
+                logger.logp(messageLevel, sourceClass, sourceMethod, supplier);
+                checkLogEvent(provider, desc, expected);
+            }
+        }
+
+        System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, format, arg1, arg2)");
+        for (Level loggerLevel : Level.values()) {
+            sink.level = loggerLevel;
+            for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
+                String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, format, arg1, arg2): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                Level expectedMessageLevel = julToSpiMap.get(messageLevel);
+                boolean isLoggable = loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0;
+                TestLoggerFinder.LogEvent expected =
+                    isLoggable || loggerBundle != null && BEST_EFFORT_FOR_LOGP?
+                        TestLoggerFinder.LogEvent.of(
+                            sequencer.get(),
+                            loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
+                            name, expectedMessageLevel, loggerBundle,
+                            logpMessage(loggerBundle, sourceClass, sourceMethod, format),
+                            null, (Throwable)null, arg1, arg2) : null;
+                logger.logp(messageLevel, sourceClass, sourceMethod, format, arg1, arg2);
+                checkLogEvent(provider, desc, expected);
+            }
+        }
+
+        System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, fooMsg, thrown)");
+        for (Level loggerLevel : Level.values()) {
+            sink.level = loggerLevel;
+            for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
+                String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, fooMsg, thrown): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                Level expectedMessageLevel = julToSpiMap.get(messageLevel);
+                boolean isLoggable = loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0;
+                TestLoggerFinder.LogEvent expected =
+                    isLoggable || loggerBundle != null && BEST_EFFORT_FOR_LOGP ?
+                        TestLoggerFinder.LogEvent.of(
+                            sequencer.get(),
+                            loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
+                            name, expectedMessageLevel, loggerBundle,
+                            logpMessage(loggerBundle, sourceClass, sourceMethod, fooMsg),
+                            null, thrown, (Object[])null) : null;
+                logger.logp(messageLevel, sourceClass, sourceMethod, fooMsg, thrown);
+                checkLogEvent(provider, desc, expected);
+            }
+        }
+
+        System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, thrown, supplier)");
+        for (Level loggerLevel : Level.values()) {
+            sink.level = loggerLevel;
+            for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
+                String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, thrown, supplier): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                Level expectedMessageLevel = julToSpiMap.get(messageLevel);
+                boolean isLoggable = loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0;
+                TestLoggerFinder.LogEvent expected = isLoggable ?
+                    TestLoggerFinder.LogEvent.ofp(BEST_EFFORT_FOR_LOGP,
+                        TestLoggerFinder.LogEvent.of(
+                            sequencer.get(),
+                            loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
+                            name, expectedMessageLevel, null, null,
+                            logpMessage(null, sourceClass, sourceMethod, supplier),
+                            thrown, (Object[])null)) : null;
+                logger.logp(messageLevel, sourceClass, sourceMethod, thrown, supplier);
+                checkLogEvent(provider, desc, expected);
+            }
+        }
+
+        ResourceBundle bundle = ResourceBundle.getBundle(MyBundle.class.getName());
+        System.out.println("\tlogger.logrb(messageLevel, bundle, format, arg1, arg2)");
+        for (Level loggerLevel : Level.values()) {
+            sink.level = loggerLevel;
+            for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
+                String desc = "logger.logrb(messageLevel, bundle, format, arg1, arg2): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                Level expectedMessageLevel = julToSpiMap.get(messageLevel);
+                TestLoggerFinder.LogEvent expected =
+                        TestLoggerFinder.LogEvent.of(
+                            sequencer.get(),
+                            loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
+                            name, expectedMessageLevel, bundle,
+                            format, null, (Throwable)null, arg1, arg2);
+                logger.logrb(messageLevel, bundle, format, arg1, arg2);
+                checkLogEvent(provider, desc, expected);
+            }
+        }
+
+        System.out.println("\tlogger.logrb(messageLevel, bundle, msg, thrown)");
+        for (Level loggerLevel : Level.values()) {
+            sink.level = loggerLevel;
+            for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
+                String desc = "logger.logrb(messageLevel, bundle, msg, thrown): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                Level expectedMessageLevel = julToSpiMap.get(messageLevel);
+                TestLoggerFinder.LogEvent expected =
+                        TestLoggerFinder.LogEvent.of(
+                            sequencer.get(),
+                            loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
+                            name, expectedMessageLevel, bundle,
+                            fooMsg, null, thrown, (Object[])null);
+                logger.logrb(messageLevel, bundle, fooMsg, thrown);
+                checkLogEvent(provider, desc, expected);
+            }
+        }
+
+        System.out.println("\tlogger.logrb(messageLevel, sourceClass, sourceMethod, bundle, format, arg1, arg2)");
+        for (Level loggerLevel : Level.values()) {
+            sink.level = loggerLevel;
+            for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
+                String desc = "logger.logrb(messageLevel, sourceClass, sourceMethod, bundle, format, arg1, arg2): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                Level expectedMessageLevel = julToSpiMap.get(messageLevel);
+                TestLoggerFinder.LogEvent expected =
+                        TestLoggerFinder.LogEvent.of(
+                            sequencer.get(),
+                            loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
+                            name, expectedMessageLevel, bundle,
+                            format, null, (Throwable)null, arg1, arg2);
+                logger.logrb(messageLevel, sourceClass, sourceMethod, bundle, format, arg1, arg2);
+                checkLogEvent(provider, desc, expected);
+            }
+        }
+
+        System.out.println("\tlogger.logrb(messageLevel, sourceClass, sourceMethod, bundle, msg, thrown)");
+        for (Level loggerLevel : Level.values()) {
+            sink.level = loggerLevel;
+            for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
+                String desc = "logger.logrb(messageLevel, sourceClass, sourceMethod, bundle, msg, thrown): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                Level expectedMessageLevel = julToSpiMap.get(messageLevel);
+                TestLoggerFinder.LogEvent expected =
+                        TestLoggerFinder.LogEvent.of(
+                            sequencer.get(),
+                            loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
+                            name, expectedMessageLevel, bundle,
+                            fooMsg, null, thrown, (Object[])null);
+                logger.logrb(messageLevel, sourceClass, sourceMethod, bundle, fooMsg, thrown);
+                checkLogEvent(provider, desc, expected);
+            }
+        }
+    }
+
+    final static class PermissionsBuilder {
+        final Permissions perms;
+        public PermissionsBuilder() {
+            this(new Permissions());
+        }
+        public PermissionsBuilder(Permissions perms) {
+            this.perms = perms;
+        }
+        public PermissionsBuilder add(Permission p) {
+            perms.add(p);
+            return this;
+        }
+        public PermissionsBuilder addAll(PermissionCollection col) {
+            if (col != null) {
+                for (Enumeration<Permission> e = col.elements(); e.hasMoreElements(); ) {
+                    perms.add(e.nextElement());
+                }
+            }
+            return this;
+        }
+        public Permissions toPermissions() {
+            final PermissionsBuilder builder = new PermissionsBuilder();
+            builder.addAll(perms);
+            return builder.perms;
+        }
+    }
+
+    public static class SimplePolicy extends Policy {
+        final static RuntimePermission CONTROL = LOGGERFINDER_PERMISSION;
+        final static RuntimePermission ACCESS_LOGGER = new RuntimePermission("accessClassInPackage.jdk.internal.logger");
+        final static RuntimePermission ACCESS_LOGGING = new RuntimePermission("accessClassInPackage.sun.util.logging");
+
+        final Permissions permissions;
+        final Permissions allPermissions;
+        final ThreadLocal<AtomicBoolean> allowControl;
+        final ThreadLocal<AtomicBoolean> allowAccess;
+        final ThreadLocal<AtomicBoolean> allowAll;
+        public SimplePolicy(ThreadLocal<AtomicBoolean> allowControl,
+                ThreadLocal<AtomicBoolean> allowAccess,
+                ThreadLocal<AtomicBoolean> allowAll) {
+            this.allowControl = allowControl;
+            this.allowAccess = allowAccess;
+            this.allowAll = allowAll;
+            permissions = new Permissions();
+            allPermissions = new PermissionsBuilder()
+                    .add(new java.security.AllPermission())
+                    .toPermissions();
+        }
+
+        Permissions getPermissions() {
+            if (allowControl.get().get() || allowAccess.get().get() || allowAll.get().get()) {
+                PermissionsBuilder builder =  new PermissionsBuilder()
+                        .addAll(permissions);
+                if (allowControl.get().get()) {
+                    builder.add(CONTROL);
+                }
+                if (allowAccess.get().get()) {
+                    builder.add(ACCESS_LOGGER);
+                    builder.add(ACCESS_LOGGING);
+                }
+                if (allowAll.get().get()) {
+                    builder.addAll(allPermissions);
+                }
+                return builder.toPermissions();
+            }
+            return permissions;
+        }
+
+        @Override
+        public boolean implies(ProtectionDomain domain, Permission permission) {
+            return getPermissions().implies(permission);
+        }
+
+        @Override
+        public PermissionCollection getPermissions(CodeSource codesource) {
+            return new PermissionsBuilder().addAll(getPermissions()).toPermissions();
+        }
+
+        @Override
+        public PermissionCollection getPermissions(ProtectionDomain domain) {
+            return new PermissionsBuilder().addAll(getPermissions()).toPermissions();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/LoggerFinder/internal/BaseLoggerBridgeTest/CustomSystemClassLoader.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.nio.file.Files;
+import java.security.AllPermission;
+import java.security.Permissions;
+import java.security.ProtectionDomain;
+
+
+/**
+ * A custom ClassLoader to load the concrete LoggerFinder class
+ * with all permissions.
+ *
+ * @author danielfuchs
+ */
+public class CustomSystemClassLoader extends ClassLoader {
+
+
+    Class<?> finderClass = null;
+
+    public CustomSystemClassLoader() {
+        super();
+    }
+    public CustomSystemClassLoader(ClassLoader parent) {
+        super(parent);
+    }
+
+    private Class<?> defineFinderClass(String name)
+        throws ClassNotFoundException {
+        final Object obj = getClassLoadingLock(name);
+        synchronized(obj) {
+            if (finderClass != null) return finderClass;
+
+            URL url = this.getClass().getProtectionDomain().getCodeSource().getLocation();
+            File file = new File(url.getPath(), name+".class");
+            if (file.canRead()) {
+                try {
+                    byte[] b = Files.readAllBytes(file.toPath());
+                    Permissions perms = new Permissions();
+                    perms.add(new AllPermission());
+                    finderClass = defineClass(
+                            name, b, 0, b.length, new ProtectionDomain(
+                            this.getClass().getProtectionDomain().getCodeSource(),
+                            perms));
+                    System.out.println("Loaded " + name);
+                    return finderClass;
+                } catch (Throwable ex) {
+                    ex.printStackTrace();
+                    throw new ClassNotFoundException(name, ex);
+                }
+            } else {
+                throw new ClassNotFoundException(name,
+                        new IOException(file.toPath() + ": can't read"));
+            }
+        }
+    }
+
+    @Override
+    public synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
+        if (name.endsWith("$BaseLoggerFinder")) {
+            Class<?> c = defineFinderClass(name);
+            if (resolve) {
+                resolveClass(c);
+            }
+            return c;
+        }
+        return super.loadClass(name, resolve);
+    }
+
+    @Override
+    protected Class<?> findClass(String name) throws ClassNotFoundException {
+        if (name.endsWith("$BaseLoggerFinder")) {
+            return defineFinderClass(name);
+        }
+        return super.findClass(name);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/LoggerFinder/internal/BaseLoggerBridgeTest/META-INF/services/java.lang.System$LoggerFinder	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,1 @@
+BaseLoggerBridgeTest$BaseLoggerFinder
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/LoggerFinder/internal/BasePlatformLoggerTest/BasePlatformLoggerTest.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,732 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+import java.security.AccessController;
+import java.security.CodeSource;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.Permissions;
+import java.security.Policy;
+import java.security.PrivilegedAction;
+import java.security.ProtectionDomain;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Queue;
+import java.util.ResourceBundle;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.Supplier;
+import java.lang.System.LoggerFinder;
+import java.lang.System.Logger;
+import java.lang.System.Logger.Level;
+import java.security.AccessControlException;
+import java.util.stream.Stream;
+import sun.util.logging.PlatformLogger;
+
+/**
+ * @test
+ * @bug     8140364
+ * @summary JDK implementation specific unit test for JDK internal API.
+ *   Tests a naive implementation of System.Logger, and in particular
+ *   the default mapping provided by PlatformLogger.
+ * @modules java.base/sun.util.logging
+ * @build CustomSystemClassLoader BasePlatformLoggerTest
+ * @run  main/othervm -Djava.system.class.loader=CustomSystemClassLoader BasePlatformLoggerTest NOSECURITY
+ * @run  main/othervm -Djava.system.class.loader=CustomSystemClassLoader BasePlatformLoggerTest NOPERMISSIONS
+ * @run  main/othervm -Djava.system.class.loader=CustomSystemClassLoader BasePlatformLoggerTest WITHPERMISSIONS
+ * @author danielfuchs
+ */
+public class BasePlatformLoggerTest {
+
+    public static final RuntimePermission LOGGERFINDER_PERMISSION =
+                new RuntimePermission("loggerFinder");
+
+    final static AtomicLong sequencer = new AtomicLong();
+    final static boolean VERBOSE = false;
+    static final ThreadLocal<AtomicBoolean> allowControl = new ThreadLocal<AtomicBoolean>() {
+        @Override
+        protected AtomicBoolean initialValue() {
+            return  new AtomicBoolean(false);
+        }
+    };
+    static final ThreadLocal<AtomicBoolean> allowAccess = new ThreadLocal<AtomicBoolean>() {
+        @Override
+        protected AtomicBoolean initialValue() {
+            return  new AtomicBoolean(false);
+        }
+    };
+    static final ThreadLocal<AtomicBoolean> allowAll = new ThreadLocal<AtomicBoolean>() {
+        @Override
+        protected AtomicBoolean initialValue() {
+            return  new AtomicBoolean(false);
+        }
+    };
+
+    static final Class<?> providerClass;
+    static {
+        try {
+            providerClass = ClassLoader.getSystemClassLoader().loadClass("BasePlatformLoggerTest$BaseLoggerFinder");
+        } catch (ClassNotFoundException ex) {
+            throw new ExceptionInInitializerError(ex);
+        }
+    }
+
+    static final PlatformLogger.Level[] julLevels = {
+        PlatformLogger.Level.ALL,
+        PlatformLogger.Level.FINEST,
+        PlatformLogger.Level.FINER,
+        PlatformLogger.Level.FINE,
+        PlatformLogger.Level.CONFIG,
+        PlatformLogger.Level.INFO,
+        PlatformLogger.Level.WARNING,
+        PlatformLogger.Level.SEVERE,
+        PlatformLogger.Level.OFF,
+    };
+
+    static final Level[] mappedLevels = {
+        Level.ALL,     // ALL
+        Level.TRACE,   // FINEST
+        Level.TRACE,   // FINER
+        Level.DEBUG,   // FINE
+        Level.DEBUG,   // CONFIG
+        Level.INFO,    // INFO
+        Level.WARNING, // WARNING
+        Level.ERROR,   // SEVERE
+        Level.OFF,     // OFF
+    };
+
+    final static Map<PlatformLogger.Level, Level> julToSpiMap;
+    static {
+        Map<PlatformLogger.Level, Level> map = new HashMap<>();
+        if (mappedLevels.length != julLevels.length) {
+            throw new ExceptionInInitializerError("Array lengths differ"
+                + "\n\tjulLevels=" + Arrays.deepToString(julLevels)
+                + "\n\tmappedLevels=" + Arrays.deepToString(mappedLevels));
+        }
+        for (int i=0; i<julLevels.length; i++) {
+            map.put(julLevels[i], mappedLevels[i]);
+        }
+        julToSpiMap = Collections.unmodifiableMap(map);
+    }
+
+    public static class MyBundle extends ResourceBundle {
+
+        final ConcurrentHashMap<String,String> map = new ConcurrentHashMap<>();
+
+        @Override
+        protected Object handleGetObject(String key) {
+            if (key.contains(" (translated)")) {
+                throw new RuntimeException("Unexpected key: " + key);
+            }
+            return map.computeIfAbsent(key, k -> k + " (translated)");
+        }
+
+        @Override
+        public Enumeration<String> getKeys() {
+            return Collections.enumeration(map.keySet());
+        }
+
+    }
+    public static class MyLoggerBundle extends MyBundle {
+
+    }
+
+
+    public static interface TestLoggerFinder  {
+        final ConcurrentHashMap<String, LoggerImpl> system = new ConcurrentHashMap<>();
+        final ConcurrentHashMap<String, LoggerImpl> user = new ConcurrentHashMap<>();
+        public Queue<LogEvent> eventQueue = new ArrayBlockingQueue<>(128);
+
+        public static final class LogEvent implements Cloneable {
+
+            public LogEvent() {
+                this(sequencer.getAndIncrement());
+            }
+
+            LogEvent(long sequenceNumber) {
+                this.sequenceNumber = sequenceNumber;
+            }
+
+            long sequenceNumber;
+            boolean isLoggable;
+            String loggerName;
+            Level level;
+            ResourceBundle bundle;
+            Throwable thrown;
+            Object[] args;
+            Supplier<String> supplier;
+            String msg;
+
+            Object[] toArray() {
+                return new Object[] {
+                    sequenceNumber,
+                    isLoggable,
+                    loggerName,
+                    level,
+                    bundle,
+                    thrown,
+                    args,
+                    supplier,
+                    msg,
+                };
+            }
+
+            @Override
+            public String toString() {
+                return Arrays.deepToString(toArray());
+            }
+
+
+
+            @Override
+            public boolean equals(Object obj) {
+                return obj instanceof LogEvent
+                        && Objects.deepEquals(this.toArray(), ((LogEvent)obj).toArray());
+            }
+
+            @Override
+            public int hashCode() {
+                return Objects.hash(toArray());
+            }
+
+            public LogEvent cloneWith(long sequenceNumber)
+                    throws CloneNotSupportedException {
+                LogEvent cloned = (LogEvent)super.clone();
+                cloned.sequenceNumber = sequenceNumber;
+                return cloned;
+            }
+
+            public static LogEvent of(boolean isLoggable, String name,
+                    Level level, ResourceBundle bundle,
+                    String key, Throwable thrown) {
+                LogEvent evt = new LogEvent();
+                evt.isLoggable = isLoggable;
+                evt.loggerName = name;
+                evt.level = level;
+                evt.args = null;
+                evt.bundle = bundle;
+                evt.thrown = thrown;
+                evt.supplier = null;
+                evt.msg = key;
+                return evt;
+            }
+
+            public static LogEvent of(boolean isLoggable, String name,
+                    Level level, Throwable thrown, Supplier<String> supplier) {
+                LogEvent evt = new LogEvent();
+                evt.isLoggable = isLoggable;
+                evt.loggerName = name;
+                evt.level = level;
+                evt.args = null;
+                evt.bundle = null;
+                evt.thrown = thrown;
+                evt.supplier = supplier;
+                evt.msg = null;
+                return evt;
+            }
+
+            public static LogEvent of(boolean isLoggable, String name,
+                    Level level, ResourceBundle bundle,
+                    String key, Object... params) {
+                LogEvent evt = new LogEvent();
+                evt.isLoggable = isLoggable;
+                evt.loggerName = name;
+                evt.level = level;
+                evt.args = params;
+                evt.bundle = bundle;
+                evt.thrown = null;
+                evt.supplier = null;
+                evt.msg = key;
+                return evt;
+            }
+
+            public static LogEvent of(long sequenceNumber,
+                    boolean isLoggable, String name,
+                    Level level, ResourceBundle bundle,
+                    String key, Supplier<String> supplier,
+                    Throwable thrown, Object... params) {
+                LogEvent evt = new LogEvent(sequenceNumber);
+                evt.loggerName = name;
+                evt.level = level;
+                evt.args = params;
+                evt.bundle = bundle;
+                evt.thrown = thrown;
+                evt.supplier = supplier;
+                evt.msg = key;
+                evt.isLoggable = isLoggable;
+                return evt;
+            }
+
+        }
+
+        public class LoggerImpl implements Logger {
+            private final String name;
+            private Level level = Level.INFO;
+
+            public LoggerImpl(String name) {
+                this.name = name;
+            }
+
+            @Override
+            public String getName() {
+                return name;
+            }
+
+            @Override
+            public boolean isLoggable(Level level) {
+                return this.level != Level.OFF && this.level.getSeverity() <= level.getSeverity();
+            }
+
+            @Override
+            public void log(Level level, ResourceBundle bundle, String key, Throwable thrown) {
+                log(LogEvent.of(isLoggable(level), this.name, level, bundle, key, thrown));
+            }
+
+            @Override
+            public void log(Level level, ResourceBundle bundle, String format, Object... params) {
+                log(LogEvent.of(isLoggable(level), name, level, bundle, format, params));
+            }
+
+            void log(LogEvent event) {
+                eventQueue.add(event);
+            }
+
+            @Override
+            public void log(Level level, Supplier<String> msgSupplier) {
+                log(LogEvent.of(isLoggable(level), name, level, null, msgSupplier));
+            }
+
+            @Override
+            public void log(Level level,  Supplier<String> msgSupplier, Throwable thrown) {
+                log(LogEvent.of(isLoggable(level), name, level, thrown, msgSupplier));
+            }
+        }
+
+        public Logger getLogger(String name, Class<?> caller);
+    }
+
+    public static class BaseLoggerFinder extends LoggerFinder implements TestLoggerFinder {
+        @Override
+        public Logger getLogger(String name, Class<?> caller) {
+            SecurityManager sm = System.getSecurityManager();
+            if (sm != null) {
+                sm.checkPermission(LOGGERFINDER_PERMISSION);
+            }
+            PrivilegedAction<ClassLoader> pa = () -> caller.getClassLoader();
+            ClassLoader callerLoader = AccessController.doPrivileged(pa);
+            if (callerLoader == null) {
+                return system.computeIfAbsent(name, (n) -> new LoggerImpl(n));
+            } else {
+                return user.computeIfAbsent(name, (n) -> new LoggerImpl(n));
+            }
+        }
+    }
+
+    static PlatformLogger getPlatformLogger(String name) {
+        boolean old = allowAccess.get().get();
+        allowAccess.get().set(true);
+        try {
+            return PlatformLogger.getLogger(name);
+        } finally {
+            allowAccess.get().set(old);
+        }
+    }
+
+    static enum TestCases {NOSECURITY, NOPERMISSIONS, WITHPERMISSIONS};
+
+    static void setSecurityManager() {
+        if (System.getSecurityManager() == null) {
+            Policy.setPolicy(new SimplePolicy(allowControl, allowAccess, allowAll));
+            System.setSecurityManager(new SecurityManager());
+        }
+    }
+
+    public static void main(String[] args) {
+        if (args.length == 0)
+            args = new String[] {
+                "NOSECURITY",
+                "NOPERMISSIONS",
+                "WITHPERMISSIONS"
+            };
+
+
+        Stream.of(args).map(TestCases::valueOf).forEach((testCase) -> {
+            TestLoggerFinder provider;
+            switch (testCase) {
+                case NOSECURITY:
+                    System.out.println("\n*** Without Security Manager\n");
+                    provider = TestLoggerFinder.class.cast(LoggerFinder.getLoggerFinder());
+                    test(provider, true);
+                    System.out.println("Tetscase count: " + sequencer.get());
+                    break;
+                case NOPERMISSIONS:
+                    System.out.println("\n*** With Security Manager, without permissions\n");
+                    setSecurityManager();
+                    try {
+                        provider = TestLoggerFinder.class.cast(LoggerFinder.getLoggerFinder());
+                        throw new RuntimeException("Expected exception not raised");
+                    } catch (AccessControlException x) {
+                        if (!LOGGERFINDER_PERMISSION.equals(x.getPermission())) {
+                            throw new RuntimeException("Unexpected permission check", x);
+                        }
+                        final boolean control = allowControl.get().get();
+                        try {
+                            allowControl.get().set(true);
+                            provider = TestLoggerFinder.class.cast(LoggerFinder.getLoggerFinder());
+                        } finally {
+                            allowControl.get().set(control);
+                        }
+                    }
+                    test(provider, false);
+                    System.out.println("Tetscase count: " + sequencer.get());
+                    break;
+                case WITHPERMISSIONS:
+                    System.out.println("\n*** With Security Manager, with control permission\n");
+                    setSecurityManager();
+                    final boolean control = allowControl.get().get();
+                    try {
+                        allowControl.get().set(true);
+                        provider = TestLoggerFinder.class.cast(LoggerFinder.getLoggerFinder());
+                        test(provider, true);
+                    } finally {
+                        allowControl.get().set(control);
+                    }
+                    break;
+                default:
+                    throw new RuntimeException("Unknown test case: " + testCase);
+            }
+        });
+        System.out.println("\nPASSED: Tested " + sequencer.get() + " cases.");
+    }
+
+    public static void test(TestLoggerFinder provider, boolean hasRequiredPermissions) {
+
+        final Map<PlatformLogger, String> loggerDescMap = new HashMap<>();
+
+        TestLoggerFinder.LoggerImpl appSink;
+        boolean before = allowControl.get().get();
+        try {
+            allowControl.get().set(true);
+            appSink = TestLoggerFinder.LoggerImpl.class.cast(
+                        provider.getLogger("foo", BasePlatformLoggerTest.class));
+        } finally {
+            allowControl.get().set(before);
+        }
+
+        TestLoggerFinder.LoggerImpl sysSink = null;
+        before = allowControl.get().get();
+        try {
+            allowControl.get().set(true);
+            sysSink = TestLoggerFinder.LoggerImpl.class.cast(provider.getLogger("foo", Thread.class));
+        } finally {
+            allowControl.get().set(before);
+        }
+
+        if (hasRequiredPermissions && appSink == sysSink) {
+            throw new RuntimeException("identical loggers");
+        }
+
+        if (provider.system.contains(appSink)) {
+            throw new RuntimeException("app logger in system map");
+        }
+        if (!provider.user.contains(appSink)) {
+            throw new RuntimeException("app logger not in appplication map");
+        }
+        if (hasRequiredPermissions && provider.user.contains(sysSink)) {
+            throw new RuntimeException("sys logger in appplication map");
+        }
+        if (hasRequiredPermissions && !provider.system.contains(sysSink)) {
+            throw new RuntimeException("sys logger not in system map");
+        }
+
+        PlatformLogger platform = getPlatformLogger("foo");
+        loggerDescMap.put(platform, "PlatformLogger.getLogger(\"foo\")");
+
+        testLogger(provider, loggerDescMap, "foo", null, platform, sysSink);
+    }
+
+    public static class Foo {
+
+    }
+
+    static void verbose(String msg) {
+       if (VERBOSE) {
+           System.out.println(msg);
+       }
+    }
+
+    static void checkLogEvent(TestLoggerFinder provider, String desc,
+            TestLoggerFinder.LogEvent expected) {
+        TestLoggerFinder.LogEvent actual =  provider.eventQueue.poll();
+        if (!expected.equals(actual)) {
+            throw new RuntimeException("mismatch for " + desc
+                    + "\n\texpected=" + expected
+                    + "\n\t  actual=" + actual);
+        } else {
+            verbose("Got expected results for "
+                    + desc + "\n\t" + expected);
+        }
+    }
+
+    static void checkLogEvent(TestLoggerFinder provider, String desc,
+            TestLoggerFinder.LogEvent expected, boolean expectNotNull) {
+        TestLoggerFinder.LogEvent actual =  provider.eventQueue.poll();
+        if (actual == null && !expectNotNull) return;
+        if (actual != null && !expectNotNull) {
+            throw new RuntimeException("Unexpected log event found for " + desc
+                + "\n\tgot: " + actual);
+        }
+        if (!expected.equals(actual)) {
+            throw new RuntimeException("mismatch for " + desc
+                    + "\n\texpected=" + expected
+                    + "\n\t  actual=" + actual);
+        } else {
+            verbose("Got expected results for "
+                    + desc + "\n\t" + expected);
+        }
+    }
+
+    // Calls the methods defined on LogProducer and verify the
+    // parameters received by the underlying TestLoggerFinder.LoggerImpl
+    // logger.
+    private static void testLogger(TestLoggerFinder provider,
+            Map<PlatformLogger, String> loggerDescMap,
+            String name,
+            ResourceBundle loggerBundle,
+            PlatformLogger logger,
+            TestLoggerFinder.LoggerImpl sink) {
+
+        System.out.println("Testing " + loggerDescMap.get(logger));
+
+        Foo foo = new Foo();
+        String fooMsg = foo.toString();
+        System.out.println("\tlogger.<level>(fooMsg)");
+        for (Level loggerLevel : Level.values()) {
+            sink.level = loggerLevel;
+            for (PlatformLogger.Level messageLevel :julLevels) {
+                Level expectedMessageLevel = julToSpiMap.get(messageLevel);
+                TestLoggerFinder.LogEvent expected =
+                        TestLoggerFinder.LogEvent.of(
+                            sequencer.get(),
+                            loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
+                            name, expectedMessageLevel, loggerBundle,
+                            fooMsg, null, (Throwable)null, (Object[])null);
+                String desc2 = "logger." + messageLevel.toString().toLowerCase()
+                        + "(fooMsg): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                if (messageLevel == PlatformLogger.Level.FINEST) {
+                    logger.finest(fooMsg);
+                    checkLogEvent(provider, desc2, expected);
+                } else if (messageLevel == PlatformLogger.Level.FINER) {
+                    logger.finer(fooMsg);
+                    checkLogEvent(provider, desc2, expected);
+                } else if (messageLevel == PlatformLogger.Level.FINE) {
+                    logger.fine(fooMsg);
+                    checkLogEvent(provider, desc2, expected);
+                } else if (messageLevel == PlatformLogger.Level.CONFIG) {
+                    logger.config(fooMsg);
+                    checkLogEvent(provider, desc2, expected);
+                } else if (messageLevel == PlatformLogger.Level.INFO) {
+                    logger.info(fooMsg);
+                    checkLogEvent(provider, desc2, expected);
+                } else if (messageLevel == PlatformLogger.Level.WARNING) {
+                    logger.warning(fooMsg);
+                    checkLogEvent(provider, desc2, expected);
+                } else if (messageLevel == PlatformLogger.Level.SEVERE) {
+                    logger.severe(fooMsg);
+                    checkLogEvent(provider, desc2, expected);
+                }
+            }
+        }
+
+        Throwable thrown = new Exception("OK: log me!");
+        System.out.println("\tlogger.<level>(msg, thrown)");
+        for (Level loggerLevel : Level.values()) {
+            sink.level = loggerLevel;
+            for (PlatformLogger.Level messageLevel :julLevels) {
+                Level expectedMessageLevel = julToSpiMap.get(messageLevel);
+                TestLoggerFinder.LogEvent expected =
+                        TestLoggerFinder.LogEvent.of(
+                            sequencer.get(),
+                            loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
+                            name, expectedMessageLevel, (ResourceBundle) null,
+                            fooMsg, null, (Throwable)thrown, (Object[])null);
+                String desc2 = "logger." + messageLevel.toString().toLowerCase()
+                        + "(msg, thrown): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                if (messageLevel == PlatformLogger.Level.FINEST) {
+                    logger.finest(fooMsg, thrown);
+                    checkLogEvent(provider, desc2, expected);
+                } else if (messageLevel == PlatformLogger.Level.FINER) {
+                    logger.finer(fooMsg, thrown);
+                    checkLogEvent(provider, desc2, expected);
+                } else if (messageLevel == PlatformLogger.Level.FINE) {
+                    logger.fine(fooMsg, thrown);
+                    checkLogEvent(provider, desc2, expected);
+                } else if (messageLevel == PlatformLogger.Level.CONFIG) {
+                    logger.config(fooMsg, thrown);
+                    checkLogEvent(provider, desc2, expected);
+                } else if (messageLevel == PlatformLogger.Level.INFO) {
+                    logger.info(fooMsg, thrown);
+                    checkLogEvent(provider, desc2, expected);
+                } else if (messageLevel == PlatformLogger.Level.WARNING) {
+                    logger.warning(fooMsg, thrown);
+                    checkLogEvent(provider, desc2, expected);
+                } else if (messageLevel == PlatformLogger.Level.SEVERE) {
+                    logger.severe(fooMsg, thrown);
+                    checkLogEvent(provider, desc2, expected);
+                }
+            }
+        }
+
+        String format = "two params [{1} {2}]";
+        Object arg1 = foo;
+        Object arg2 = fooMsg;
+        System.out.println("\tlogger.<level>(format, arg1, arg2)");
+        for (Level loggerLevel : Level.values()) {
+            sink.level = loggerLevel;
+            for (PlatformLogger.Level messageLevel :julLevels) {
+                Level expectedMessageLevel = julToSpiMap.get(messageLevel);
+                TestLoggerFinder.LogEvent expected =
+                        TestLoggerFinder.LogEvent.of(
+                            sequencer.get(),
+                            loggerLevel != Level.OFF && expectedMessageLevel.compareTo(loggerLevel) >= 0,
+                            name, expectedMessageLevel, (ResourceBundle) null,
+                            format, null, (Throwable)null, foo, fooMsg);
+                String desc2 = "logger." + messageLevel.toString().toLowerCase()
+                        + "(format, foo, fooMsg): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                if (messageLevel == PlatformLogger.Level.FINEST) {
+                    logger.finest(format, foo, fooMsg);
+                    checkLogEvent(provider, desc2, expected);
+                } else if (messageLevel == PlatformLogger.Level.FINER) {
+                    logger.finer(format, foo, fooMsg);
+                    checkLogEvent(provider, desc2, expected);
+                } else if (messageLevel == PlatformLogger.Level.FINE) {
+                    logger.fine(format, foo, fooMsg);
+                    checkLogEvent(provider, desc2, expected);
+                } else if (messageLevel == PlatformLogger.Level.CONFIG) {
+                    logger.config(format, foo, fooMsg);
+                    checkLogEvent(provider, desc2, expected);
+                } else if (messageLevel == PlatformLogger.Level.INFO) {
+                    logger.info(format, foo, fooMsg);
+                    checkLogEvent(provider, desc2, expected);
+                } else if (messageLevel == PlatformLogger.Level.WARNING) {
+                    logger.warning(format, foo, fooMsg);
+                    checkLogEvent(provider, desc2, expected);
+                } else if (messageLevel == PlatformLogger.Level.SEVERE) {
+                    logger.severe(format, foo, fooMsg);
+                    checkLogEvent(provider, desc2, expected);
+                }
+            }
+        }
+
+    }
+
+    final static class PermissionsBuilder {
+        final Permissions perms;
+        public PermissionsBuilder() {
+            this(new Permissions());
+        }
+        public PermissionsBuilder(Permissions perms) {
+            this.perms = perms;
+        }
+        public PermissionsBuilder add(Permission p) {
+            perms.add(p);
+            return this;
+        }
+        public PermissionsBuilder addAll(PermissionCollection col) {
+            if (col != null) {
+                for (Enumeration<Permission> e = col.elements(); e.hasMoreElements(); ) {
+                    perms.add(e.nextElement());
+                }
+            }
+            return this;
+        }
+        public Permissions toPermissions() {
+            final PermissionsBuilder builder = new PermissionsBuilder();
+            builder.addAll(perms);
+            return builder.perms;
+        }
+    }
+
+    public static class SimplePolicy extends Policy {
+        final static RuntimePermission CONTROL = LOGGERFINDER_PERMISSION;
+        final static RuntimePermission ACCESS_LOGGING = new RuntimePermission("accessClassInPackage.sun.util.logging");
+
+        final Permissions permissions;
+        final Permissions allPermissions;
+        final ThreadLocal<AtomicBoolean> allowControl;
+        final ThreadLocal<AtomicBoolean> allowAccess;
+        final ThreadLocal<AtomicBoolean> allowAll;
+        public SimplePolicy(ThreadLocal<AtomicBoolean> allowControl,
+                ThreadLocal<AtomicBoolean> allowAccess,
+                ThreadLocal<AtomicBoolean> allowAll) {
+            this.allowControl = allowControl;
+            this.allowAccess = allowAccess;
+            this.allowAll = allowAll;
+            permissions = new Permissions();
+            allPermissions = new PermissionsBuilder()
+                    .add(new java.security.AllPermission())
+                    .toPermissions();
+        }
+
+        Permissions getPermissions() {
+            if (allowControl.get().get() || allowAccess.get().get() || allowAll.get().get()) {
+                PermissionsBuilder builder =  new PermissionsBuilder()
+                        .addAll(permissions);
+                if (allowControl.get().get()) {
+                    builder.add(CONTROL);
+                }
+                if (allowAccess.get().get()) {
+                    builder.add(ACCESS_LOGGING);
+                }
+                if (allowAll.get().get()) {
+                    builder.addAll(allPermissions);
+                }
+                return builder.toPermissions();
+            }
+            return permissions;
+        }
+
+        @Override
+        public boolean implies(ProtectionDomain domain, Permission permission) {
+            return getPermissions().implies(permission);
+        }
+
+        @Override
+        public PermissionCollection getPermissions(CodeSource codesource) {
+            return new PermissionsBuilder().addAll(getPermissions()).toPermissions();
+        }
+
+        @Override
+        public PermissionCollection getPermissions(ProtectionDomain domain) {
+            return new PermissionsBuilder().addAll(getPermissions()).toPermissions();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/LoggerFinder/internal/BasePlatformLoggerTest/CustomSystemClassLoader.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.nio.file.Files;
+import java.security.AllPermission;
+import java.security.Permissions;
+import java.security.ProtectionDomain;
+
+
+/**
+ * A custom ClassLoader to load the concrete LoggerFinder class
+ * with all permissions.
+ *
+ * @author danielfuchs
+ */
+public class CustomSystemClassLoader extends ClassLoader {
+
+
+    Class<?> finderClass = null;
+
+    public CustomSystemClassLoader() {
+        super();
+    }
+    public CustomSystemClassLoader(ClassLoader parent) {
+        super(parent);
+    }
+
+    private Class<?> defineFinderClass(String name)
+        throws ClassNotFoundException {
+        final Object obj = getClassLoadingLock(name);
+        synchronized(obj) {
+            if (finderClass != null) return finderClass;
+
+            URL url = this.getClass().getProtectionDomain().getCodeSource().getLocation();
+            File file = new File(url.getPath(), name+".class");
+            if (file.canRead()) {
+                try {
+                    byte[] b = Files.readAllBytes(file.toPath());
+                    Permissions perms = new Permissions();
+                    perms.add(new AllPermission());
+                    finderClass = defineClass(
+                            name, b, 0, b.length, new ProtectionDomain(
+                            this.getClass().getProtectionDomain().getCodeSource(),
+                            perms));
+                    System.out.println("Loaded " + name);
+                    return finderClass;
+                } catch (Throwable ex) {
+                    ex.printStackTrace();
+                    throw new ClassNotFoundException(name, ex);
+                }
+            } else {
+                throw new ClassNotFoundException(name,
+                        new IOException(file.toPath() + ": can't read"));
+            }
+        }
+    }
+
+    @Override
+    public synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
+        if (name.endsWith("$BaseLoggerFinder")) {
+            Class<?> c = defineFinderClass(name);
+            if (resolve) {
+                resolveClass(c);
+            }
+            return c;
+        }
+        return super.loadClass(name, resolve);
+    }
+
+    @Override
+    protected Class<?> findClass(String name) throws ClassNotFoundException {
+        if (name.endsWith("$BaseLoggerFinder")) {
+            return defineFinderClass(name);
+        }
+        return super.findClass(name);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/LoggerFinder/internal/BasePlatformLoggerTest/META-INF/services/java.lang.System$LoggerFinder	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,1 @@
+BasePlatformLoggerTest$BaseLoggerFinder
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/LoggerFinder/internal/BootstrapLogger/BootstrapLoggerTest.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,430 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.ByteArrayOutputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.lang.reflect.Array;
+import java.lang.reflect.Field;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.function.BooleanSupplier;
+import java.lang.System.Logger;
+import java.lang.System.Logger.Level;
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.security.AllPermission;
+import java.security.CodeSource;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.Permissions;
+import java.security.Policy;
+import java.security.ProtectionDomain;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import jdk.internal.logger.BootstrapLogger;
+import jdk.internal.logger.LazyLoggers;
+
+/*
+ * @test
+ * @bug     8140364
+ * @author  danielfuchs
+ * @summary JDK implementation specific unit test for JDK internal artifacts.
+            Tests the behavior of bootstrap loggers (and SimpleConsoleLoggers
+ *          too).
+ * @modules java.base/jdk.internal.logger
+ * @run main/othervm BootstrapLoggerTest NO_SECURITY
+ * @run main/othervm BootstrapLoggerTest SECURE
+ * @run main/othervm/timeout=120 BootstrapLoggerTest SECURE_AND_WAIT
+ */
+public class BootstrapLoggerTest {
+
+    static final Method awaitPending;
+    static final Method isAlive;
+    static final Field isBooted;
+    static final Field logManagerInitialized;
+    static {
+        try {
+            isBooted = BootstrapLogger.class.getDeclaredField("isBooted");
+            isBooted.setAccessible(true);
+            // private reflection hook that allows us to test wait until all
+            // the tasks pending in the BootstrapExecutor are finished.
+            awaitPending = BootstrapLogger.class
+                    .getDeclaredMethod("awaitPendingTasks");
+            awaitPending.setAccessible(true);
+            // private reflection hook that allows us to test whether
+            // the BootstrapExecutor is alive.
+            isAlive = BootstrapLogger.class
+                    .getDeclaredMethod("isAlive");
+            isAlive.setAccessible(true);
+            // private reflection hook that allows us to test whether the LogManager
+            // has initialized and registered with the BootstrapLogger class
+            logManagerInitialized = BootstrapLogger.class
+                    .getDeclaredField("logManagerConfigured");
+            logManagerInitialized.setAccessible(true);
+        } catch (Exception ex) {
+            throw new ExceptionInInitializerError(ex);
+        }
+    }
+
+    static void awaitPending() {
+        try {
+            awaitPending.invoke(null);
+        } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
+            ex.printStackTrace(LogStream.err);
+        }
+    }
+
+    /**
+     * We use an instance of this class to check what the logging system has
+     * printed on System.err.
+     */
+    public static class LogStream extends OutputStream {
+
+        final static PrintStream err = System.err;
+        final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+
+        public LogStream() {
+            super();
+        }
+
+        @Override
+        public synchronized void write(int b) {
+            baos.write(b);
+            err.write(b);
+        }
+
+        public String drain() {
+            awaitPending();
+            synchronized(this) {
+                String txt = baos.toString();
+                baos.reset();
+                return txt;
+            }
+        }
+    }
+
+    static enum TestCase {
+        NO_SECURITY, SECURE, SECURE_AND_WAIT
+    }
+
+    public static void main(String[] args) throws Exception {
+        if (args == null || args.length == 0) {
+            args = new String[] { TestCase.SECURE_AND_WAIT.name() };
+        }
+        if (args.length > 1) throw new RuntimeException("Only one argument allowed");
+        TestCase test = TestCase.valueOf(args[0]);
+        System.err.println("Testing: " + test);
+
+
+        // private reflection hook that allows us to simulate a non booted VM
+        final AtomicBoolean vmBooted = new AtomicBoolean(false);
+        isBooted.set(null,(BooleanSupplier) () -> vmBooted.get());
+
+        // We  replace System.err to check the messages that have been logged
+        // by the JUL ConsoleHandler and default SimpleConsoleLogger
+        // implementaion
+        final LogStream err = new LogStream();
+        System.setErr(new PrintStream(err));
+
+        if (BootstrapLogger.isBooted()) {
+            throw new RuntimeException("VM should not be booted!");
+        }
+        Logger logger = LazyLoggers.getLogger("foo.bar", Thread.class);
+
+        if (test != TestCase.NO_SECURITY) {
+            LogStream.err.println("Setting security manager");
+            Policy.setPolicy(new SimplePolicy());
+            System.setSecurityManager(new SecurityManager());
+        }
+
+        Level[] levels = {Level.INFO, Level.WARNING, Level.INFO};
+        int index = 0;
+        logger.log(levels[index], "Early message #" + (index+1)); index++;
+        logger.log(levels[index], "Early message #" + (index+1)); index++;
+        LogStream.err.println("VM Booted: " + vmBooted.get());
+        LogStream.err.println("LogManager initialized: " + logManagerInitialized.get(null));
+        logger.log(levels[index], "Early message #" + (index+1)); index++;
+        if (err.drain().contains("Early message")) {
+            // We're expecting that logger will be a LazyLogger wrapping a
+            // BootstrapLogger. The Bootstrap logger will stack the log messages
+            // it receives until the VM is booted.
+            // Since our private hook pretend that the VM is not booted yet,
+            // the logged messages shouldn't have reached System.err yet.
+            throw new RuntimeException("Early message logged while VM is not booted!");
+        }
+
+        // Now pretend that the VM is booted. Nothing should happen yet, until
+        // we try to log a new message.
+        vmBooted.getAndSet(true);
+        LogStream.err.println("VM Booted: " + vmBooted.get());
+        LogStream.err.println("LogManager initialized: " + logManagerInitialized.get(null));
+        if (!BootstrapLogger.isBooted()) {
+            throw new RuntimeException("VM should now be booted!");
+        }
+        if (((Boolean)logManagerInitialized.get(null)).booleanValue()) {
+            throw new RuntimeException("LogManager shouldn't be initialized yet!");
+        }
+
+        // Logging a message should cause the BootstrapLogger to replace itself
+        // by a 'real' logger in the LazyLogger. But since the LogManager isn't
+        // initialized yet, this should be a SimpleConsoleLogger...
+        logger.log(Level.INFO, "LOG#4: VM now booted: {0}", vmBooted.get());
+        logger.log(Level.DEBUG, "LOG#5: hi!");
+        SimplePolicy.allowAll.set(Boolean.TRUE);
+        WeakReference<Thread> threadRef = null;
+        ReferenceQueue<Thread> queue = new ReferenceQueue<>();
+        try {
+            Set<Thread> set = Thread.getAllStackTraces().keySet().stream()
+                    .filter((t) -> t.getName().startsWith("BootstrapMessageLoggerTask-"))
+                    .collect(Collectors.toSet());
+            set.stream().forEach(t -> LogStream.err.println("Found: " + t));
+            if (set.size() > 1) {
+                throw new RuntimeException("Too many bootsrap threads found");
+            }
+            Optional<Thread> t = set.stream().findFirst();
+            if (t.isPresent()) {
+                threadRef = new WeakReference<>(t.get(), queue);
+            }
+        } finally{
+            SimplePolicy.allowAll.set(Boolean.FALSE);
+        }
+        if (!BootstrapLogger.isBooted()) {
+            throw new RuntimeException("VM should still be booted!");
+        }
+        if (((Boolean)logManagerInitialized.get(null)).booleanValue()) {
+            throw new RuntimeException("LogManager shouldn't be initialized yet!");
+        }
+
+        // Now check that the early messages we had printed before the VM was
+        // booted have appeared on System.err...
+        String afterBoot = err.drain();
+        for (int i=0; i<levels.length; i++) {
+            String m = levels[i].getName()+": Early message #"+(i+1);
+            if (!afterBoot.contains(m)) {
+                throw new RuntimeException("System.err does not contain: "+m);
+            }
+        }
+        // check that the message logged *after* the VM was booted also printed.
+        if (!afterBoot.contains("INFO: LOG#4")) {
+            throw new RuntimeException("System.err does not contain: "
+                    + "INFO: LOG#4");
+        }
+        // check that the debug message was not printed.
+        if (afterBoot.contains("LOG#5")) {
+            throw new RuntimeException("System.err contain: " + "LOG#5");
+        }
+        LogStream.err.println("VM Booted: " + vmBooted.get());
+        LogStream.err.println("LogManager initialized: " + logManagerInitialized.get(null));
+        if (!BootstrapLogger.isBooted()) {
+            throw new RuntimeException("VM should still be booted!");
+        }
+        if (((Boolean)logManagerInitialized.get(null)).booleanValue()) {
+            throw new RuntimeException("LogManager shouldn't be initialized yet!");
+        }
+
+        // Now we're going to use reflection to access JUL, and change
+        // the level of the "foo" logger.
+        // We're using reflection so that the test can also run in
+        // configurations where java.util.logging is not present.
+        boolean hasJUL = false;
+        SimplePolicy.allowAll.set(Boolean.TRUE);
+        try {
+            Class<?> loggerClass = Class.forName("java.util.logging.Logger");
+            Class<?> levelClass  = Class.forName("java.util.logging.Level");
+            Class<?> handlerClass  = Class.forName("java.util.logging.Handler");
+
+            // java.util.logging.Logger.getLogger("foo")
+            //        .setLevel(java.util.logging.Level.FINEST);
+            Object fooLogger = loggerClass.getMethod("getLogger", String.class)
+                    .invoke(null, "foo");
+            loggerClass.getMethod("setLevel", levelClass)
+                    .invoke(fooLogger, levelClass.getField("FINEST").get(null));
+
+            // java.util.logging.Logger.getLogger("").getHandlers()[0]
+            //        .setLevel(java.util.logging.Level.ALL);
+            Object rootLogger = loggerClass.getMethod("getLogger", String.class)
+                    .invoke(null, "");
+            Object handlers = loggerClass.getMethod("getHandlers").
+                    invoke(rootLogger);
+            handlerClass.getMethod("setLevel", levelClass)
+                    .invoke(Array.get(handlers, 0), levelClass.getField("ALL")
+                            .get(null));
+
+            hasJUL = true;
+        } catch (ClassNotFoundException x) {
+            LogStream.err.println("JUL is not present: class " + x.getMessage()
+                    + " not found");
+            hasJUL = false;
+        } finally {
+            SimplePolicy.allowAll.set(Boolean.FALSE);
+        }
+
+        logger.log(Level.DEBUG, "hi now!");
+        String debug = err.drain();
+        if (hasJUL) {
+            if (!((Boolean)logManagerInitialized.get(null)).booleanValue()) {
+                throw new RuntimeException("LogManager should be initialized now!");
+            }
+            if (!debug.contains("FINE: hi now!")) {
+                throw new RuntimeException("System.err does not contain: "
+                        + "FINE: hi now!");
+            }
+        } else {
+            if (debug.contains("hi now!")) {
+                throw new RuntimeException("System.err contains: " + "hi now!");
+            }
+            if (((Boolean)logManagerInitialized.get(null)).booleanValue()) {
+                throw new RuntimeException("LogManager shouldn't be initialized yet!");
+            }
+            Logger baz = System.getLogger("foo.bar.baz");
+            if (((Boolean)logManagerInitialized.get(null)).booleanValue()) {
+                throw new RuntimeException("LogManager shouldn't be initialized yet!");
+            }
+        }
+        Logger bazbaz = null;
+        SimplePolicy.allowAll.set(Boolean.TRUE);
+        try {
+            bazbaz = java.lang.System.LoggerFinder
+                    .getLoggerFinder().getLogger("foo.bar.baz.baz", BootstrapLoggerTest.class);
+        } finally {
+            SimplePolicy.allowAll.set(Boolean.FALSE);
+        }
+        if (!((Boolean)logManagerInitialized.get(null)).booleanValue()) {
+            throw new RuntimeException("LogManager should be initialized now!");
+        }
+        Logger bazbaz2 = System.getLogger("foo.bar.baz.baz");
+        if (bazbaz2.getClass() != bazbaz.getClass()) {
+            throw new RuntimeException("bazbaz2.class != bazbaz.class ["
+                    + bazbaz2.getClass() + " != "
+                    + bazbaz.getClass() + "]");
+        }
+        if (hasJUL != bazbaz2.getClass().getName()
+                .equals("sun.util.logging.internal.LoggingProviderImpl$JULWrapper")) {
+            throw new RuntimeException("Unexpected class for bazbaz: "
+                    + bazbaz.getClass().getName()
+                    + "\n\t expected: "
+                    + "sun.util.logging.internal.LoggingProviderImpl$JULWrapper");
+        }
+
+        // Now we're going to check that the thread of the BootstrapLogger
+        // executor terminates, and that the Executor is GC'ed after that.
+        // This will involve a bit of waiting, hence the timeout=120 in
+        // the @run line.
+        // If this test fails in timeout - we could envisage skipping this part,
+        // or adding some System property to configure the keep alive delay
+        // of the executor.
+        SimplePolicy.allowAll.set(Boolean.TRUE);
+        try {
+            Stream<Thread> stream = Thread.getAllStackTraces().keySet().stream();
+            stream.filter((t) -> t.getName().startsWith("BootstrapMessageLoggerTask-"))
+                    .forEach(t -> LogStream.err.println(t));
+            stream = null;
+            if (threadRef != null && test == TestCase.SECURE_AND_WAIT) {
+                Thread t = threadRef.get();
+                if (t != null) {
+                    if (!(Boolean)isAlive.invoke(null)) {
+                        throw new RuntimeException("Executor already terminated");
+                    } else {
+                        LogStream.err.println("Executor still alive as expected.");
+                    }
+                    LogStream.err.println("Waiting for " + t.getName() + " to terminate (join)");
+                    t.join(60_000);
+                    t = null;
+                }
+                LogStream.err.println("Calling System.gc()");
+                System.gc();
+                LogStream.err.println("Waiting for BootstrapMessageLoggerTask to be gc'ed");
+                while (queue.remove(1000) == null) {
+                    LogStream.err.println("Calling System.gc()");
+                    System.gc();
+                }
+
+                // Call the reference here to make sure threadRef will not be
+                // eagerly garbage collected before the thread it references.
+                // otherwise, it might not be enqueued, resulting in the
+                // queue.remove() call above to always return null....
+                if (threadRef.get() != null) {
+                    throw new RuntimeException("Reference should have been cleared");
+                }
+
+                LogStream.err.println("BootstrapMessageLoggerTask has been gc'ed");
+                // Wait for the executor to be gc'ed...
+                for (int i=0; i<10; i++) {
+                    LogStream.err.println("Calling System.gc()");
+                    System.gc();
+                    if (!(Boolean)isAlive.invoke(null)) break;
+                    // It would be unexpected that we reach here...
+                    Thread.sleep(1000);
+                }
+
+                if ((Boolean)isAlive.invoke(null)) {
+                    throw new RuntimeException("Executor still alive");
+                } else {
+                    LogStream.err.println("Executor terminated as expected.");
+                }
+            } else {
+                LogStream.err.println("Not checking executor termination for " + test);
+            }
+        } finally {
+            SimplePolicy.allowAll.set(Boolean.FALSE);
+        }
+        LogStream.err.println(test.name() + ": PASSED");
+    }
+
+    final static class SimplePolicy extends Policy {
+        static final ThreadLocal<Boolean> allowAll = new ThreadLocal<Boolean>() {
+            @Override
+            protected Boolean initialValue() {
+                return Boolean.FALSE;
+            }
+        };
+
+        Permissions getPermissions() {
+            Permissions perms = new Permissions();
+            if (allowAll.get()) {
+                perms.add(new AllPermission());
+            }
+            return perms;
+        }
+
+        @Override
+        public boolean implies(ProtectionDomain domain, Permission permission) {
+            return getPermissions(domain).implies(permission);
+        }
+
+        @Override
+        public PermissionCollection getPermissions(CodeSource codesource) {
+            return getPermissions();
+        }
+
+        @Override
+        public PermissionCollection getPermissions(ProtectionDomain domain) {
+            return getPermissions();
+        }
+
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/LoggerFinder/internal/LoggerBridgeTest/CustomSystemClassLoader.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.nio.file.Files;
+import java.security.AllPermission;
+import java.security.Permissions;
+import java.security.ProtectionDomain;
+
+
+/**
+ * A custom ClassLoader to load the concrete LoggerFinder class
+ * with all permissions.
+ *
+ * @author danielfuchs
+ */
+public class CustomSystemClassLoader extends ClassLoader {
+
+
+    Class<?> loggerFinderClass = null;
+//    Class<?> loggerImplClass = null;
+
+    public CustomSystemClassLoader() {
+        super();
+    }
+    public CustomSystemClassLoader(ClassLoader parent) {
+        super(parent);
+    }
+
+    private Class<?> defineFinderClass(String name)
+        throws ClassNotFoundException {
+        final Object obj = getClassLoadingLock(name);
+        synchronized(obj) {
+            if (loggerFinderClass != null) return loggerFinderClass;
+
+            URL url = this.getClass().getProtectionDomain().getCodeSource().getLocation();
+            File file = new File(url.getPath(), name+".class");
+            if (file.canRead()) {
+                try {
+                    byte[] b = Files.readAllBytes(file.toPath());
+                    Permissions perms = new Permissions();
+                    perms.add(new AllPermission());
+                    loggerFinderClass = defineClass(
+                            name, b, 0, b.length, new ProtectionDomain(
+                            this.getClass().getProtectionDomain().getCodeSource(),
+                            perms));
+                    System.out.println("Loaded " + name);
+                    return loggerFinderClass;
+                } catch (Throwable ex) {
+                    ex.printStackTrace();
+                    throw new ClassNotFoundException(name, ex);
+                }
+            } else {
+                throw new ClassNotFoundException(name,
+                        new IOException(file.toPath() + ": can't read"));
+            }
+        }
+    }
+//    private Class<?> defineLoggerImplClass(String name)
+//        throws ClassNotFoundException {
+//        final Object obj = getClassLoadingLock(name);
+//        synchronized(obj) {
+//            if (loggerImplClass != null) return loggerImplClass;
+//
+//            URL url = this.getClass().getProtectionDomain().getCodeSource().getLocation();
+//            File file = new File(url.getPath(), name+".class");
+//            if (file.canRead()) {
+//                try {
+//                    byte[] b = Files.readAllBytes(file.toPath());
+//                    Permissions perms = new Permissions();
+//                    perms.add(new AllPermission());
+//                    loggerImplClass = defineClass(
+//                            name, b, 0, b.length, new ProtectionDomain(
+//                            this.getClass().getProtectionDomain().getCodeSource(),
+//                            perms));
+//                    System.out.println("Loaded " + name);
+//                    return loggerImplClass;
+//                } catch (Throwable ex) {
+//                    ex.printStackTrace();
+//                    throw new ClassNotFoundException(name, ex);
+//                }
+//            } else {
+//                throw new ClassNotFoundException(name,
+//                        new IOException(file.toPath() + ": can't read"));
+//            }
+//        }
+//    }
+//
+    @Override
+    public synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
+        if (name.endsWith("$LogProducerFinder")) {
+            Class<?> c = defineFinderClass(name);
+            if (resolve) {
+                resolveClass(c);
+            }
+            return c;
+        }
+//        if (name.endsWith("$LogProducerFinder$LoggerImpl")) {
+//            Class<?> c = defineLoggerImplClass(name);
+//            if (resolve) {
+//                resolveClass(c);
+//            }
+//            return c;
+//        }
+        return super.loadClass(name, resolve);
+    }
+
+    @Override
+    protected Class<?> findClass(String name) throws ClassNotFoundException {
+//        if (name.endsWith("$LogProducerFinder$LoggerImpl")) {
+//            return defineLoggerImplClass(name);
+//        }
+        if (name.endsWith("$$LogProducerFinder")) {
+            return defineFinderClass(name);
+        }
+        return super.findClass(name);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/LoggerFinder/internal/LoggerBridgeTest/LoggerBridgeTest.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,1087 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.security.AccessControlException;
+import java.security.AccessController;
+import java.security.CodeSource;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.Permissions;
+import java.security.Policy;
+import java.security.PrivilegedAction;
+import java.security.ProtectionDomain;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Queue;
+import java.util.ResourceBundle;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.Supplier;
+import java.util.logging.Handler;
+import java.util.logging.LogRecord;
+import java.lang.System.LoggerFinder;
+import java.lang.System.Logger;
+import java.lang.System.Logger.Level;
+import java.util.stream.Stream;
+import sun.util.logging.PlatformLogger;
+
+/**
+ * @test
+ * @bug     8140364
+ * @summary JDK implementation specific unit test for JDK internal artifacts.
+ *          Tests all bridge methods with the a custom backend whose
+ *          loggers implement PlatformLogger.Bridge.
+ * @modules java.base/sun.util.logging java.base/jdk.internal.logger
+ * @build CustomSystemClassLoader LoggerBridgeTest
+ * @run  main/othervm -Djava.system.class.loader=CustomSystemClassLoader LoggerBridgeTest NOSECURITY
+ * @run  main/othervm -Djava.system.class.loader=CustomSystemClassLoader LoggerBridgeTest NOPERMISSIONS
+ * @run  main/othervm -Djava.system.class.loader=CustomSystemClassLoader LoggerBridgeTest WITHPERMISSIONS
+ * @author danielfuchs
+ */
+public class LoggerBridgeTest {
+
+    public static final RuntimePermission LOGGERFINDER_PERMISSION =
+                new RuntimePermission("loggerFinder");
+
+    final static AtomicLong sequencer = new AtomicLong();
+    final static boolean VERBOSE = false;
+    static final ThreadLocal<AtomicBoolean> allowControl = new ThreadLocal<AtomicBoolean>() {
+        @Override
+        protected AtomicBoolean initialValue() {
+            return  new AtomicBoolean(false);
+        }
+    };
+    static final ThreadLocal<AtomicBoolean> allowAccess = new ThreadLocal<AtomicBoolean>() {
+        @Override
+        protected AtomicBoolean initialValue() {
+            return  new AtomicBoolean(false);
+        }
+    };
+    static final ThreadLocal<AtomicBoolean> allowAll = new ThreadLocal<AtomicBoolean>() {
+        @Override
+        protected AtomicBoolean initialValue() {
+            return  new AtomicBoolean(false);
+        }
+    };
+
+    public static final Queue<LogEvent> eventQueue = new ArrayBlockingQueue<>(128);
+
+    public static final class LogEvent implements Cloneable {
+
+        public LogEvent() {
+            this(sequencer.getAndIncrement());
+        }
+
+        LogEvent(long sequenceNumber) {
+            this.sequenceNumber = sequenceNumber;
+        }
+
+        long sequenceNumber;
+        boolean isLoggable;
+        String loggerName;
+        sun.util.logging.PlatformLogger.Level level;
+        ResourceBundle bundle;
+        Throwable thrown;
+        Object[] args;
+        String msg;
+        Supplier<String> supplier;
+        String className;
+        String methodName;
+
+        Object[] toArray() {
+            return new Object[] {
+                sequenceNumber,
+                loggerName,
+                level,
+                isLoggable,
+                bundle,
+                msg,
+                supplier,
+                thrown,
+                args,
+                className,
+                methodName,
+            };
+        }
+
+        @Override
+        public String toString() {
+            return Arrays.deepToString(toArray());
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            return obj instanceof LogEvent
+                    && Objects.deepEquals(this.toArray(), ((LogEvent)obj).toArray());
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(toArray());
+        }
+
+        public LogEvent cloneWith(long sequenceNumber)
+                throws CloneNotSupportedException {
+            LogEvent cloned = (LogEvent)super.clone();
+            cloned.sequenceNumber = sequenceNumber;
+            return cloned;
+        }
+
+        public static LogEvent of(long sequenceNumber,
+                boolean isLoggable, String name,
+                sun.util.logging.PlatformLogger.Level level, ResourceBundle bundle,
+                String key, Throwable thrown, Object... params) {
+            return LogEvent.of(sequenceNumber, isLoggable, name,
+                    null, null, level, bundle, key,
+                    thrown, params);
+        }
+        public static LogEvent of(long sequenceNumber,
+                boolean isLoggable, String name,
+                sun.util.logging.PlatformLogger.Level level, ResourceBundle bundle,
+                Supplier<String> supplier, Throwable thrown, Object... params) {
+            return LogEvent.of(sequenceNumber, isLoggable, name,
+                    null, null, level, bundle, supplier,
+                    thrown, params);
+        }
+
+        public static LogEvent of(long sequenceNumber,
+                boolean isLoggable, String name,
+                String className, String methodName,
+                sun.util.logging.PlatformLogger.Level level, ResourceBundle bundle,
+                String key, Throwable thrown, Object... params) {
+            LogEvent evt = new LogEvent(sequenceNumber);
+            evt.loggerName = name;
+            evt.level = level;
+            evt.args = params;
+            evt.bundle = bundle;
+            evt.thrown = thrown;
+            evt.msg = key;
+            evt.isLoggable = isLoggable;
+            evt.className = className;
+            evt.methodName = methodName;
+            return evt;
+        }
+
+        public static LogEvent of(boolean isLoggable, String name,
+                String className, String methodName,
+                sun.util.logging.PlatformLogger.Level level, ResourceBundle bundle,
+                String key, Throwable thrown, Object... params) {
+            return LogEvent.of(sequencer.getAndIncrement(), isLoggable, name,
+                    className, methodName, level, bundle, key, thrown, params);
+        }
+
+        public static LogEvent of(long sequenceNumber,
+                boolean isLoggable, String name,
+                String className, String methodName,
+                sun.util.logging.PlatformLogger.Level level, ResourceBundle bundle,
+                Supplier<String> supplier, Throwable thrown, Object... params) {
+            LogEvent evt = new LogEvent(sequenceNumber);
+            evt.loggerName = name;
+            evt.level = level;
+            evt.args = params;
+            evt.bundle = bundle;
+            evt.thrown = thrown;
+            evt.supplier = supplier;
+            evt.isLoggable = isLoggable;
+            evt.className = className;
+            evt.methodName = methodName;
+            return evt;
+        }
+
+        public static LogEvent of(boolean isLoggable, String name,
+                String className, String methodName,
+                sun.util.logging.PlatformLogger.Level level, ResourceBundle bundle,
+                Supplier<String> supplier, Throwable thrown, Object... params) {
+            return LogEvent.of(sequencer.getAndIncrement(), isLoggable, name,
+                    className, methodName, level, bundle, supplier, thrown, params);
+        }
+
+    }
+    static final Class<?> providerClass;
+    static {
+        try {
+            // Preload classes before the security manager is on.
+            providerClass = ClassLoader.getSystemClassLoader().loadClass("LoggerBridgeTest$LogProducerFinder");
+            ((LoggerFinder)providerClass.newInstance()).getLogger("foo", providerClass);
+        } catch (Exception ex) {
+            throw new ExceptionInInitializerError(ex);
+        }
+    }
+
+    public static class LogProducerFinder extends LoggerFinder {
+        final ConcurrentHashMap<String, LoggerImpl> system = new ConcurrentHashMap<>();
+        final ConcurrentHashMap<String, LoggerImpl> user = new ConcurrentHashMap<>();
+
+        public class LoggerImpl implements Logger, PlatformLogger.Bridge {
+            private final String name;
+            private sun.util.logging.PlatformLogger.Level level = sun.util.logging.PlatformLogger.Level.INFO;
+            private sun.util.logging.PlatformLogger.Level OFF = sun.util.logging.PlatformLogger.Level.OFF;
+            private sun.util.logging.PlatformLogger.Level FINE = sun.util.logging.PlatformLogger.Level.FINE;
+            private sun.util.logging.PlatformLogger.Level FINER = sun.util.logging.PlatformLogger.Level.FINER;
+            private sun.util.logging.PlatformLogger.Level FINEST = sun.util.logging.PlatformLogger.Level.FINEST;
+            private sun.util.logging.PlatformLogger.Level CONFIG = sun.util.logging.PlatformLogger.Level.CONFIG;
+            private sun.util.logging.PlatformLogger.Level INFO = sun.util.logging.PlatformLogger.Level.INFO;
+            private sun.util.logging.PlatformLogger.Level WARNING = sun.util.logging.PlatformLogger.Level.WARNING;
+            private sun.util.logging.PlatformLogger.Level SEVERE = sun.util.logging.PlatformLogger.Level.SEVERE;
+
+            public LoggerImpl(String name) {
+                this.name = name;
+            }
+
+            @Override
+            public String getName() {
+                return name;
+            }
+
+            @Override
+            public boolean isLoggable(Level level) {
+                return this.level != OFF && this.level.intValue() <= level.getSeverity();
+            }
+
+            @Override
+            public void log(Level level, ResourceBundle bundle,
+                    String key, Throwable thrown) {
+                throw new UnsupportedOperationException();
+            }
+
+            @Override
+            public void log(Level level, ResourceBundle bundle,
+                    String format, Object... params) {
+                throw new UnsupportedOperationException();
+            }
+
+            void log(LogEvent event) {
+                eventQueue.add(event);
+            }
+
+            @Override
+            public void log(Level level, Supplier<String> msgSupplier) {
+                throw new UnsupportedOperationException();
+            }
+
+            @Override
+            public void log(Level level, Supplier<String> msgSupplier,
+                    Throwable thrown) {
+                throw new UnsupportedOperationException();
+            }
+
+            @Override
+            public void log(sun.util.logging.PlatformLogger.Level level, String msg) {
+                log(LogEvent.of(isLoggable(level), name, null, null,
+                        level, null, msg, null, (Object[])null));
+            }
+
+            @Override
+            public void log(sun.util.logging.PlatformLogger.Level level,
+                    Supplier<String> msgSupplier) {
+                log(LogEvent.of(isLoggable(level), name, null, null,
+                        level, null, msgSupplier, null, (Object[])null));
+            }
+
+            @Override
+            public void log(sun.util.logging.PlatformLogger.Level level, String msg,
+                    Object... params) {
+                log(LogEvent.of(isLoggable(level), name, null, null,
+                        level, null, msg, null, params));
+            }
+
+            @Override
+            public void log(sun.util.logging.PlatformLogger.Level level, String msg,
+                    Throwable thrown) {
+                log(LogEvent.of(isLoggable(level), name, null, null,
+                        level, null, msg, thrown, (Object[])null));
+            }
+
+            @Override
+            public void log(sun.util.logging.PlatformLogger.Level level, Throwable thrown,
+                    Supplier<String> msgSupplier) {
+                log(LogEvent.of(isLoggable(level), name, null, null,
+                        level, null, msgSupplier, thrown, (Object[])null));
+            }
+
+            @Override
+            public void logp(sun.util.logging.PlatformLogger.Level level, String sourceClass,
+                    String sourceMethod, String msg) {
+                log(LogEvent.of(isLoggable(level), name,
+                        sourceClass, sourceMethod,
+                        level, null, msg, null, (Object[])null));
+            }
+
+            @Override
+            public void logp(sun.util.logging.PlatformLogger.Level level, String sourceClass,
+                    String sourceMethod, Supplier<String> msgSupplier) {
+                log(LogEvent.of(isLoggable(level), name,
+                        sourceClass, sourceMethod,
+                        level, null, msgSupplier, null, (Object[])null));
+            }
+
+            @Override
+            public void logp(sun.util.logging.PlatformLogger.Level level, String sourceClass,
+                    String sourceMethod, String msg, Object... params) {
+                log(LogEvent.of(isLoggable(level), name,
+                        sourceClass, sourceMethod,
+                        level, null, msg, null, params));
+            }
+
+            @Override
+            public void logp(sun.util.logging.PlatformLogger.Level level, String sourceClass,
+                    String sourceMethod, String msg, Throwable thrown) {
+                log(LogEvent.of(isLoggable(level), name,
+                        sourceClass, sourceMethod,
+                        level, null, msg, thrown, (Object[])null));
+            }
+
+            @Override
+            public void logp(sun.util.logging.PlatformLogger.Level level, String sourceClass,
+                    String sourceMethod, Throwable thrown,
+                    Supplier<String> msgSupplier) {
+                log(LogEvent.of(isLoggable(level), name,
+                        sourceClass, sourceMethod,
+                        level, null, msgSupplier, thrown, (Object[])null));
+            }
+
+            @Override
+            public void logrb(sun.util.logging.PlatformLogger.Level level, String sourceClass,
+                    String sourceMethod, ResourceBundle bundle, String msg,
+                    Object... params) {
+                log(LogEvent.of(isLoggable(level), name,
+                        sourceClass, sourceMethod,
+                        level, bundle, msg, null, params));
+            }
+
+            @Override
+            public void logrb(sun.util.logging.PlatformLogger.Level level, ResourceBundle bundle,
+                    String msg, Object... params) {
+                log(LogEvent.of(isLoggable(level), name, null, null,
+                        level, bundle, msg, null, params));
+            }
+
+            @Override
+            public void logrb(sun.util.logging.PlatformLogger.Level level, String sourceClass,
+                    String sourceMethod, ResourceBundle bundle, String msg,
+                    Throwable thrown) {
+                log(LogEvent.of(isLoggable(level), name,
+                        sourceClass, sourceMethod,
+                        level, bundle, msg, thrown, (Object[])null));
+            }
+
+            @Override
+            public void logrb(sun.util.logging.PlatformLogger.Level level, ResourceBundle bundle,
+                    String msg, Throwable thrown) {
+                log(LogEvent.of(isLoggable(level), name, null, null,
+                        level, bundle, msg, thrown, (Object[])null));
+            }
+
+            @Override
+            public boolean isLoggable(sun.util.logging.PlatformLogger.Level level) {
+                return this.level != OFF && level.intValue()
+                        >= this.level.intValue();
+            }
+
+            @Override
+            public boolean isEnabled() {
+                return this.level != OFF;
+            }
+
+        }
+
+        @Override
+        public Logger getLogger(String name, Class<?> caller) {
+            SecurityManager sm = System.getSecurityManager();
+            if (sm != null) {
+                sm.checkPermission(LOGGERFINDER_PERMISSION);
+            }
+            PrivilegedAction<ClassLoader> pa = () -> caller.getClassLoader();
+            ClassLoader callerLoader = AccessController.doPrivileged(pa);
+            if (callerLoader == null) {
+                return system.computeIfAbsent(name, (n) -> new LoggerImpl(n));
+            } else {
+                return user.computeIfAbsent(name, (n) -> new LoggerImpl(n));
+            }
+        }
+    }
+
+    static final sun.util.logging.PlatformLogger.Level[] julLevels = {
+        sun.util.logging.PlatformLogger.Level.ALL,
+        sun.util.logging.PlatformLogger.Level.FINEST,
+        sun.util.logging.PlatformLogger.Level.FINER,
+        sun.util.logging.PlatformLogger.Level.FINE,
+        sun.util.logging.PlatformLogger.Level.CONFIG,
+        sun.util.logging.PlatformLogger.Level.INFO,
+        sun.util.logging.PlatformLogger.Level.WARNING,
+        sun.util.logging.PlatformLogger.Level.SEVERE,
+        sun.util.logging.PlatformLogger.Level.OFF,
+    };
+
+    public static class MyBundle extends ResourceBundle {
+
+        final ConcurrentHashMap<String,String> map = new ConcurrentHashMap<>();
+
+        @Override
+        protected Object handleGetObject(String key) {
+            if (key.contains(" (translated)")) {
+                throw new RuntimeException("Unexpected key: " + key);
+            }
+            return map.computeIfAbsent(key, k -> k + " (translated)");
+        }
+
+        @Override
+        public Enumeration<String> getKeys() {
+            return Collections.enumeration(map.keySet());
+        }
+
+    }
+
+    public static class MyHandler extends Handler {
+
+        @Override
+        public java.util.logging.Level getLevel() {
+            return java.util.logging.Level.ALL;
+        }
+
+        @Override
+        public void publish(LogRecord record) {
+            eventQueue.add(LogEvent.of(sequencer.getAndIncrement(),
+                    true, record.getLoggerName(),
+                    record.getSourceClassName(),
+                    record.getSourceMethodName(),
+                    PlatformLogger.Level.valueOf(record.getLevel().getName()),
+                    record.getResourceBundle(), record.getMessage(),
+                    record.getThrown(), record.getParameters()));
+        }
+        @Override
+        public void flush() {
+        }
+        @Override
+        public void close() throws SecurityException {
+        }
+
+    }
+
+    public static class MyLoggerBundle extends MyBundle {
+
+    }
+
+    final static Method lazyGetLogger;
+    static {
+        // jdk.internal.logging.LoggerBridge.getLogger(name, caller)
+        try {
+            Class<?> bridgeClass = Class.forName("jdk.internal.logger.LazyLoggers");
+            lazyGetLogger = bridgeClass.getDeclaredMethod("getLogger",
+                    String.class, Class.class);
+            lazyGetLogger.setAccessible(true);
+        } catch (Throwable ex) {
+            throw new ExceptionInInitializerError(ex);
+        }
+    }
+
+    static Logger getLogger(LoggerFinder provider, String name, Class<?> caller) {
+        Logger logger;
+        try {
+            logger = Logger.class.cast(lazyGetLogger.invoke(null, name, caller));
+        } catch (Throwable x) {
+            Throwable t = (x instanceof InvocationTargetException) ?
+                    ((InvocationTargetException)x).getTargetException() : x;
+            if (t instanceof RuntimeException) {
+                throw (RuntimeException)t;
+            } else if (t instanceof Exception) {
+                throw new RuntimeException(t);
+            } else {
+                throw (Error)t;
+            }
+        }
+        // The method above does not throw exception...
+        // call the provider here to verify that an exception would have
+        // been thrown by the provider.
+        if (logger != null && caller == Thread.class) {
+            Logger log = provider.getLogger(name, caller);
+        }
+        return logger;
+    }
+
+    static Logger getLogger(LoggerFinder provider, String name, ResourceBundle bundle, Class<?> caller) {
+        if (caller.getClassLoader() != null) {
+            return System.getLogger(name,bundle);
+        } else {
+            return provider.getLocalizedLogger(name, bundle, caller);
+        }
+    }
+
+    static PlatformLogger.Bridge convert(Logger logger) {
+        return PlatformLogger.Bridge.convert(logger);
+    }
+
+    static enum TestCases {NOSECURITY, NOPERMISSIONS, WITHPERMISSIONS};
+
+    static void setSecurityManager() {
+        if (System.getSecurityManager() == null) {
+            Policy.setPolicy(new SimplePolicy(allowControl, allowAccess, allowAll));
+            System.setSecurityManager(new SecurityManager());
+        }
+    }
+
+    public static void main(String[] args) {
+        if (args.length == 0)
+            args = new String[] {
+                //"NOSECURITY",
+                "NOPERMISSIONS",
+                "WITHPERMISSIONS"
+            };
+
+
+        Stream.of(args).map(TestCases::valueOf).forEach((testCase) -> {
+            LoggerFinder provider;
+            switch (testCase) {
+                case NOSECURITY:
+                    System.out.println("\n*** Without Security Manager\n");
+                    provider = LoggerFinder.getLoggerFinder();
+                    test(provider, true);
+                    System.out.println("Tetscase count: " + sequencer.get());
+                    break;
+                case NOPERMISSIONS:
+                    System.out.println("\n*** With Security Manager, without permissions\n");
+                    setSecurityManager();
+                    try {
+                        provider = LoggerFinder.getLoggerFinder();
+                        throw new RuntimeException("Expected exception not raised");
+                    } catch (AccessControlException x) {
+                        if (!LOGGERFINDER_PERMISSION.equals(x.getPermission())) {
+                            throw new RuntimeException("Unexpected permission check", x);
+                        }
+                        final boolean control = allowControl.get().get();
+                        try {
+                            allowControl.get().set(true);
+                            provider = LoggerFinder.getLoggerFinder();
+                        } finally {
+                            allowControl.get().set(control);
+                        }
+                    }
+                    test(provider, false);
+                    System.out.println("Tetscase count: " + sequencer.get());
+                    break;
+                case WITHPERMISSIONS:
+                    System.out.println("\n*** With Security Manager, with control permission\n");
+                    setSecurityManager();
+                    final boolean control = allowControl.get().get();
+                    try {
+                        allowControl.get().set(true);
+                        provider = LoggerFinder.getLoggerFinder();
+                        test(provider, true);
+                    } finally {
+                        allowControl.get().set(control);
+                    }
+                    break;
+                default:
+                    throw new RuntimeException("Unknown test case: " + testCase);
+            }
+        });
+        System.out.println("\nPASSED: Tested " + sequencer.get() + " cases.");
+    }
+
+    public static void test(LoggerFinder provider, boolean hasRequiredPermissions) {
+
+        ResourceBundle loggerBundle = ResourceBundle.getBundle(MyLoggerBundle.class.getName());
+        final Map<Object, String> loggerDescMap = new HashMap<>();
+
+
+        Logger appLogger1 = System.getLogger("foo");
+        loggerDescMap.put(appLogger1, "LogProducer.getApplicationLogger(\"foo\")");
+
+        Logger sysLogger1 = null;
+        try {
+            sysLogger1 = getLogger(provider, "foo", Thread.class);
+            loggerDescMap.put(sysLogger1, "LogProducer.getSystemLogger(\"foo\")");
+            if (!hasRequiredPermissions) {
+                throw new RuntimeException("Managed to obtain a system logger without permission");
+            }
+        } catch (AccessControlException acx) {
+            if (hasRequiredPermissions) {
+                throw new RuntimeException("Unexpected security exception: ", acx);
+            }
+            if (!acx.getPermission().equals(LOGGERFINDER_PERMISSION)) {
+                throw new RuntimeException("Unexpected permission in exception: " + acx, acx);
+            }
+            System.out.println("Got expected exception for system logger: " + acx);
+        }
+
+
+        Logger appLogger2 =
+                System.getLogger("foo", loggerBundle);
+        loggerDescMap.put(appLogger2, "LogProducer.getApplicationLogger(\"foo\", loggerBundle)");
+
+        Logger sysLogger2 = null;
+        try {
+            sysLogger2 = getLogger(provider, "foo", loggerBundle, Thread.class);
+            loggerDescMap.put(sysLogger2, "provider.getSystemLogger(\"foo\", loggerBundle)");
+            if (!hasRequiredPermissions) {
+                throw new RuntimeException("Managed to obtain a system logger without permission");
+            }
+        } catch (AccessControlException acx) {
+            if (hasRequiredPermissions) {
+                throw new RuntimeException("Unexpected security exception: ", acx);
+            }
+            if (!acx.getPermission().equals(LOGGERFINDER_PERMISSION)) {
+                throw new RuntimeException("Unexpected permission in exception: " + acx, acx);
+            }
+            System.out.println("Got expected exception for localized system logger: " + acx);
+        }
+        if (hasRequiredPermissions && appLogger2 == sysLogger2) {
+            throw new RuntimeException("identical loggers");
+        }
+        if (appLogger2 == appLogger1) {
+            throw new RuntimeException("identical loggers");
+        }
+        if (hasRequiredPermissions && sysLogger2 == sysLogger1) {
+            throw new RuntimeException("identical loggers");
+        }
+
+
+        final LogProducerFinder.LoggerImpl appSink;
+        final LogProducerFinder.LoggerImpl sysSink;
+        boolean old = allowControl.get().get();
+        allowControl.get().set(true);
+        try {
+           appSink = LogProducerFinder.LoggerImpl.class.cast(
+                   provider.getLogger("foo",  LoggerBridgeTest.class));
+           sysSink = LogProducerFinder.LoggerImpl.class.cast(
+                        provider.getLogger("foo", Thread.class));
+        } finally {
+            allowControl.get().set(old);
+        }
+
+        testLogger(provider, loggerDescMap, "foo", null, convert(appLogger1), appSink);
+        if (hasRequiredPermissions) {
+            testLogger(provider, loggerDescMap, "foo", null, convert(sysLogger1), sysSink);
+        }
+        testLogger(provider, loggerDescMap, "foo", loggerBundle, convert(appLogger2), appSink);
+        if (hasRequiredPermissions) {
+            testLogger(provider, loggerDescMap, "foo", loggerBundle, convert(sysLogger2), sysSink);
+        }
+    }
+
+    public static class Foo {
+
+    }
+
+    static void verbose(String msg) {
+       if (VERBOSE) {
+           System.out.println(msg);
+       }
+    }
+
+    static void checkLogEvent(LoggerFinder provider, String desc,
+            LogEvent expected) {
+        LogEvent actual =  eventQueue.poll();
+        if (!expected.equals(actual)) {
+            throw new RuntimeException("mismatch for " + desc
+                    + "\n\texpected=" + expected
+                    + "\n\t  actual=" + actual);
+        } else {
+            verbose("Got expected results for "
+                    + desc + "\n\t" + expected);
+        }
+    }
+
+    static void checkLogEvent(LoggerFinder provider, String desc,
+            LogEvent expected, boolean expectNotNull) {
+        LogEvent actual =  eventQueue.poll();
+        if (actual == null && !expectNotNull) return;
+        if (actual != null && !expectNotNull) {
+            throw new RuntimeException("Unexpected log event found for " + desc
+                + "\n\tgot: " + actual);
+        }
+        if (!expected.equals(actual)) {
+            throw new RuntimeException("mismatch for " + desc
+                    + "\n\texpected=" + expected
+                    + "\n\t  actual=" + actual);
+        } else {
+            verbose("Got expected results for "
+                    + desc + "\n\t" + expected);
+        }
+    }
+
+    static void setLevel( LogProducerFinder.LoggerImpl sink,
+            sun.util.logging.PlatformLogger.Level loggerLevel) {
+        sink.level = loggerLevel;
+    }
+
+    // Calls the methods defined on LogProducer and verify the
+    // parameters received by the underlying LogProducerFinder.LoggerImpl
+    // logger.
+    private static void testLogger(LoggerFinder provider,
+            Map<Object, String> loggerDescMap,
+            String name,
+            ResourceBundle loggerBundle,
+            PlatformLogger.Bridge logger,
+            LogProducerFinder.LoggerImpl sink) {
+
+        System.out.println("Testing " + loggerDescMap.get(logger) + "[" + logger + "]");
+        final sun.util.logging.PlatformLogger.Level OFF = sun.util.logging.PlatformLogger.Level.OFF;
+
+        Foo foo = new Foo();
+        String fooMsg = foo.toString();
+        System.out.println("\tlogger.log(messageLevel, fooMsg)");
+        System.out.println("\tlogger.<level>(fooMsg)");
+        for (sun.util.logging.PlatformLogger.Level loggerLevel : julLevels) {
+            setLevel(sink, loggerLevel);
+            for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
+                String desc = "logger.log(messageLevel, fooMsg): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                LogEvent expected =
+                        LogEvent.of(
+                            sequencer.get(),
+                            loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
+                            name, messageLevel, loggerBundle,
+                            fooMsg, (Throwable)null, (Object[])null);
+                logger.log(messageLevel, fooMsg);
+                checkLogEvent(provider, desc, expected);
+            }
+        }
+
+        Supplier<String> supplier = new Supplier<String>() {
+            @Override
+            public String get() {
+                return this.toString();
+            }
+        };
+        System.out.println("\tlogger.log(messageLevel, supplier)");
+        System.out.println("\tlogger.<level>(supplier)");
+        for (sun.util.logging.PlatformLogger.Level loggerLevel : julLevels) {
+            setLevel(sink, loggerLevel);
+            for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
+                String desc = "logger.log(messageLevel, supplier): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                LogEvent expected =
+                        LogEvent.of(
+                            sequencer.get(),
+                            loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
+                            name, messageLevel, null,
+                            supplier, (Throwable)null, (Object[])null);
+                logger.log(messageLevel, supplier);
+                checkLogEvent(provider, desc, expected);
+            }
+        }
+
+        String format = "two params [{1} {2}]";
+        Object arg1 = foo;
+        Object arg2 = fooMsg;
+        System.out.println("\tlogger.log(messageLevel, format, arg1, arg2)");
+        for (sun.util.logging.PlatformLogger.Level loggerLevel : julLevels) {
+            setLevel(sink, loggerLevel);
+            for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
+                String desc = "logger.log(messageLevel, format, foo, fooMsg): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                LogEvent expected =
+                        LogEvent.of(
+                            sequencer.get(),
+                            loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
+                            name, messageLevel, loggerBundle,
+                            format, (Throwable)null, arg1, arg2);
+                logger.log(messageLevel, format, arg1, arg2);
+                checkLogEvent(provider, desc, expected);
+            }
+        }
+
+        Throwable thrown = new Exception("OK: log me!");
+        System.out.println("\tlogger.log(messageLevel, fooMsg, thrown)");
+        for (sun.util.logging.PlatformLogger.Level loggerLevel : julLevels) {
+            setLevel(sink, loggerLevel);
+            for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
+                String desc = "logger.log(messageLevel, fooMsg, thrown): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                LogEvent expected =
+                        LogEvent.of(
+                            sequencer.get(),
+                            loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
+                            name, messageLevel, loggerBundle,
+                            fooMsg, thrown, (Object[])null);
+                logger.log(messageLevel, fooMsg, thrown);
+                checkLogEvent(provider, desc, expected);
+            }
+        }
+
+        System.out.println("\tlogger.log(messageLevel, thrown, supplier)");
+        for (sun.util.logging.PlatformLogger.Level loggerLevel : julLevels) {
+            setLevel(sink, loggerLevel);
+            for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
+                String desc = "logger.log(messageLevel, thrown, supplier): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                LogEvent expected =
+                        LogEvent.of(
+                            sequencer.get(),
+                            loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
+                            name, messageLevel, null,
+                            supplier, thrown, (Object[])null);
+                logger.log(messageLevel, thrown, supplier);
+                checkLogEvent(provider, desc, expected);
+            }
+        }
+
+        String sourceClass = "blah.Blah";
+        String sourceMethod = "blih";
+        System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, fooMsg)");
+        for (sun.util.logging.PlatformLogger.Level loggerLevel : julLevels) {
+            setLevel(sink, loggerLevel);
+            for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
+                String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, fooMsg): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                LogEvent expected =
+                        LogEvent.of(
+                            sequencer.get(),
+                            loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
+                            name, sourceClass, sourceMethod, messageLevel, loggerBundle,
+                            fooMsg, (Throwable)null, (Object[])null);
+                logger.logp(messageLevel, sourceClass, sourceMethod, fooMsg);
+                checkLogEvent(provider, desc, expected);
+            }
+        }
+
+        System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, supplier)");
+        for (sun.util.logging.PlatformLogger.Level loggerLevel : julLevels) {
+            setLevel(sink, loggerLevel);
+            for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
+                String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, supplier): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                LogEvent expected =
+                        LogEvent.of(
+                            sequencer.get(),
+                            loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
+                            name, sourceClass, sourceMethod, messageLevel, null,
+                            supplier, (Throwable)null, (Object[])null);
+                logger.logp(messageLevel, sourceClass, sourceMethod, supplier);
+                checkLogEvent(provider, desc, expected);
+            }
+        }
+
+        System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, format, arg1, arg2)");
+        for (sun.util.logging.PlatformLogger.Level loggerLevel : julLevels) {
+            setLevel(sink, loggerLevel);
+            for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
+                String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, format, arg1, arg2): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                LogEvent expected =
+                        LogEvent.of(
+                            sequencer.get(),
+                            loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
+                            name, sourceClass, sourceMethod, messageLevel, loggerBundle,
+                            format, (Throwable)null, arg1, arg2);
+                logger.logp(messageLevel, sourceClass, sourceMethod, format, arg1, arg2);
+                checkLogEvent(provider, desc, expected);
+            }
+        }
+
+        System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, fooMsg, thrown)");
+        for (sun.util.logging.PlatformLogger.Level loggerLevel : julLevels) {
+            setLevel(sink, loggerLevel);
+            for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
+                String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, fooMsg, thrown): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                LogEvent expected =
+                        LogEvent.of(
+                            sequencer.get(),
+                            loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
+                            name, sourceClass, sourceMethod, messageLevel, loggerBundle,
+                            fooMsg, thrown, (Object[])null);
+                logger.logp(messageLevel, sourceClass, sourceMethod, fooMsg, thrown);
+                checkLogEvent(provider, desc, expected);
+            }
+        }
+
+        System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, thrown, supplier)");
+        for (sun.util.logging.PlatformLogger.Level loggerLevel : julLevels) {
+            setLevel(sink, loggerLevel);
+            for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
+                String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, thrown, supplier): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                LogEvent expected =
+                        LogEvent.of(
+                            sequencer.get(),
+                            loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
+                            name, sourceClass, sourceMethod, messageLevel, null,
+                            supplier, thrown, (Object[])null);
+                logger.logp(messageLevel, sourceClass, sourceMethod, thrown, supplier);
+                checkLogEvent(provider, desc, expected);
+            }
+        }
+
+        ResourceBundle bundle = ResourceBundle.getBundle(MyBundle.class.getName());
+        System.out.println("\tlogger.logrb(messageLevel, bundle, format, arg1, arg2)");
+        for (sun.util.logging.PlatformLogger.Level loggerLevel : julLevels) {
+            setLevel(sink, loggerLevel);
+            for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
+                String desc = "logger.logrb(messageLevel, bundle, format, arg1, arg2): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                LogEvent expected =
+                        LogEvent.of(
+                            sequencer.get(),
+                            loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
+                            name, messageLevel, bundle,
+                            format, (Throwable)null, arg1, arg2);
+                logger.logrb(messageLevel, bundle, format, arg1, arg2);
+                checkLogEvent(provider, desc, expected);
+            }
+        }
+
+        System.out.println("\tlogger.logrb(messageLevel, bundle, msg, thrown)");
+        for (sun.util.logging.PlatformLogger.Level loggerLevel : julLevels) {
+            setLevel(sink, loggerLevel);
+            for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
+                String desc = "logger.logrb(messageLevel, bundle, msg, thrown): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                LogEvent expected =
+                        LogEvent.of(
+                            sequencer.get(),
+                            loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
+                            name, messageLevel, bundle,
+                            fooMsg, thrown, (Object[])null);
+                logger.logrb(messageLevel, bundle, fooMsg, thrown);
+                checkLogEvent(provider, desc, expected);
+            }
+        }
+
+        System.out.println("\tlogger.logrb(messageLevel, sourceClass, sourceMethod, bundle, format, arg1, arg2)");
+        for (sun.util.logging.PlatformLogger.Level loggerLevel : julLevels) {
+            setLevel(sink, loggerLevel);
+            for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
+                String desc = "logger.logrb(messageLevel, sourceClass, sourceMethod, bundle, format, arg1, arg2): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                LogEvent expected =
+                        LogEvent.of(
+                            sequencer.get(),
+                            loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
+                            name, sourceClass, sourceMethod, messageLevel, bundle,
+                            format, (Throwable)null, arg1, arg2);
+                logger.logrb(messageLevel, sourceClass, sourceMethod, bundle, format, arg1, arg2);
+                checkLogEvent(provider, desc, expected);
+            }
+        }
+
+        System.out.println("\tlogger.logrb(messageLevel, sourceClass, sourceMethod, bundle, msg, thrown)");
+        for (sun.util.logging.PlatformLogger.Level loggerLevel : julLevels) {
+            setLevel(sink, loggerLevel);
+            for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
+                String desc = "logger.logrb(messageLevel, sourceClass, sourceMethod, bundle, msg, thrown): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                LogEvent expected =
+                        LogEvent.of(
+                            sequencer.get(),
+                            loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
+                            name, sourceClass, sourceMethod, messageLevel, bundle,
+                            fooMsg, thrown, (Object[])null);
+                logger.logrb(messageLevel, sourceClass, sourceMethod, bundle, fooMsg, thrown);
+                checkLogEvent(provider, desc, expected);
+            }
+        }
+    }
+
+    final static class PermissionsBuilder {
+        final Permissions perms;
+        public PermissionsBuilder() {
+            this(new Permissions());
+        }
+        public PermissionsBuilder(Permissions perms) {
+            this.perms = perms;
+        }
+        public PermissionsBuilder add(Permission p) {
+            perms.add(p);
+            return this;
+        }
+        public PermissionsBuilder addAll(PermissionCollection col) {
+            if (col != null) {
+                for (Enumeration<Permission> e = col.elements(); e.hasMoreElements(); ) {
+                    perms.add(e.nextElement());
+                }
+            }
+            return this;
+        }
+        public Permissions toPermissions() {
+            final PermissionsBuilder builder = new PermissionsBuilder();
+            builder.addAll(perms);
+            return builder.perms;
+        }
+    }
+
+    public static class SimplePolicy extends Policy {
+        final static RuntimePermission CONTROL = LOGGERFINDER_PERMISSION;
+        final static RuntimePermission ACCESS_LOGGER = new RuntimePermission("accessClassInPackage.jdk.internal.logger");
+        final static RuntimePermission ACCESS_LOGGING = new RuntimePermission("accessClassInPackage.sun.util.logging");
+
+        final Permissions permissions;
+        final Permissions allPermissions;
+        final ThreadLocal<AtomicBoolean> allowControl;
+        final ThreadLocal<AtomicBoolean> allowAccess;
+        final ThreadLocal<AtomicBoolean> allowAll;
+        public SimplePolicy(ThreadLocal<AtomicBoolean> allowControl,
+                ThreadLocal<AtomicBoolean> allowAccess,
+                ThreadLocal<AtomicBoolean> allowAll) {
+            this.allowControl = allowControl;
+            this.allowAccess = allowAccess;
+            this.allowAll = allowAll;
+            permissions = new Permissions();
+            allPermissions = new PermissionsBuilder()
+                    .add(new java.security.AllPermission())
+                    .toPermissions();
+        }
+
+        Permissions getPermissions() {
+            if (allowControl.get().get() || allowAccess.get().get() || allowAll.get().get()) {
+                PermissionsBuilder builder =  new PermissionsBuilder()
+                        .addAll(permissions);
+                if (allowControl.get().get()) {
+                    builder.add(CONTROL);
+                }
+                if (allowAccess.get().get()) {
+                    builder.add(ACCESS_LOGGER);
+                    builder.add(ACCESS_LOGGING);
+                }
+                if (allowAll.get().get()) {
+                    builder.addAll(allPermissions);
+                }
+                return builder.toPermissions();
+            }
+            return permissions;
+        }
+
+        @Override
+        public boolean implies(ProtectionDomain domain, Permission permission) {
+            return getPermissions().implies(permission);
+        }
+
+        @Override
+        public PermissionCollection getPermissions(CodeSource codesource) {
+            return new PermissionsBuilder().addAll(getPermissions()).toPermissions();
+        }
+
+        @Override
+        public PermissionCollection getPermissions(ProtectionDomain domain) {
+            return new PermissionsBuilder().addAll(getPermissions()).toPermissions();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/LoggerFinder/internal/LoggerBridgeTest/META-INF/services/java.lang.System$LoggerFinder	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,1 @@
+LoggerBridgeTest$LogProducerFinder
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/LoggerFinder/internal/LoggerFinderLoaderTest/AccessSystemLogger.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+import java.io.IOException;
+import java.lang.System.Logger;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+import java.util.ResourceBundle;
+
+/**
+ *
+ * @author danielfuchs
+ */
+public final class AccessSystemLogger {
+
+    public AccessSystemLogger() {
+        this(check());
+    }
+
+    private AccessSystemLogger(Void unused) {
+    }
+
+    private static Void check() {
+        if (AccessSystemLogger.class.getClassLoader() != null) {
+            throw new RuntimeException("AccessSystemLogger should be loaded by the null classloader");
+        }
+        return null;
+    }
+
+    public Logger getLogger(String name) {
+        Logger logger = System.getLogger(name);
+        System.out.println("System.getLogger(\"" + name + "\"): " + logger);
+        return logger;
+    }
+
+    public Logger getLogger(String name, ResourceBundle bundle) {
+        Logger logger = System.getLogger(name, bundle);
+        System.out.println("System.getLogger(\"" + name + "\", bundle): " + logger);
+        return logger;
+    }
+
+    static final Class<?>[] toCopy = { AccessSystemLogger.class, CustomSystemClassLoader.class };
+
+    // copy AccessSystemLogger.class to ./boot
+    public static void main(String[] args) throws IOException {
+        Path testDir = Paths.get(System.getProperty("user.dir", "."));
+        Path bootDir = Paths.get(testDir.toString(), "boot");
+        Path classes = Paths.get(System.getProperty("test.classes", "build/classes"));
+        if (Files.notExists(bootDir)) {
+            Files.createDirectory(bootDir);
+        }
+        for (Class<?> c : toCopy) {
+            Path thisClass = Paths.get(classes.toString(),
+                c.getSimpleName()+".class");
+            Path dest = Paths.get(bootDir.toString(),
+                c.getSimpleName()+".class");
+            Files.copy(thisClass, dest, StandardCopyOption.REPLACE_EXISTING);
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/LoggerFinder/internal/LoggerFinderLoaderTest/CustomSystemClassLoader.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.nio.file.Files;
+import java.security.AllPermission;
+import java.security.Permissions;
+import java.security.ProtectionDomain;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * A custom ClassLoader to load the concrete LoggerFinder class
+ * with all permissions. The CustomSystemClassLoader class must be
+ * in the BCL, otherwise when system classes - such as
+ * ZoneDateTime try to load their resource bundle a MissingResourceBundle
+ * caused by a SecurityException may be thrown, as the CustomSystemClassLoader
+ * code base will be found in the stack called by doPrivileged.
+ *
+ * @author danielfuchs
+ */
+public class CustomSystemClassLoader extends ClassLoader {
+
+
+    final List<String> finderClassNames =
+            Arrays.asList("LoggerFinderLoaderTest$BaseLoggerFinder",
+                    "LoggerFinderLoaderTest$BaseLoggerFinder2");
+    final Map<String, Class<?>> finderClasses = new HashMap<>();
+    Class<?> testLoggerFinderClass;
+
+    public CustomSystemClassLoader() {
+        super();
+    }
+    public CustomSystemClassLoader(ClassLoader parent) {
+        super(parent);
+    }
+
+    private Class<?> defineFinderClass(String name)
+        throws ClassNotFoundException {
+        final Object obj = getClassLoadingLock(name);
+        synchronized(obj) {
+            if (finderClasses.get(name) != null) return finderClasses.get(name);
+            if (testLoggerFinderClass == null) {
+                // Hack: we  load testLoggerFinderClass to get its code source.
+                //       we can't use this.getClass() since we are in the boot.
+                testLoggerFinderClass = super.loadClass("LoggerFinderLoaderTest$TestLoggerFinder");
+            }
+            URL url = testLoggerFinderClass.getProtectionDomain().getCodeSource().getLocation();
+            File file = new File(url.getPath(), name+".class");
+            if (file.canRead()) {
+                try {
+                    byte[] b = Files.readAllBytes(file.toPath());
+                    Permissions perms = new Permissions();
+                    perms.add(new AllPermission());
+                    Class<?> finderClass = defineClass(
+                            name, b, 0, b.length, new ProtectionDomain(
+                            this.getClass().getProtectionDomain().getCodeSource(),
+                            perms));
+                    System.out.println("Loaded " + name);
+                    finderClasses.put(name, finderClass);
+                    return finderClass;
+                } catch (Throwable ex) {
+                    ex.printStackTrace();
+                    throw new ClassNotFoundException(name, ex);
+                }
+            } else {
+                throw new ClassNotFoundException(name,
+                        new IOException(file.toPath() + ": can't read"));
+            }
+        }
+    }
+
+    @Override
+    public synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
+        if (finderClassNames.contains(name)) {
+            Class<?> c = defineFinderClass(name);
+            if (resolve) {
+                resolveClass(c);
+            }
+            return c;
+        }
+        return super.loadClass(name, resolve);
+    }
+
+    @Override
+    protected Class<?> findClass(String name) throws ClassNotFoundException {
+        if (finderClassNames.contains(name)) {
+            return defineFinderClass(name);
+        }
+        return super.findClass(name);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/LoggerFinder/internal/LoggerFinderLoaderTest/LoggerFinderLoaderTest.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,882 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.io.UncheckedIOException;
+import java.security.AccessControlException;
+import java.security.CodeSource;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.Permissions;
+import java.security.Policy;
+import java.security.ProtectionDomain;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.ResourceBundle;
+import java.util.stream.Stream;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.Supplier;
+import java.lang.System.LoggerFinder;
+import java.lang.System.Logger;
+import java.lang.System.Logger.Level;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.EnumSet;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.ServiceConfigurationError;
+import java.util.ServiceLoader;
+import java.util.concurrent.atomic.AtomicReference;
+import jdk.internal.logger.SimpleConsoleLogger;
+
+/**
+ * @test
+ * @bug     8140364
+ * @summary JDK implementation specific unit test for LoggerFinderLoader.
+ *          Tests the behavior of LoggerFinderLoader with respect to the
+ *          value of the internal diagnosability switches. Also test the
+ *          DefaultLoggerFinder and SimpleConsoleLogger implementation.
+ * @modules java.base/sun.util.logging
+ *          java.base/jdk.internal.logger
+ * @build AccessSystemLogger LoggerFinderLoaderTest CustomSystemClassLoader
+ * @run  driver AccessSystemLogger
+ * @run  main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader LoggerFinderLoaderTest NOSECURITY
+ * @run  main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader LoggerFinderLoaderTest NOPERMISSIONS
+ * @run  main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader LoggerFinderLoaderTest WITHPERMISSIONS
+ * @run  main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Dtest.fails=true LoggerFinderLoaderTest NOSECURITY
+ * @run  main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Dtest.fails=true LoggerFinderLoaderTest NOPERMISSIONS
+ * @run  main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Dtest.fails=true LoggerFinderLoaderTest WITHPERMISSIONS
+ * @run  main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Dtest.fails=true -Djdk.logger.finder.error=ERROR LoggerFinderLoaderTest NOSECURITY
+ * @run  main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Dtest.fails=true -Djdk.logger.finder.error=ERROR LoggerFinderLoaderTest NOPERMISSIONS
+ * @run  main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Dtest.fails=true -Djdk.logger.finder.error=ERROR LoggerFinderLoaderTest WITHPERMISSIONS
+ * @run  main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Dtest.fails=true -Djdk.logger.finder.error=DEBUG LoggerFinderLoaderTest NOSECURITY
+ * @run  main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Dtest.fails=true -Djdk.logger.finder.error=DEBUG LoggerFinderLoaderTest NOPERMISSIONS
+ * @run  main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Dtest.fails=true -Djdk.logger.finder.error=DEBUG LoggerFinderLoaderTest WITHPERMISSIONS
+ * @run  main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Dtest.fails=true -Djdk.logger.finder.error=QUIET LoggerFinderLoaderTest NOSECURITY
+ * @run  main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Dtest.fails=true -Djdk.logger.finder.error=QUIET LoggerFinderLoaderTest NOPERMISSIONS
+ * @run  main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Dtest.fails=true -Djdk.logger.finder.error=QUIET LoggerFinderLoaderTest WITHPERMISSIONS
+ * @run  main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Djdk.logger.finder.singleton=true LoggerFinderLoaderTest NOSECURITY
+ * @run  main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Djdk.logger.finder.singleton=true LoggerFinderLoaderTest NOPERMISSIONS
+ * @run  main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Djdk.logger.finder.singleton=true LoggerFinderLoaderTest WITHPERMISSIONS
+ * @run  main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Djdk.logger.finder.singleton=true -Djdk.logger.finder.error=ERROR LoggerFinderLoaderTest NOSECURITY
+ * @run  main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Djdk.logger.finder.singleton=true -Djdk.logger.finder.error=ERROR LoggerFinderLoaderTest NOPERMISSIONS
+ * @run  main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Djdk.logger.finder.singleton=true -Djdk.logger.finder.error=ERROR LoggerFinderLoaderTest WITHPERMISSIONS
+ * @run  main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Djdk.logger.finder.singleton=true -Djdk.logger.finder.error=DEBUG LoggerFinderLoaderTest NOSECURITY
+ * @run  main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Djdk.logger.finder.singleton=true -Djdk.logger.finder.error=DEBUG LoggerFinderLoaderTest NOPERMISSIONS
+ * @run  main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Djdk.logger.finder.singleton=true -Djdk.logger.finder.error=DEBUG LoggerFinderLoaderTest WITHPERMISSIONS
+ * @run  main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Djdk.logger.finder.singleton=true -Djdk.logger.finder.error=QUIET LoggerFinderLoaderTest NOSECURITY
+ * @run  main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Djdk.logger.finder.singleton=true -Djdk.logger.finder.error=QUIET LoggerFinderLoaderTest NOPERMISSIONS
+ * @run  main/othervm -Xbootclasspath/a:boot -Djava.system.class.loader=CustomSystemClassLoader -Djdk.logger.finder.singleton=true -Djdk.logger.finder.error=QUIET LoggerFinderLoaderTest WITHPERMISSIONS
+ * @author danielfuchs
+ */
+public class LoggerFinderLoaderTest {
+
+    static final RuntimePermission LOGGERFINDER_PERMISSION =
+                new RuntimePermission("loggerFinder");
+    final static boolean VERBOSE = false;
+    static final ThreadLocal<AtomicBoolean> allowControl = new ThreadLocal<AtomicBoolean>() {
+        @Override
+        protected AtomicBoolean initialValue() {
+            return  new AtomicBoolean(false);
+        }
+    };
+    static final ThreadLocal<AtomicBoolean> allowAccess = new ThreadLocal<AtomicBoolean>() {
+        @Override
+        protected AtomicBoolean initialValue() {
+            return  new AtomicBoolean(false);
+        }
+    };
+
+    final static AccessSystemLogger accessSystemLogger = new AccessSystemLogger();
+    static final Class<?>[] providerClass;
+    static {
+        try {
+            providerClass = new Class<?>[] {
+                ClassLoader.getSystemClassLoader().loadClass("LoggerFinderLoaderTest$BaseLoggerFinder"),
+                ClassLoader.getSystemClassLoader().loadClass("LoggerFinderLoaderTest$BaseLoggerFinder2")
+            };
+        } catch (ClassNotFoundException ex) {
+            throw new ExceptionInInitializerError(ex);
+        }
+    }
+
+    /**
+     * What our test provider needs to implement.
+     */
+    public static interface TestLoggerFinder {
+        public final static AtomicBoolean fails = new AtomicBoolean();
+        public final static AtomicReference<String> conf = new AtomicReference<>("");
+        public final static AtomicLong sequencer = new AtomicLong();
+        public final ConcurrentHashMap<String, LoggerImpl> system = new ConcurrentHashMap<>();
+        public final ConcurrentHashMap<String, LoggerImpl> user = new ConcurrentHashMap<>();
+
+        public class LoggerImpl implements System.Logger {
+            final String name;
+            final Logger logger;
+
+            public LoggerImpl(String name, Logger logger) {
+                this.name = name;
+                this.logger = logger;
+            }
+
+            @Override
+            public String getName() {
+                return name;
+            }
+
+            @Override
+            public boolean isLoggable(Logger.Level level) {
+                return logger.isLoggable(level);
+            }
+
+            @Override
+            public void log(Logger.Level level, ResourceBundle bundle, String key, Throwable thrown) {
+                logger.log(level, bundle, key, thrown);
+            }
+
+            @Override
+            public void log(Logger.Level level, ResourceBundle bundle, String format, Object... params) {
+                logger.log(level, bundle, format, params);
+            }
+
+        }
+
+        public Logger getLogger(String name, Class<?> caller);
+        public Logger getLocalizedLogger(String name, ResourceBundle bundle, Class<?> caller);
+    }
+
+    public static class BaseLoggerFinder extends LoggerFinder implements TestLoggerFinder {
+
+        static final RuntimePermission LOGGERFINDER_PERMISSION =
+                    new RuntimePermission("loggerFinder");
+        public BaseLoggerFinder() {
+            if (fails.get()) {
+                throw new RuntimeException("Simulate exception while loading provider");
+            }
+        }
+
+        System.Logger createSimpleLogger(String name) {
+            PrivilegedAction<System.Logger> pa = () -> SimpleConsoleLogger.makeSimpleLogger(name, false);
+            return AccessController.doPrivileged(pa);
+        }
+
+
+        @Override
+        public Logger getLogger(String name, Class<?> caller) {
+            SecurityManager sm = System.getSecurityManager();
+            if (sm != null) {
+                sm.checkPermission(LOGGERFINDER_PERMISSION);
+            }
+            PrivilegedAction<ClassLoader> pa = () -> caller.getClassLoader();
+            ClassLoader callerLoader = AccessController.doPrivileged(pa);
+            if (callerLoader == null) {
+                return system.computeIfAbsent(name, (n) -> new LoggerImpl(n, createSimpleLogger(name)));
+            } else {
+                return user.computeIfAbsent(name, (n) -> new LoggerImpl(n, createSimpleLogger(name)));
+            }
+        }
+    }
+
+    public static class BaseLoggerFinder2 extends LoggerFinder implements TestLoggerFinder {
+
+        static final RuntimePermission LOGGERFINDER_PERMISSION =
+                    new RuntimePermission("loggerFinder");
+        public BaseLoggerFinder2() {
+            throw new ServiceConfigurationError("Should not come here");
+        }
+        @Override
+        public Logger getLogger(String name, Class<?> caller) {
+            throw new ServiceConfigurationError("Should not come here");
+        }
+    }
+
+    public static class MyBundle extends ResourceBundle {
+
+        final ConcurrentHashMap<String,String> map = new ConcurrentHashMap<>();
+
+        @Override
+        protected Object handleGetObject(String key) {
+            if (key.contains(" (translated)")) {
+                throw new RuntimeException("Unexpected key: " + key);
+            }
+            return map.computeIfAbsent(key, k -> k.toUpperCase(Locale.ROOT) + " (translated)");
+        }
+
+        @Override
+        public Enumeration<String> getKeys() {
+            return Collections.enumeration(map.keySet());
+        }
+
+    }
+    public static class MyLoggerBundle extends MyBundle {
+
+    }
+
+    static enum TestCases {NOSECURITY, NOPERMISSIONS, WITHPERMISSIONS};
+
+    static void setSecurityManager() {
+        if (System.getSecurityManager() == null) {
+            Policy.setPolicy(new SimplePolicy(allowControl, allowAccess));
+            System.setSecurityManager(new SecurityManager());
+        }
+    }
+
+    static LoggerFinder getLoggerFinder(Class<?> expectedClass,
+            String errorPolicy, boolean singleton) {
+        LoggerFinder provider = null;
+        try {
+            TestLoggerFinder.sequencer.incrementAndGet();
+            provider = LoggerFinder.getLoggerFinder();
+            if (TestLoggerFinder.fails.get() || singleton) {
+                if ("ERROR".equals(errorPolicy.toUpperCase(Locale.ROOT))) {
+                    throw new RuntimeException("Expected exception not thrown");
+                } else if ("WARNING".equals(errorPolicy.toUpperCase(Locale.ROOT))) {
+                    String warning = ErrorStream.errorStream.peek();
+                    if (!warning.contains("WARNING: Failed to instantiate LoggerFinder provider; Using default.")) {
+                        throw new RuntimeException("Expected message not found. Error stream contained: " + warning);
+                    }
+                } else if ("DEBUG".equals(errorPolicy.toUpperCase(Locale.ROOT))) {
+                    String warning = ErrorStream.errorStream.peek();
+                    if (!warning.contains("WARNING: Failed to instantiate LoggerFinder provider; Using default.")) {
+                        throw new RuntimeException("Expected message not found. Error stream contained: " + warning);
+                    }
+                    if (!warning.contains("WARNING: Exception raised trying to instantiate LoggerFinder")) {
+                        throw new RuntimeException("Expected message not found. Error stream contained: " + warning);
+                    }
+                    if (TestLoggerFinder.fails.get()) {
+                        if (!warning.contains("java.util.ServiceConfigurationError: java.lang.System$LoggerFinder: Provider LoggerFinderLoaderTest$BaseLoggerFinder could not be instantiated")) {
+                            throw new RuntimeException("Expected message not found. Error stream contained: " + warning);
+                        }
+                    } else if (singleton) {
+                        if (!warning.contains("java.util.ServiceConfigurationError: More than on LoggerFinder implementation")) {
+                            throw new RuntimeException("Expected message not found. Error stream contained: " + warning);
+                        }
+                    }
+                } else if ("QUIET".equals(errorPolicy.toUpperCase(Locale.ROOT))) {
+                    if (!ErrorStream.errorStream.peek().isEmpty()) {
+                        throw new RuntimeException("Unexpected error message found: "
+                                + ErrorStream.errorStream.peek());
+                    }
+                }
+            }
+        } catch(AccessControlException a) {
+            throw a;
+        } catch(Throwable t) {
+            if (TestLoggerFinder.fails.get() || singleton) {
+                // must check System.err
+                if ("ERROR".equals(errorPolicy.toUpperCase(Locale.ROOT))) {
+                    provider = LoggerFinder.getLoggerFinder();
+                } else {
+                    Throwable orig = t.getCause();
+                    while (orig != null && orig.getCause() != null) orig = orig.getCause();
+                    if (orig != null) orig.printStackTrace(ErrorStream.err);
+                    throw new RuntimeException("Unexpected exception: " + t, t);
+                }
+            } else {
+                throw new RuntimeException("Unexpected exception: " + t, t);
+            }
+        }
+        expectedClass.cast(provider);
+        ErrorStream.errorStream.store();
+        System.out.println("*** Actual LoggerFinder class is: " + provider.getClass().getName());
+        return provider;
+    }
+
+
+    static class ErrorStream extends PrintStream {
+
+        static AtomicBoolean forward = new AtomicBoolean();
+        ByteArrayOutputStream out;
+        String saved = "";
+        public ErrorStream(ByteArrayOutputStream out) {
+            super(out);
+            this.out = out;
+        }
+
+        @Override
+        public void write(int b) {
+            super.write(b);
+            if (forward.get()) err.write(b);
+        }
+
+        @Override
+        public void write(byte[] b) throws IOException {
+            super.write(b);
+            if (forward.get()) err.write(b);
+        }
+
+        @Override
+        public void write(byte[] buf, int off, int len) {
+            super.write(buf, off, len);
+            if (forward.get()) err.write(buf, off, len);
+        }
+
+        public String peek() {
+            flush();
+            return out.toString();
+        }
+
+        public String drain() {
+            flush();
+            String res = out.toString();
+            out.reset();
+            return res;
+        }
+
+        public void store() {
+            flush();
+            saved = out.toString();
+            out.reset();
+        }
+
+        public void restore() {
+            out.reset();
+            try {
+                out.write(saved.getBytes());
+            } catch(IOException io) {
+                throw new UncheckedIOException(io);
+            }
+        }
+
+        static final PrintStream err = System.err;
+        static final ErrorStream errorStream = new ErrorStream(new ByteArrayOutputStream());
+    }
+
+    private static StringBuilder appendProperty(StringBuilder b, String name) {
+        String value = System.getProperty(name);
+        if (value == null) return b;
+        return b.append(name).append("=").append(value).append('\n');
+    }
+
+    public static void main(String[] args) {
+        if (args.length == 0) {
+            args = new String[] {
+                "NOSECURITY",
+                "NOPERMISSIONS",
+                "WITHPERMISSIONS"
+            };
+        }
+        Locale.setDefault(Locale.ENGLISH);
+        System.setErr(ErrorStream.errorStream);
+        System.setProperty("jdk.logger.packages", TestLoggerFinder.LoggerImpl.class.getName());
+        //System.setProperty("jdk.logger.finder.error", "ERROR");
+        //System.setProperty("jdk.logger.finder.singleton", "true");
+        //System.setProperty("test.fails", "true");
+        TestLoggerFinder.fails.set(Boolean.getBoolean("test.fails"));
+        StringBuilder c = new StringBuilder();
+        appendProperty(c, "jdk.logger.packages");
+        appendProperty(c, "jdk.logger.finder.error");
+        appendProperty(c, "jdk.logger.finder.singleton");
+        appendProperty(c, "test.fails");
+        TestLoggerFinder.conf.set(c.toString());
+        try {
+            test(args);
+        } finally {
+            try {
+                System.setErr(ErrorStream.err);
+            } catch (Error | RuntimeException x) {
+                x.printStackTrace(ErrorStream.err);
+            }
+        }
+    }
+
+
+    public static void test(String[] args) {
+
+        final String errorPolicy =  System.getProperty("jdk.logger.finder.error", "WARNING");
+        final Boolean ensureSingleton = Boolean.getBoolean("jdk.logger.finder.singleton");
+
+        final Class<?> expectedClass =
+                TestLoggerFinder.fails.get() || ensureSingleton
+                ? jdk.internal.logger.DefaultLoggerFinder.class
+                : TestLoggerFinder.class;
+
+        System.out.println("Declared provider class: " + providerClass[0]
+                + "[" + providerClass[0].getClassLoader() + "]");
+
+        if (!TestLoggerFinder.fails.get()) {
+            ServiceLoader<LoggerFinder> serviceLoader =
+                ServiceLoader.load(LoggerFinder.class, ClassLoader.getSystemClassLoader());
+            Iterator<LoggerFinder> iterator = serviceLoader.iterator();
+            Object firstProvider = iterator.next();
+            if (!firstProvider.getClass().getName().equals("LoggerFinderLoaderTest$BaseLoggerFinder")) {
+                throw new RuntimeException("Unexpected provider: " + firstProvider.getClass().getName());
+            }
+            if (!iterator.hasNext()) {
+                throw new RuntimeException("Expected two providers");
+            }
+        }
+
+        Stream.of(args).map(TestCases::valueOf).forEach((testCase) -> {
+            LoggerFinder provider;
+            ErrorStream.errorStream.restore();
+            switch (testCase) {
+                case NOSECURITY:
+                    System.out.println("\n*** Without Security Manager\n");
+                    System.out.println(TestLoggerFinder.conf.get());
+                    provider = getLoggerFinder(expectedClass, errorPolicy, ensureSingleton);
+                    test(provider, true);
+                    System.out.println("Tetscase count: " + TestLoggerFinder.sequencer.get());
+                    break;
+                case NOPERMISSIONS:
+                    System.out.println("\n*** With Security Manager, without permissions\n");
+                    System.out.println(TestLoggerFinder.conf.get());
+                    setSecurityManager();
+                    try {
+                        provider = getLoggerFinder(expectedClass, errorPolicy, ensureSingleton);
+                        throw new RuntimeException("Expected exception not raised");
+                    } catch (AccessControlException x) {
+                        if (!LOGGERFINDER_PERMISSION.equals(x.getPermission())) {
+                            throw new RuntimeException("Unexpected permission check", x);
+                        }
+                        final boolean control = allowControl.get().get();
+                        try {
+                            allowControl.get().set(true);
+                            provider = getLoggerFinder(expectedClass, errorPolicy, ensureSingleton);
+                        } finally {
+                            allowControl.get().set(control);
+                        }
+                    }
+                    test(provider, false);
+                    System.out.println("Tetscase count: " + TestLoggerFinder.sequencer.get());
+                    break;
+                case WITHPERMISSIONS:
+                    System.out.println("\n*** With Security Manager, with control permission\n");
+                    System.out.println(TestLoggerFinder.conf.get());
+                    setSecurityManager();
+                    final boolean control = allowControl.get().get();
+                    try {
+                        allowControl.get().set(true);
+                        provider = getLoggerFinder(expectedClass, errorPolicy, ensureSingleton);
+                        test(provider, true);
+                    } finally {
+                        allowControl.get().set(control);
+                    }
+                    break;
+                default:
+                    throw new RuntimeException("Unknown test case: " + testCase);
+            }
+        });
+        System.out.println("\nPASSED: Tested " + TestLoggerFinder.sequencer.get() + " cases.");
+    }
+
+    public static void test(LoggerFinder provider, boolean hasRequiredPermissions) {
+
+        ResourceBundle loggerBundle = ResourceBundle.getBundle(MyLoggerBundle.class.getName());
+        final Map<Logger, String> loggerDescMap = new HashMap<>();
+
+        System.Logger sysLogger = accessSystemLogger.getLogger("foo");
+        loggerDescMap.put(sysLogger, "accessSystemLogger.getLogger(\"foo\")");
+        System.Logger localizedSysLogger = accessSystemLogger.getLogger("fox", loggerBundle);
+        loggerDescMap.put(localizedSysLogger, "accessSystemLogger.getLogger(\"fox\", loggerBundle)");
+        System.Logger appLogger = System.getLogger("bar");
+        loggerDescMap.put(appLogger,"System.getLogger(\"bar\")");
+        System.Logger localizedAppLogger = System.getLogger("baz", loggerBundle);
+        loggerDescMap.put(localizedAppLogger,"System.getLogger(\"baz\", loggerBundle)");
+
+        testLogger(provider, loggerDescMap, "foo", null, sysLogger);
+        testLogger(provider, loggerDescMap, "foo", loggerBundle, localizedSysLogger);
+        testLogger(provider, loggerDescMap, "foo", null, appLogger);
+        testLogger(provider, loggerDescMap, "foo", loggerBundle, localizedAppLogger);
+    }
+
+    public static class Foo {
+
+    }
+
+    static void verbose(String msg) {
+       if (VERBOSE) {
+           System.out.println(msg);
+       }
+    }
+
+    // Calls the 8 methods defined on Logger and verify the
+    // parameters received by the underlying TestProvider.LoggerImpl
+    // logger.
+    private static void testLogger(LoggerFinder provider,
+            Map<Logger, String> loggerDescMap,
+            String name,
+            ResourceBundle loggerBundle,
+            Logger logger) {
+
+        System.out.println("Testing " + loggerDescMap.get(logger) + " [" + logger +"]");
+        AtomicLong sequencer = TestLoggerFinder.sequencer;
+
+        Foo foo = new Foo();
+        String fooMsg = foo.toString();
+        for (Level loggerLevel : EnumSet.of(Level.INFO)) {
+            for (Level messageLevel : Level.values()) {
+                ErrorStream.errorStream.drain();
+                String desc = "logger.log(messageLevel, foo): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                sequencer.incrementAndGet();
+                logger.log(messageLevel, foo);
+                if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
+                    if (!ErrorStream.errorStream.peek().isEmpty()) {
+                        throw new RuntimeException("unexpected event in queue for "
+                                + desc +": " + "\n\t" + ErrorStream.errorStream.drain());
+                    }
+                } else {
+                    String logged = ErrorStream.errorStream.drain();
+                    if (!logged.contains("LoggerFinderLoaderTest testLogger")
+                        || !logged.contains(messageLevel.getName() + ": " + fooMsg)) {
+                        throw new RuntimeException("mismatch for " + desc
+                                + "\n\texpected:" + "\n<<<<\n"
+                                + "[date] LoggerFinderLoaderTest testLogger\n"
+                                + messageLevel.getName() + " " + fooMsg
+                                + "\n>>>>"
+                                + "\n\t  actual:"
+                                + "\n<<<<\n" + logged + ">>>>\n");
+                    } else {
+                        verbose("Got expected results for "
+                                + desc + "\n<<<<\n" + logged + ">>>>\n");
+                    }
+                }
+            }
+        }
+
+        String msg = "blah";
+        for (Level loggerLevel : EnumSet.of(Level.INFO)) {
+            for (Level messageLevel : Level.values()) {
+                String desc = "logger.log(messageLevel, \"blah\"): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                sequencer.incrementAndGet();
+                logger.log(messageLevel, msg);
+                if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
+                    if (!ErrorStream.errorStream.peek().isEmpty()) {
+                        throw new RuntimeException("unexpected event in queue for "
+                                + desc +": " + "\n\t" + ErrorStream.errorStream.drain());
+                    }
+                } else {
+                    String logged = ErrorStream.errorStream.drain();
+                    String msgText = loggerBundle == null ? msg : loggerBundle.getString(msg);
+                    if (!logged.contains("LoggerFinderLoaderTest testLogger")
+                        || !logged.contains(messageLevel.getName() + ": " + msgText)) {
+                        throw new RuntimeException("mismatch for " + desc
+                                + "\n\texpected:" + "\n<<<<\n"
+                                + "[date] LoggerFinderLoaderTest testLogger\n"
+                                + messageLevel.getName() + " " + msgText
+                                + "\n>>>>"
+                                + "\n\t  actual:"
+                                + "\n<<<<\n" + logged + ">>>>\n");
+                    } else {
+                        verbose("Got expected results for "
+                                + desc + "\n<<<<\n" + logged + ">>>>\n");
+                    }
+                }
+            }
+        }
+
+        Supplier<String> fooSupplier = new Supplier<String>() {
+            @Override
+            public String get() {
+                return this.toString();
+            }
+        };
+
+        for (Level loggerLevel : EnumSet.of(Level.INFO)) {
+            for (Level messageLevel : Level.values()) {
+                String desc = "logger.log(messageLevel, fooSupplier): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                sequencer.incrementAndGet();
+                logger.log(messageLevel, fooSupplier);
+                if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
+                    if (!ErrorStream.errorStream.peek().isEmpty()) {
+                        throw new RuntimeException("unexpected event in queue for "
+                                + desc +": " + "\n\t" + ErrorStream.errorStream.drain());
+                    }
+                } else {
+                    String logged = ErrorStream.errorStream.drain();
+                    if (!logged.contains("LoggerFinderLoaderTest testLogger")
+                        || !logged.contains(messageLevel.getName() + ": " + fooSupplier.get())) {
+                        throw new RuntimeException("mismatch for " + desc
+                                + "\n\texpected:" + "\n<<<<\n"
+                                + "[date] LoggerFinderLoaderTest testLogger\n"
+                                + messageLevel.getName() + " " + fooSupplier.get()
+                                + "\n>>>>"
+                                + "\n\t  actual:"
+                                + "\n<<<<\n" + logged + ">>>>\n");
+                    } else {
+                        verbose("Got expected results for "
+                                + desc + "\n<<<<\n" + logged + ">>>>\n");
+                    }
+                }
+            }
+        }
+
+
+        String format = "two params [{1} {2}]";
+        Object arg1 = foo;
+        Object arg2 = msg;
+        for (Level loggerLevel : EnumSet.of(Level.INFO)) {
+            for (Level messageLevel : Level.values()) {
+                String desc = "logger.log(messageLevel, format, params...): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                sequencer.incrementAndGet();
+                logger.log(messageLevel, format, foo, msg);
+                if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
+                    if (!ErrorStream.errorStream.peek().isEmpty()) {
+                        throw new RuntimeException("unexpected event in queue for "
+                                + desc +": " + "\n\t" + ErrorStream.errorStream.drain());
+                    }
+                } else {
+                    String logged = ErrorStream.errorStream.drain();
+                    String msgFormat = loggerBundle == null ? format : loggerBundle.getString(format);
+                    String text = java.text.MessageFormat.format(msgFormat, foo, msg);
+                    if (!logged.contains("LoggerFinderLoaderTest testLogger")
+                        || !logged.contains(messageLevel.getName() + ": " + text)) {
+                        throw new RuntimeException("mismatch for " + desc
+                                + "\n\texpected:" + "\n<<<<\n"
+                                + "[date] LoggerFinderLoaderTest testLogger\n"
+                                + messageLevel.getName() + " " + text
+                                + "\n>>>>"
+                                + "\n\t  actual:"
+                                + "\n<<<<\n" + logged + ">>>>\n");
+                    } else {
+                        verbose("Got expected results for "
+                                + desc + "\n<<<<\n" + logged + ">>>>\n");
+                    }
+                }
+            }
+        }
+
+        Throwable thrown = new Exception("OK: log me!");
+        for (Level loggerLevel : EnumSet.of(Level.INFO)) {
+            for (Level messageLevel : Level.values()) {
+                String desc = "logger.log(messageLevel, \"blah\", thrown): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                sequencer.incrementAndGet();
+                logger.log(messageLevel, msg, thrown);
+                if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
+                    if (!ErrorStream.errorStream.peek().isEmpty()) {
+                        throw new RuntimeException("unexpected event in queue for "
+                                + desc +": " + "\n\t" + ErrorStream.errorStream.drain());
+                    }
+                } else {
+                    String logged = ErrorStream.errorStream.drain();
+                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
+                    thrown.printStackTrace(new PrintStream(baos));
+                    String text = baos.toString();
+                    String msgText = loggerBundle == null ? msg : loggerBundle.getString(msg);
+                    if (!logged.contains("LoggerFinderLoaderTest testLogger")
+                        || !logged.contains(messageLevel.getName() + ": " + msgText)
+                        || !logged.contains(text)) {
+                        throw new RuntimeException("mismatch for " + desc
+                                + "\n\texpected:" + "\n<<<<\n"
+                                + "[date] LoggerFinderLoaderTest testLogger\n"
+                                + messageLevel.getName() + " " + msgText +"\n"
+                                + text
+                                + ">>>>"
+                                + "\n\t  actual:"
+                                + "\n<<<<\n" + logged + ">>>>\n");
+                    } else {
+                        verbose("Got expected results for "
+                                + desc + "\n<<<<\n" + logged + ">>>>\n");
+                    }
+                }
+            }
+        }
+
+
+        for (Level loggerLevel : EnumSet.of(Level.INFO)) {
+            for (Level messageLevel : Level.values()) {
+                String desc = "logger.log(messageLevel, thrown, fooSupplier): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                sequencer.incrementAndGet();
+                logger.log(messageLevel, fooSupplier, thrown);
+                if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
+                    if (!ErrorStream.errorStream.peek().isEmpty()) {
+                        throw new RuntimeException("unexpected event in queue for "
+                                + desc +": " + "\n\t" + ErrorStream.errorStream.drain());
+                    }
+                } else {
+                    String logged = ErrorStream.errorStream.drain();
+                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
+                    thrown.printStackTrace(new PrintStream(baos));
+                    String text = baos.toString();
+                    if (!logged.contains("LoggerFinderLoaderTest testLogger")
+                        || !logged.contains(messageLevel.getName() + ": " + fooSupplier.get())
+                        || !logged.contains(text)) {
+                        throw new RuntimeException("mismatch for " + desc
+                                + "\n\texpected:" + "\n<<<<\n"
+                                + "[date] LoggerFinderLoaderTest testLogger\n"
+                                + messageLevel.getName() + " " + fooSupplier.get() +"\n"
+                                + text
+                                + ">>>>"
+                                + "\n\t  actual:"
+                                + "\n<<<<\n" + logged + ">>>>\n");
+                    } else {
+                        verbose("Got expected results for "
+                                + desc + "\n<<<<\n" + logged + ">>>>\n");
+                    }
+                }
+            }
+        }
+
+        ResourceBundle bundle = ResourceBundle.getBundle(MyBundle.class.getName());
+        for (Level loggerLevel : EnumSet.of(Level.INFO)) {
+            for (Level messageLevel : Level.values()) {
+                String desc = "logger.log(messageLevel, bundle, format, params...): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                sequencer.incrementAndGet();
+                logger.log(messageLevel, bundle, format, foo, msg);
+                if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
+                    if (!ErrorStream.errorStream.peek().isEmpty()) {
+                        throw new RuntimeException("unexpected event in queue for "
+                                + desc +": " + "\n\t" + ErrorStream.errorStream.drain());
+                    }
+                } else {
+                    String logged = ErrorStream.errorStream.drain();
+                    String text = java.text.MessageFormat.format(bundle.getString(format), foo, msg);
+                    if (!logged.contains("LoggerFinderLoaderTest testLogger")
+                        || !logged.contains(messageLevel.getName() + ": " + text)) {
+                        throw new RuntimeException("mismatch for " + desc
+                                + "\n\texpected:" + "\n<<<<\n"
+                                + "[date] LoggerFinderLoaderTest testLogger\n"
+                                + messageLevel.getName() + " " + text
+                                + "\n>>>>"
+                                + "\n\t  actual:"
+                                + "\n<<<<\n" + logged + ">>>>\n");
+                    } else {
+                        verbose("Got expected results for "
+                                + desc + "\n<<<<\n" + logged + ">>>>\n");
+                    }
+                }
+            }
+        }
+
+        for (Level loggerLevel : EnumSet.of(Level.INFO)) {
+            for (Level messageLevel : Level.values()) {
+                String desc = "logger.log(messageLevel, bundle, \"blah\", thrown): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                sequencer.incrementAndGet();
+                logger.log(messageLevel, bundle, msg, thrown);
+                if (loggerLevel == Level.OFF || messageLevel == Level.OFF || messageLevel.compareTo(loggerLevel) < 0) {
+                    if (!ErrorStream.errorStream.peek().isEmpty()) {
+                        throw new RuntimeException("unexpected event in queue for "
+                                + desc +": " + "\n\t" + ErrorStream.errorStream.drain());
+                    }
+                } else {
+                    String logged = ErrorStream.errorStream.drain();
+                    String textMsg = bundle.getString(msg);
+                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
+                    thrown.printStackTrace(new PrintStream(baos));
+                    String text = baos.toString();
+                    if (!logged.contains("LoggerFinderLoaderTest testLogger")
+                        || !logged.contains(messageLevel.getName() + ": " + textMsg)
+                        || !logged.contains(text)) {
+                        throw new RuntimeException("mismatch for " + desc
+                                + "\n\texpected:" + "\n<<<<\n"
+                                + "[date] LoggerFinderLoaderTest testLogger\n"
+                                + messageLevel.getName() + " " + textMsg +"\n"
+                                + text
+                                + ">>>>"
+                                + "\n\t  actual:"
+                                + "\n<<<<\n" + logged + ">>>>\n");
+                    } else {
+                        verbose("Got expected results for "
+                                + desc + "\n<<<<\n" + logged + ">>>>\n");
+                    }
+                }
+            }
+        }
+
+    }
+
+    final static class PermissionsBuilder {
+        final Permissions perms;
+        public PermissionsBuilder() {
+            this(new Permissions());
+        }
+        public PermissionsBuilder(Permissions perms) {
+            this.perms = perms;
+        }
+        public PermissionsBuilder add(Permission p) {
+            perms.add(p);
+            return this;
+        }
+        public PermissionsBuilder addAll(PermissionCollection col) {
+            if (col != null) {
+                for (Enumeration<Permission> e = col.elements(); e.hasMoreElements(); ) {
+                    perms.add(e.nextElement());
+                }
+            }
+            return this;
+        }
+        public Permissions toPermissions() {
+            final PermissionsBuilder builder = new PermissionsBuilder();
+            builder.addAll(perms);
+            return builder.perms;
+        }
+    }
+
+    public static class SimplePolicy extends Policy {
+        final static RuntimePermission CONTROL = LOGGERFINDER_PERMISSION;
+        final static RuntimePermission ACCESS = new RuntimePermission("accessClassInPackage.jdk.internal.logger");
+
+        final Permissions permissions;
+        final ThreadLocal<AtomicBoolean> allowControl;
+        final ThreadLocal<AtomicBoolean> allowAccess;
+        public SimplePolicy(ThreadLocal<AtomicBoolean> allowControl, ThreadLocal<AtomicBoolean> allowAccess) {
+            this.allowControl = allowControl;
+            this.allowAccess = allowAccess;
+            permissions = new Permissions();
+            permissions.add(new RuntimePermission("setIO"));
+        }
+
+        Permissions getPermissions() {
+            if (allowControl.get().get() || allowAccess.get().get()) {
+                PermissionsBuilder builder =  new PermissionsBuilder()
+                        .addAll(permissions);
+                if (allowControl.get().get()) {
+                    builder.add(CONTROL);
+                }
+                if (allowAccess.get().get()) {
+                    builder.add(ACCESS);
+                }
+                return builder.toPermissions();
+            }
+            return permissions;
+        }
+
+        @Override
+        public boolean implies(ProtectionDomain domain, Permission permission) {
+            return getPermissions().implies(permission);
+        }
+
+        @Override
+        public PermissionCollection getPermissions(CodeSource codesource) {
+            return new PermissionsBuilder().addAll(getPermissions()).toPermissions();
+        }
+
+        @Override
+        public PermissionCollection getPermissions(ProtectionDomain domain) {
+            return new PermissionsBuilder().addAll(getPermissions()).toPermissions();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/LoggerFinder/internal/LoggerFinderLoaderTest/META-INF/services/java.lang.System$LoggerFinder	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,3 @@
+LoggerFinderLoaderTest$BaseLoggerFinder
+LoggerFinderLoaderTest$BaseLoggerFinder2
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/LoggerFinder/internal/PlatformLoggerBridgeTest/CustomSystemClassLoader.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.nio.file.Files;
+import java.security.AllPermission;
+import java.security.Permissions;
+import java.security.ProtectionDomain;
+
+
+/**
+ * A custom ClassLoader to load the concrete LoggerFinder class
+ * with all permissions.
+ *
+ * @author danielfuchs
+ */
+public class CustomSystemClassLoader extends ClassLoader {
+
+
+    Class<?> loggerFinderClass = null;
+
+    public CustomSystemClassLoader() {
+        super();
+    }
+    public CustomSystemClassLoader(ClassLoader parent) {
+        super(parent);
+    }
+
+    private Class<?> defineFinderClass(String name)
+        throws ClassNotFoundException {
+        final Object obj = getClassLoadingLock(name);
+        synchronized(obj) {
+            if (loggerFinderClass != null) return loggerFinderClass;
+
+            URL url = this.getClass().getProtectionDomain().getCodeSource().getLocation();
+            File file = new File(url.getPath(), name+".class");
+            if (file.canRead()) {
+                try {
+                    byte[] b = Files.readAllBytes(file.toPath());
+                    Permissions perms = new Permissions();
+                    perms.add(new AllPermission());
+                    loggerFinderClass = defineClass(
+                            name, b, 0, b.length, new ProtectionDomain(
+                            this.getClass().getProtectionDomain().getCodeSource(),
+                            perms));
+                    System.out.println("Loaded " + name);
+                    return loggerFinderClass;
+                } catch (Throwable ex) {
+                    ex.printStackTrace();
+                    throw new ClassNotFoundException(name, ex);
+                }
+            } else {
+                throw new ClassNotFoundException(name,
+                        new IOException(file.toPath() + ": can't read"));
+            }
+        }
+    }
+
+    @Override
+    public synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
+        if (name.endsWith("$LogProducerFinder")) {
+            Class<?> c = defineFinderClass(name);
+            if (resolve) {
+                resolveClass(c);
+            }
+            return c;
+        }
+        return super.loadClass(name, resolve);
+    }
+
+    @Override
+    protected Class<?> findClass(String name) throws ClassNotFoundException {
+        if (name.endsWith("$$LogProducerFinder")) {
+            return defineFinderClass(name);
+        }
+        return super.findClass(name);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/LoggerFinder/internal/PlatformLoggerBridgeTest/META-INF/services/java.lang.System$LoggerFinder	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,1 @@
+PlatformLoggerBridgeTest$LogProducerFinder
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/LoggerFinder/internal/PlatformLoggerBridgeTest/PlatformLoggerBridgeTest.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,876 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+import java.security.AccessControlException;
+import java.security.AccessController;
+import java.security.CodeSource;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.Permissions;
+import java.security.Policy;
+import java.security.PrivilegedAction;
+import java.security.ProtectionDomain;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Queue;
+import java.util.ResourceBundle;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.Supplier;
+import java.util.logging.Handler;
+import java.util.logging.LogRecord;
+import java.lang.System.LoggerFinder;
+import java.lang.System.Logger;
+import java.lang.System.Logger.Level;
+import java.util.stream.Stream;
+import sun.util.logging.PlatformLogger;
+
+/**
+ * @test
+ * @bug     8140364
+ * @summary JDK implementation specific unit test for JDK internal artifacts.
+ *          Tests all bridge methods from PlatformLogger with the a custom
+ *          backend whose loggers implement PlatformLogger.Bridge.
+ * @modules java.base/sun.util.logging
+ * @build CustomSystemClassLoader PlatformLoggerBridgeTest
+ * @run  main/othervm -Djava.system.class.loader=CustomSystemClassLoader PlatformLoggerBridgeTest NOSECURITY
+ * @run  main/othervm -Djava.system.class.loader=CustomSystemClassLoader PlatformLoggerBridgeTest NOPERMISSIONS
+ * @run  main/othervm -Djava.system.class.loader=CustomSystemClassLoader PlatformLoggerBridgeTest WITHPERMISSIONS
+ * @author danielfuchs
+ */
+public class PlatformLoggerBridgeTest {
+
+    static final RuntimePermission LOGGERFINDER_PERMISSION =
+                new RuntimePermission("loggerFinder");
+    final static AtomicLong sequencer = new AtomicLong();
+    final static boolean VERBOSE = false;
+    static final ThreadLocal<AtomicBoolean> allowControl = new ThreadLocal<AtomicBoolean>() {
+        @Override
+        protected AtomicBoolean initialValue() {
+            return  new AtomicBoolean(false);
+        }
+    };
+    static final ThreadLocal<AtomicBoolean> allowAccess = new ThreadLocal<AtomicBoolean>() {
+        @Override
+        protected AtomicBoolean initialValue() {
+            return  new AtomicBoolean(false);
+        }
+    };
+    static final ThreadLocal<AtomicBoolean> allowAll = new ThreadLocal<AtomicBoolean>() {
+        @Override
+        protected AtomicBoolean initialValue() {
+            return  new AtomicBoolean(false);
+        }
+    };
+
+    static final Class<?> providerClass;
+    static {
+        try {
+            // Preload classes before the security manager is on.
+            providerClass = ClassLoader.getSystemClassLoader().loadClass("PlatformLoggerBridgeTest$LogProducerFinder");
+            ((LoggerFinder)providerClass.newInstance()).getLogger("foo", providerClass);
+        } catch (Exception ex) {
+            throw new ExceptionInInitializerError(ex);
+        }
+    }
+
+    public static final Queue<LogEvent> eventQueue = new ArrayBlockingQueue<>(128);
+
+    public static final class LogEvent implements Cloneable {
+
+        public LogEvent() {
+            this(sequencer.getAndIncrement());
+        }
+
+        LogEvent(long sequenceNumber) {
+            this.sequenceNumber = sequenceNumber;
+        }
+
+        long sequenceNumber;
+        boolean isLoggable;
+        String loggerName;
+        sun.util.logging.PlatformLogger.Level level;
+        ResourceBundle bundle;
+        Throwable thrown;
+        Object[] args;
+        String msg;
+        Supplier<String> supplier;
+        String className;
+        String methodName;
+
+        Object[] toArray() {
+            return new Object[] {
+                sequenceNumber,
+                loggerName,
+                level,
+                isLoggable,
+                bundle,
+                msg,
+                supplier,
+                thrown,
+                args,
+                className,
+                methodName,
+            };
+        }
+
+        @Override
+        public String toString() {
+            return Arrays.deepToString(toArray());
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            return obj instanceof LogEvent
+                    && Objects.deepEquals(this.toArray(), ((LogEvent)obj).toArray());
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(toArray());
+        }
+
+        public LogEvent cloneWith(long sequenceNumber)
+                throws CloneNotSupportedException {
+            LogEvent cloned = (LogEvent)super.clone();
+            cloned.sequenceNumber = sequenceNumber;
+            return cloned;
+        }
+
+        public static LogEvent of(long sequenceNumber,
+                boolean isLoggable, String name,
+                sun.util.logging.PlatformLogger.Level level, ResourceBundle bundle,
+                String key, Throwable thrown, Object... params) {
+            return LogEvent.of(sequenceNumber, isLoggable, name,
+                    null, null, level, bundle, key,
+                    thrown, params);
+        }
+        public static LogEvent of(long sequenceNumber,
+                boolean isLoggable, String name,
+                sun.util.logging.PlatformLogger.Level level, ResourceBundle bundle,
+                Supplier<String> supplier, Throwable thrown, Object... params) {
+            return LogEvent.of(sequenceNumber, isLoggable, name,
+                    null, null, level, bundle, supplier,
+                    thrown, params);
+        }
+
+        public static LogEvent of(long sequenceNumber,
+                boolean isLoggable, String name,
+                String className, String methodName,
+                sun.util.logging.PlatformLogger.Level level, ResourceBundle bundle,
+                String key, Throwable thrown, Object... params) {
+            LogEvent evt = new LogEvent(sequenceNumber);
+            evt.loggerName = name;
+            evt.level = level;
+            evt.args = params;
+            evt.bundle = bundle;
+            evt.thrown = thrown;
+            evt.msg = key;
+            evt.isLoggable = isLoggable;
+            evt.className = className;
+            evt.methodName = methodName;
+            return evt;
+        }
+
+        public static LogEvent of(boolean isLoggable, String name,
+                String className, String methodName,
+                sun.util.logging.PlatformLogger.Level level, ResourceBundle bundle,
+                String key, Throwable thrown, Object... params) {
+            return LogEvent.of(sequencer.getAndIncrement(), isLoggable, name,
+                    className, methodName, level, bundle, key, thrown, params);
+        }
+
+        public static LogEvent of(long sequenceNumber,
+                boolean isLoggable, String name,
+                String className, String methodName,
+                sun.util.logging.PlatformLogger.Level level, ResourceBundle bundle,
+                Supplier<String> supplier, Throwable thrown, Object... params) {
+            LogEvent evt = new LogEvent(sequenceNumber);
+            evt.loggerName = name;
+            evt.level = level;
+            evt.args = params;
+            evt.bundle = bundle;
+            evt.thrown = thrown;
+            evt.supplier = supplier;
+            evt.isLoggable = isLoggable;
+            evt.className = className;
+            evt.methodName = methodName;
+            return evt;
+        }
+
+        public static LogEvent of(boolean isLoggable, String name,
+                String className, String methodName,
+                sun.util.logging.PlatformLogger.Level level, ResourceBundle bundle,
+                Supplier<String> supplier, Throwable thrown, Object... params) {
+            return LogEvent.of(sequencer.getAndIncrement(), isLoggable, name,
+                    className, methodName, level, bundle, supplier, thrown, params);
+        }
+
+    }
+
+    public static class LogProducerFinder extends LoggerFinder {
+        static final RuntimePermission LOGGERFINDER_PERMISSION =
+                new RuntimePermission("loggerFinder");
+        final ConcurrentHashMap<String, LoggerImpl> system = new ConcurrentHashMap<>();
+        final ConcurrentHashMap<String, LoggerImpl> user = new ConcurrentHashMap<>();
+
+        public class LoggerImpl implements Logger, PlatformLogger.Bridge {
+            private final String name;
+            private sun.util.logging.PlatformLogger.Level level = sun.util.logging.PlatformLogger.Level.INFO;
+            private sun.util.logging.PlatformLogger.Level OFF = sun.util.logging.PlatformLogger.Level.OFF;
+            private sun.util.logging.PlatformLogger.Level FINE = sun.util.logging.PlatformLogger.Level.FINE;
+            private sun.util.logging.PlatformLogger.Level FINER = sun.util.logging.PlatformLogger.Level.FINER;
+            private sun.util.logging.PlatformLogger.Level FINEST = sun.util.logging.PlatformLogger.Level.FINEST;
+            private sun.util.logging.PlatformLogger.Level CONFIG = sun.util.logging.PlatformLogger.Level.CONFIG;
+            private sun.util.logging.PlatformLogger.Level INFO = sun.util.logging.PlatformLogger.Level.INFO;
+            private sun.util.logging.PlatformLogger.Level WARNING = sun.util.logging.PlatformLogger.Level.WARNING;
+            private sun.util.logging.PlatformLogger.Level SEVERE = sun.util.logging.PlatformLogger.Level.SEVERE;
+
+            public LoggerImpl(String name) {
+                this.name = name;
+            }
+
+            @Override
+            public String getName() {
+                return name;
+            }
+
+            @Override
+            public boolean isLoggable(Level level) {
+                return this.level != OFF && this.level.intValue() <= level.getSeverity();
+            }
+
+            @Override
+            public void log(Level level, ResourceBundle bundle,
+                    String key, Throwable thrown) {
+                throw new UnsupportedOperationException();
+            }
+
+            @Override
+            public void log(Level level, ResourceBundle bundle,
+                    String format, Object... params) {
+                throw new UnsupportedOperationException();
+            }
+
+            void log(LogEvent event) {
+                eventQueue.add(event);
+            }
+
+            @Override
+            public void log(Level level, Supplier<String> msgSupplier) {
+                throw new UnsupportedOperationException();
+            }
+
+            @Override
+            public void log(Level level, Supplier<String> msgSupplier,
+                            Throwable thrown) {
+                throw new UnsupportedOperationException();
+            }
+
+            @Override
+            public void log(sun.util.logging.PlatformLogger.Level level, String msg) {
+                log(LogEvent.of(isLoggable(level), name, null, null,
+                        level, null, msg, null, (Object[])null));
+            }
+
+            @Override
+            public void log(sun.util.logging.PlatformLogger.Level level,
+                    Supplier<String> msgSupplier) {
+                log(LogEvent.of(isLoggable(level), name, null, null,
+                        level, null, msgSupplier, null, (Object[])null));
+            }
+
+            @Override
+            public void log(sun.util.logging.PlatformLogger.Level level, String msg,
+                    Object... params) {
+                log(LogEvent.of(isLoggable(level), name, null, null,
+                        level, null, msg, null, params));
+            }
+
+            @Override
+            public void log(sun.util.logging.PlatformLogger.Level level, String msg,
+                    Throwable thrown) {
+                log(LogEvent.of(isLoggable(level), name, null, null,
+                        level, null, msg, thrown, (Object[])null));
+            }
+
+            @Override
+            public void log(sun.util.logging.PlatformLogger.Level level, Throwable thrown,
+                    Supplier<String> msgSupplier) {
+                log(LogEvent.of(isLoggable(level), name, null, null,
+                        level, null, msgSupplier, thrown, (Object[])null));
+            }
+
+            @Override
+            public void logp(sun.util.logging.PlatformLogger.Level level, String sourceClass,
+                    String sourceMethod, String msg) {
+                log(LogEvent.of(isLoggable(level), name,
+                        sourceClass, sourceMethod,
+                        level, null, msg, null, (Object[])null));
+            }
+
+            @Override
+            public void logp(sun.util.logging.PlatformLogger.Level level, String sourceClass,
+                    String sourceMethod, Supplier<String> msgSupplier) {
+                log(LogEvent.of(isLoggable(level), name,
+                        sourceClass, sourceMethod,
+                        level, null, msgSupplier, null, (Object[])null));
+            }
+
+            @Override
+            public void logp(sun.util.logging.PlatformLogger.Level level, String sourceClass,
+                    String sourceMethod, String msg, Object... params) {
+                log(LogEvent.of(isLoggable(level), name,
+                        sourceClass, sourceMethod,
+                        level, null, msg, null, params));
+            }
+
+            @Override
+            public void logp(sun.util.logging.PlatformLogger.Level level, String sourceClass,
+                    String sourceMethod, String msg, Throwable thrown) {
+                log(LogEvent.of(isLoggable(level), name,
+                        sourceClass, sourceMethod,
+                        level, null, msg, thrown, (Object[])null));
+            }
+
+            @Override
+            public void logp(sun.util.logging.PlatformLogger.Level level, String sourceClass,
+                    String sourceMethod, Throwable thrown,
+                    Supplier<String> msgSupplier) {
+                log(LogEvent.of(isLoggable(level), name,
+                        sourceClass, sourceMethod,
+                        level, null, msgSupplier, thrown, (Object[])null));
+            }
+
+            @Override
+            public void logrb(sun.util.logging.PlatformLogger.Level level, String sourceClass,
+                    String sourceMethod, ResourceBundle bundle, String msg,
+                    Object... params) {
+                log(LogEvent.of(isLoggable(level), name,
+                        sourceClass, sourceMethod,
+                        level, bundle, msg, null, params));
+            }
+
+            @Override
+            public void logrb(sun.util.logging.PlatformLogger.Level level, ResourceBundle bundle,
+                    String msg, Object... params) {
+                log(LogEvent.of(isLoggable(level), name, null, null,
+                        level, bundle, msg, null, params));
+            }
+
+            @Override
+            public void logrb(sun.util.logging.PlatformLogger.Level level, String sourceClass,
+                    String sourceMethod, ResourceBundle bundle, String msg,
+                    Throwable thrown) {
+                log(LogEvent.of(isLoggable(level), name,
+                        sourceClass, sourceMethod,
+                        level, bundle, msg, thrown, (Object[])null));
+            }
+
+            @Override
+            public void logrb(sun.util.logging.PlatformLogger.Level level, ResourceBundle bundle,
+                    String msg, Throwable thrown) {
+                log(LogEvent.of(isLoggable(level), name, null, null,
+                        level, bundle, msg, thrown, (Object[])null));
+            }
+
+            @Override
+            public boolean isLoggable(sun.util.logging.PlatformLogger.Level level) {
+                return this.level != OFF && level.intValue()
+                        >= this.level.intValue();
+            }
+
+            @Override
+            public boolean isEnabled() {
+                return this.level != OFF;
+            }
+
+
+        }
+
+        @Override
+        public Logger getLogger(String name, Class<?> caller) {
+            SecurityManager sm = System.getSecurityManager();
+            if (sm != null) {
+                sm.checkPermission(LOGGERFINDER_PERMISSION);
+            }
+            PrivilegedAction<ClassLoader> pa = () -> caller.getClassLoader();
+            ClassLoader callerLoader = AccessController.doPrivileged(pa);
+            if (callerLoader == null) {
+                return system.computeIfAbsent(name, (n) -> new LoggerImpl(n));
+            } else {
+                return user.computeIfAbsent(name, (n) -> new LoggerImpl(n));
+            }
+        }
+    }
+
+    static final sun.util.logging.PlatformLogger.Level[] julLevels = {
+        sun.util.logging.PlatformLogger.Level.ALL,
+        sun.util.logging.PlatformLogger.Level.FINEST,
+        sun.util.logging.PlatformLogger.Level.FINER,
+        sun.util.logging.PlatformLogger.Level.FINE,
+        sun.util.logging.PlatformLogger.Level.CONFIG,
+        sun.util.logging.PlatformLogger.Level.INFO,
+        sun.util.logging.PlatformLogger.Level.WARNING,
+        sun.util.logging.PlatformLogger.Level.SEVERE,
+        sun.util.logging.PlatformLogger.Level.OFF,
+    };
+
+    public static class MyBundle extends ResourceBundle {
+
+        final ConcurrentHashMap map = new ConcurrentHashMap();
+
+        @Override
+        protected Object handleGetObject(String key) {
+            if (key.contains(" (translated)")) {
+                throw new RuntimeException("Unexpected key: " + key);
+            }
+            return map.computeIfAbsent(key, k -> k + " (translated)");
+        }
+
+        @Override
+        public Enumeration<String> getKeys() {
+            return Collections.enumeration(map.keySet());
+        }
+
+    }
+
+    public static class MyHandler extends Handler {
+
+        @Override
+        public java.util.logging.Level getLevel() {
+            return java.util.logging.Level.ALL;
+        }
+
+        @Override
+        public void publish(LogRecord record) {
+            eventQueue.add(LogEvent.of(sequencer.getAndIncrement(),
+                    true, record.getLoggerName(),
+                    record.getSourceClassName(),
+                    record.getSourceMethodName(),
+                    PlatformLogger.Level.valueOf(record.getLevel().getName()),
+                    record.getResourceBundle(), record.getMessage(),
+                    record.getThrown(), record.getParameters()));
+        }
+        @Override
+        public void flush() {
+        }
+        @Override
+        public void close() throws SecurityException {
+        }
+
+    }
+
+    public static class MyLoggerBundle extends MyBundle {
+
+    }
+
+    static enum TestCases {NOSECURITY, NOPERMISSIONS, WITHPERMISSIONS};
+
+    static void setSecurityManager() {
+        if (System.getSecurityManager() == null) {
+            Policy.setPolicy(new SimplePolicy(allowControl, allowAccess, allowAll));
+            System.setSecurityManager(new SecurityManager());
+        }
+    }
+
+    public static void main(String[] args) {
+        if (args.length == 0)
+            args = new String[] {
+                //"NOSECURITY",
+                "NOPERMISSIONS",
+                "WITHPERMISSIONS"
+            };
+
+
+        Stream.of(args).map(TestCases::valueOf).forEach((testCase) -> {
+            LoggerFinder provider;
+            switch (testCase) {
+                case NOSECURITY:
+                    System.out.println("\n*** Without Security Manager\n");
+                    provider = LoggerFinder.getLoggerFinder();
+                    test(provider, true);
+                    System.out.println("Tetscase count: " + sequencer.get());
+                    break;
+                case NOPERMISSIONS:
+                    System.out.println("\n*** With Security Manager, without permissions\n");
+                    setSecurityManager();
+                    try {
+                        provider = LoggerFinder.getLoggerFinder();
+                        throw new RuntimeException("Expected exception not raised");
+                    } catch (AccessControlException x) {
+                        if (!LOGGERFINDER_PERMISSION.equals(x.getPermission())) {
+                            throw new RuntimeException("Unexpected permission check", x);
+                        }
+                        final boolean control = allowControl.get().get();
+                        try {
+                            allowControl.get().set(true);
+                            provider = LoggerFinder.getLoggerFinder();
+                        } finally {
+                            allowControl.get().set(control);
+                        }
+                    }
+                    test(provider, false);
+                    System.out.println("Tetscase count: " + sequencer.get());
+                    break;
+                case WITHPERMISSIONS:
+                    System.out.println("\n*** With Security Manager, with access permission\n");
+                    setSecurityManager();
+                    final boolean control = allowControl.get().get();
+                    try {
+                        allowControl.get().set(true);
+                        provider = LoggerFinder.getLoggerFinder();
+                    } finally {
+                        allowControl.get().set(control);
+                    }
+                    final boolean access = allowAccess.get().get();
+                    try {
+                        allowAccess.get().set(true);
+                        test(provider, true);
+                    } finally {
+                        allowAccess.get().set(access);
+                    }
+                    break;
+                default:
+                    throw new RuntimeException("Unknown test case: " + testCase);
+            }
+        });
+        System.out.println("\nPASSED: Tested " + sequencer.get() + " cases.");
+    }
+
+    public static void test(LoggerFinder provider, boolean hasRequiredPermissions) {
+
+        final Map<PlatformLogger, String> loggerDescMap = new HashMap<>();
+
+        PlatformLogger sysLogger1 = null;
+        try {
+            sysLogger1 = PlatformLogger.getLogger("foo");
+            loggerDescMap.put(sysLogger1, "PlatformLogger.getLogger(\"foo\")");
+            if (!hasRequiredPermissions) {
+                throw new RuntimeException("Managed to obtain a system logger without permission");
+            }
+        } catch (AccessControlException acx) {
+            if (hasRequiredPermissions) {
+                throw new RuntimeException("Unexpected security exception: ", acx);
+            }
+            if (!acx.getPermission().equals(SimplePolicy.ACCESS_LOGGING)) {
+                throw new RuntimeException("Unexpected permission in exception: " + acx, acx);
+            }
+            final boolean old = allowAccess.get().get();
+            allowAccess.get().set(true);
+            try {
+                sysLogger1 = PlatformLogger.getLogger("foo");
+                loggerDescMap.put(sysLogger1, "PlatformLogger.getLogger(\"foo\")");
+            } finally {
+                allowAccess.get().set(old);
+            }
+            System.out.println("Got expected exception for system logger: " + acx);
+        }
+
+        final LogProducerFinder.LoggerImpl sysSink;
+        boolean old = allowControl.get().get();
+        allowControl.get().set(true);
+        try {
+           sysSink = LogProducerFinder.LoggerImpl.class.cast(
+                        provider.getLogger("foo", Thread.class));
+        } finally {
+            allowControl.get().set(old);
+        }
+
+        testLogger(provider, loggerDescMap, "foo", null, sysLogger1, sysSink);
+    }
+
+    public static class Foo {
+
+    }
+
+    static void verbose(String msg) {
+       if (VERBOSE) {
+           System.out.println(msg);
+       }
+    }
+
+    static void checkLogEvent(LoggerFinder provider, String desc,
+            LogEvent expected) {
+        LogEvent actual =  eventQueue.poll();
+        if (!expected.equals(actual)) {
+            throw new RuntimeException("mismatch for " + desc
+                    + "\n\texpected=" + expected
+                    + "\n\t  actual=" + actual);
+        } else {
+            verbose("Got expected results for "
+                    + desc + "\n\t" + expected);
+        }
+    }
+
+    static void checkLogEvent(LoggerFinder provider, String desc,
+            LogEvent expected, boolean expectNotNull) {
+        LogEvent actual =  eventQueue.poll();
+        if (actual == null && !expectNotNull) return;
+        if (actual != null && !expectNotNull) {
+            throw new RuntimeException("Unexpected log event found for " + desc
+                + "\n\tgot: " + actual);
+        }
+        if (!expected.equals(actual)) {
+            throw new RuntimeException("mismatch for " + desc
+                    + "\n\texpected=" + expected
+                    + "\n\t  actual=" + actual);
+        } else {
+            verbose("Got expected results for "
+                    + desc + "\n\t" + expected);
+        }
+    }
+
+    static void setLevel( LogProducerFinder.LoggerImpl sink,
+            sun.util.logging.PlatformLogger.Level loggerLevel) {
+        sink.level = loggerLevel;
+    }
+
+    // Calls the methods defined on LogProducer and verify the
+    // parameters received by the underlying LogProducerFinder.LoggerImpl
+    // logger.
+    private static void testLogger(LoggerFinder provider,
+            Map<PlatformLogger, String> loggerDescMap,
+            String name,
+            ResourceBundle loggerBundle,
+            PlatformLogger logger,
+            LogProducerFinder.LoggerImpl sink) {
+
+        System.out.println("Testing " + loggerDescMap.get(logger) + " [" + logger +"]");
+        final sun.util.logging.PlatformLogger.Level OFF = sun.util.logging.PlatformLogger.Level.OFF;
+        final sun.util.logging.PlatformLogger.Level ALL = sun.util.logging.PlatformLogger.Level.OFF;
+
+        Foo foo = new Foo();
+        String fooMsg = foo.toString();
+        System.out.println("\tlogger.log(messageLevel, fooMsg)");
+        System.out.println("\tlogger.<level>(fooMsg)");
+        for (sun.util.logging.PlatformLogger.Level loggerLevel : julLevels) {
+            setLevel(sink, loggerLevel);
+            for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
+                if (messageLevel == ALL || messageLevel == OFF) continue;
+                LogEvent expected =
+                        LogEvent.of(
+                            sequencer.get(),
+                            loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
+                            name, messageLevel, loggerBundle,
+                            fooMsg, (Throwable)null, (Object[])null);
+                String desc2 = "logger." + messageLevel.toString().toLowerCase()
+                        + "(fooMsg): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                if (messageLevel == sun.util.logging.PlatformLogger.Level.FINEST) {
+                    logger.finest(fooMsg);
+                    checkLogEvent(provider, desc2, expected);
+                } else if (messageLevel == sun.util.logging.PlatformLogger.Level.FINER) {
+                    logger.finer(fooMsg);
+                    checkLogEvent(provider, desc2, expected);
+                } else if (messageLevel == sun.util.logging.PlatformLogger.Level.FINE) {
+                    logger.fine(fooMsg);
+                    checkLogEvent(provider, desc2, expected);
+                } else if (messageLevel == sun.util.logging.PlatformLogger.Level.CONFIG) {
+                    logger.config(fooMsg);
+                    checkLogEvent(provider, desc2, expected);
+                } else if (messageLevel == sun.util.logging.PlatformLogger.Level.INFO) {
+                    logger.info(fooMsg);
+                    checkLogEvent(provider, desc2, expected);
+                } else if (messageLevel == sun.util.logging.PlatformLogger.Level.WARNING) {
+                    logger.warning(fooMsg);
+                    checkLogEvent(provider, desc2, expected);
+                } else if (messageLevel == sun.util.logging.PlatformLogger.Level.SEVERE) {
+                    logger.severe(fooMsg);
+                    checkLogEvent(provider, desc2, expected);
+                }
+            }
+        }
+
+        String format = "two params [{1} {2}]";
+        Object arg1 = foo;
+        Object arg2 = fooMsg;
+        System.out.println("\tlogger.log(messageLevel, format, arg1, arg2)");
+        for (sun.util.logging.PlatformLogger.Level loggerLevel : julLevels) {
+            setLevel(sink, loggerLevel);
+            for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
+                if (messageLevel == ALL || messageLevel == OFF) continue;
+                LogEvent expected =
+                        LogEvent.of(
+                            sequencer.get(),
+                            loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
+                            name, messageLevel, loggerBundle,
+                            format, (Throwable)null, arg1, arg2);
+                String desc2 = "logger." + messageLevel.toString().toLowerCase()
+                        + "(format, foo, fooMsg): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                if (messageLevel == sun.util.logging.PlatformLogger.Level.FINEST) {
+                    logger.finest(format, arg1, arg2);
+                    checkLogEvent(provider, desc2, expected);
+                } else if (messageLevel == sun.util.logging.PlatformLogger.Level.FINER) {
+                    logger.finer(format, arg1, arg2);
+                    checkLogEvent(provider, desc2, expected);
+                } else if (messageLevel == sun.util.logging.PlatformLogger.Level.FINE) {
+                    logger.fine(format, arg1, arg2);
+                    checkLogEvent(provider, desc2, expected);
+                } else if (messageLevel == sun.util.logging.PlatformLogger.Level.CONFIG) {
+                    logger.config(format, arg1, arg2);
+                    checkLogEvent(provider, desc2, expected);
+                } else if (messageLevel == sun.util.logging.PlatformLogger.Level.INFO) {
+                    logger.info(format, arg1, arg2);
+                    checkLogEvent(provider, desc2, expected);
+                } else if (messageLevel == sun.util.logging.PlatformLogger.Level.WARNING) {
+                    logger.warning(format, arg1, arg2);
+                    checkLogEvent(provider, desc2, expected);
+                } else if (messageLevel == sun.util.logging.PlatformLogger.Level.SEVERE) {
+                    logger.severe(format, arg1, arg2);
+                    checkLogEvent(provider, desc2, expected);
+                }
+            }
+        }
+
+        Throwable thrown = new Exception("OK: log me!");
+        System.out.println("\tlogger.log(messageLevel, fooMsg, thrown)");
+        for (sun.util.logging.PlatformLogger.Level loggerLevel : julLevels) {
+            setLevel(sink, loggerLevel);
+            for (sun.util.logging.PlatformLogger.Level messageLevel :julLevels) {
+                if (messageLevel == ALL || messageLevel == OFF) continue;
+                LogEvent expected =
+                        LogEvent.of(
+                            sequencer.get(),
+                            loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
+                            name, messageLevel, loggerBundle,
+                            fooMsg, thrown, (Object[])null);
+                String desc2 = "logger." + messageLevel.toString().toLowerCase()
+                        + "(fooMsg, thrown): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                if (messageLevel == sun.util.logging.PlatformLogger.Level.FINEST) {
+                    logger.finest(fooMsg, thrown);
+                    checkLogEvent(provider, desc2, expected);
+                } else if (messageLevel == sun.util.logging.PlatformLogger.Level.FINER) {
+                    logger.finer(fooMsg, thrown);
+                    checkLogEvent(provider, desc2, expected);
+                } else if (messageLevel == sun.util.logging.PlatformLogger.Level.FINE) {
+                    logger.fine(fooMsg, thrown);
+                    checkLogEvent(provider, desc2, expected);
+                } else if (messageLevel == sun.util.logging.PlatformLogger.Level.CONFIG) {
+                    logger.config(fooMsg, thrown);
+                    checkLogEvent(provider, desc2, expected);
+                } else if (messageLevel == sun.util.logging.PlatformLogger.Level.INFO) {
+                    logger.info(fooMsg, thrown);
+                    checkLogEvent(provider, desc2, expected);
+                } else if (messageLevel == sun.util.logging.PlatformLogger.Level.WARNING) {
+                    logger.warning(fooMsg, thrown);
+                    checkLogEvent(provider, desc2, expected);
+                } else if (messageLevel == sun.util.logging.PlatformLogger.Level.SEVERE) {
+                    logger.severe(fooMsg, thrown);
+                    checkLogEvent(provider, desc2, expected);
+                }
+            }
+        }
+    }
+
+    final static class PermissionsBuilder {
+        final Permissions perms;
+        public PermissionsBuilder() {
+            this(new Permissions());
+        }
+        public PermissionsBuilder(Permissions perms) {
+            this.perms = perms;
+        }
+        public PermissionsBuilder add(Permission p) {
+            perms.add(p);
+            return this;
+        }
+        public PermissionsBuilder addAll(PermissionCollection col) {
+            if (col != null) {
+                for (Enumeration<Permission> e = col.elements(); e.hasMoreElements(); ) {
+                    perms.add(e.nextElement());
+                }
+            }
+            return this;
+        }
+        public Permissions toPermissions() {
+            final PermissionsBuilder builder = new PermissionsBuilder();
+            builder.addAll(perms);
+            return builder.perms;
+        }
+    }
+
+    public static class SimplePolicy extends Policy {
+        final static RuntimePermission CONTROL = LOGGERFINDER_PERMISSION;
+        final static RuntimePermission ACCESS_LOGGER = new RuntimePermission("accessClassInPackage.jdk.internal.logger");
+        final static RuntimePermission ACCESS_LOGGING = new RuntimePermission("accessClassInPackage.sun.util.logging");
+
+        final Permissions permissions;
+        final Permissions allPermissions;
+        final ThreadLocal<AtomicBoolean> allowControl;
+        final ThreadLocal<AtomicBoolean> allowAccess;
+        final ThreadLocal<AtomicBoolean> allowAll;
+        public SimplePolicy(ThreadLocal<AtomicBoolean> allowControl,
+                ThreadLocal<AtomicBoolean> allowAccess,
+                ThreadLocal<AtomicBoolean> allowAll) {
+            this.allowControl = allowControl;
+            this.allowAccess = allowAccess;
+            this.allowAll = allowAll;
+            permissions = new Permissions();
+            allPermissions = new PermissionsBuilder()
+                    .add(new java.security.AllPermission())
+                    .toPermissions();
+        }
+
+        Permissions getPermissions() {
+            if (allowControl.get().get() || allowAccess.get().get() || allowAll.get().get()) {
+                PermissionsBuilder builder =  new PermissionsBuilder()
+                        .addAll(permissions);
+                if (allowControl.get().get()) {
+                    builder.add(CONTROL);
+                }
+                if (allowAccess.get().get()) {
+                    builder.add(ACCESS_LOGGER);
+                    builder.add(ACCESS_LOGGING);
+                }
+                if (allowAll.get().get()) {
+                    builder.addAll(allPermissions);
+                }
+                return builder.toPermissions();
+            }
+            return permissions;
+        }
+
+        @Override
+        public boolean implies(ProtectionDomain domain, Permission permission) {
+            return getPermissions().implies(permission);
+        }
+
+        @Override
+        public PermissionCollection getPermissions(CodeSource codesource) {
+            return new PermissionsBuilder().addAll(getPermissions()).toPermissions();
+        }
+
+        @Override
+        public PermissionCollection getPermissions(ProtectionDomain domain) {
+            return new PermissionsBuilder().addAll(getPermissions()).toPermissions();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/LoggerFinder/internal/api/LoggerFinderAPITest.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,497 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug     8140364
+ * @author  danielfuchs
+ * @summary JDK implementation specific unit test for JDK internal artifacts.
+ *          Tests the consistency of the LoggerFinder and JDK extensions.
+ * @modules java.base/sun.util.logging
+ *          java.base/jdk.internal.logger
+ * @run  main LoggerFinderAPITest
+ */
+
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.ResourceBundle;
+import java.util.function.Supplier;
+import java.util.logging.ConsoleHandler;
+import java.util.logging.Handler;
+import java.util.logging.LogRecord;
+import java.util.logging.Logger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import sun.util.logging.PlatformLogger;
+
+public class LoggerFinderAPITest {
+
+    static final Class<java.lang.System.Logger> spiLoggerClass
+            = java.lang.System.Logger.class;
+    static final Class<java.lang.System.Logger> jdkLoggerClass
+            = java.lang.System.Logger.class;
+    static final Class<sun.util.logging.PlatformLogger.Bridge> bridgeLoggerClass
+            = sun.util.logging.PlatformLogger.Bridge.class;
+    static final Class<java.util.logging.Logger> julLoggerClass
+            = java.util.logging.Logger.class;
+    static final Class<sun.util.logging.PlatformLogger.Bridge> julLogProducerClass
+            = PlatformLogger.Bridge.class;
+    static final Pattern julLogNames = Pattern.compile(
+            "^((log(p|rb)?)|severe|warning|info|config|fine|finer|finest|isLoggable)$");
+    static final Collection<Method> julLoggerIgnores;
+    static {
+        List<Method> ignores = new ArrayList<>();
+        try {
+            ignores.add(julLoggerClass.getDeclaredMethod("log", LogRecord.class));
+        } catch (NoSuchMethodException | SecurityException ex) {
+            throw new ExceptionInInitializerError(ex);
+        }
+        julLoggerIgnores = Collections.unmodifiableList(ignores);
+    }
+
+
+
+    // Don't require LoggerBridge to have a body for those methods
+    interface LoggerBridgeMethodsWithNoBody extends
+        PlatformLogger.Bridge, java.lang.System.Logger {
+
+        @Override
+        public default String getName() {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        @Override
+        public default boolean isLoggable(PlatformLogger.Level level) {
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+        @Override
+        public default void log(sun.util.logging.PlatformLogger.Level level,
+                         String msg, Throwable thrown) {
+        }
+        @Override
+        public default void log(sun.util.logging.PlatformLogger.Level level,
+                         Throwable thrown, Supplier<String> msgSupplier) {
+        }
+        @Override
+        public default void log(sun.util.logging.PlatformLogger.Level level,
+                         Supplier<String> msgSupplier) {
+        }
+        @Override
+        public default void log(sun.util.logging.PlatformLogger.Level level, String msg) {
+        }
+        @Override
+        public default void log(sun.util.logging.PlatformLogger.Level level,
+                         String format, Object... params) {
+        }
+        @Override
+        public default void logrb(sun.util.logging.PlatformLogger.Level level,
+                         ResourceBundle bundle, String key, Throwable thrown) {
+        }
+        @Override
+        public default void logrb(sun.util.logging.PlatformLogger.Level level,
+                         ResourceBundle bundle, String format, Object... params) {
+        }
+
+        @Override
+        public default void logrb(PlatformLogger.Level level,
+                         String sourceClass, String sourceMethod,
+                         ResourceBundle bundle, String msg, Throwable thrown) {
+        }
+
+        @Override
+        public default void logrb(PlatformLogger.Level level, String sourceClass,
+                         String sourceMethod, ResourceBundle bundle, String msg,
+                         Object... params) {
+        }
+
+        @Override
+        public default void logp(PlatformLogger.Level level, String sourceClass,
+                         String sourceMethod, Supplier<String> msgSupplier) {
+        }
+
+        @Override
+        public default void logp(PlatformLogger.Level level, String sourceClass,
+                         String sourceMethod, String msg, Object... params) {
+        }
+
+        @Override
+        public default void logp(PlatformLogger.Level level, String sourceClass,
+                         String sourceMethod, String msg, Throwable thrown) {
+        }
+
+        @Override
+        public default void logp(PlatformLogger.Level level, String sourceClass,
+                         String sourceMethod, String msg) {
+        }
+
+        @Override
+        public default void logp(PlatformLogger.Level level, String sourceClass,
+                         String sourceMethod, Throwable thrown,
+                         Supplier<String> msgSupplier) {
+        }
+
+        static boolean requiresDefaultBodyFor(Method m) {
+            try {
+                Method m2 = LoggerBridgeMethodsWithNoBody.class
+                        .getDeclaredMethod(m.getName(),
+                        m.getParameterTypes());
+                return !m2.isDefault();
+            } catch (NoSuchMethodException x) {
+                return true;
+            }
+        }
+    }
+
+    final boolean warnDuplicateMappings;
+    public LoggerFinderAPITest(boolean verbose) {
+        this.warnDuplicateMappings = verbose;
+        for (Handler h : Logger.getLogger("").getHandlers()) {
+            if (h instanceof ConsoleHandler) {
+                Logger.getLogger("").removeHandler(h);
+            }
+        }
+        Logger.getLogger("").addHandler( new Handler() {
+            @Override
+            public void publish(LogRecord record) {
+                StringBuilder builder = new StringBuilder();
+                builder.append("GOT LogRecord: ")
+                        .append(record.getLevel().getLocalizedName())
+                        .append(": [").append(record.getLoggerName())
+                        .append("] ").append(record.getSourceClassName())
+                        .append('.')
+                        .append(record.getSourceMethodName()).append(" -> ")
+                        .append(record.getMessage())
+                        .append(' ')
+                        .append(record.getParameters() == null ? ""
+                                : Arrays.toString(record.getParameters()))
+                        ;
+                System.out.println(builder);
+                if (record.getThrown() != null) {
+                    record.getThrown().printStackTrace(System.out);
+                }
+            }
+            @Override public void flush() {}
+            @Override public void close() {}
+        });
+    }
+
+    public Stream<Method> getJulLogMethodStream(Class<?> loggerClass) {
+
+        return Stream.of(loggerClass.getMethods()).filter((x) -> {
+            final Matcher m = julLogNames.matcher(x.getName());
+            return m.matches() ? x.getAnnotation(Deprecated.class) == null : false;
+        });
+    }
+
+    /**
+     * Tells whether a method invocation of 'origin' can be transformed in a
+     * method invocation of 'target'.
+     * This method only look at the parameter signatures, it doesn't look at
+     * the name, nor does it look at the return types.
+     * <p>
+     * Example:
+     * <ul>
+     *     <li>java.util.logging.Logger.log(Level, String, Object) can be invoked as<br>
+         java.util.logging.spi.Logger.log(Level, String, Object...) because the
+         last parameter in 'target' is a varargs.</li>
+     *     <li>java.util.logging.Logger.log(Level, String) can also be invoked as<br>
+         java.util.logging.spi.Logger.log(Level, String, Object...) for the
+         same reason.</li>
+     * </ul>
+     * <p>
+     * The algorithm is tailored for our needs: when the last parameter in the
+     * target is a vararg, and when origin & target have the same number of
+     * parameters, then we consider that the types of the last parameter *must*
+     * match.
+     * <p>
+     * Similarly - we do not consider that o(X x, Y y, Y y) matches t(X x, Y... y)
+     * although strictly speaking, it should...
+     *
+     * @param origin The method in the original class
+     * @param target The correspondent candidate in the target class
+     * @return true if a method invocation of 'origin' can be transformed in a
+     * method invocation of 'target'.
+     */
+    public boolean canBeInvokedAs(Method origin, Method target,
+                                  Map<Class<?>,Class<?>> substitutes) {
+        final Class<?>[] xParams = target.getParameterTypes();
+        final Class<?>[] mParams = Stream.of(origin.getParameterTypes())
+                .map((x) -> substitutes.getOrDefault(x, x))
+                .collect(Collectors.toList()).toArray(new Class<?>[0]);
+        if (Arrays.deepEquals(xParams, mParams)) return true;
+        if (target.isVarArgs()) {
+            if (xParams.length == mParams.length) {
+                if (xParams[xParams.length-1].isArray()) {
+                    return mParams[mParams.length -1].equals(
+                            xParams[xParams.length -1].getComponentType());
+                }
+            } else if (xParams.length == mParams.length + 1) {
+                return Arrays.deepEquals(
+                        Arrays.copyOfRange(xParams, 0, xParams.length-1), mParams);
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Look whether {@code otherClass} has a public method similar to m
+     * @param m
+     * @param otherClass
+     * @return
+     */
+    public Stream<Method> findInvokable(Method m, Class<?> otherClass) {
+        final Map<Class<?>,Class<?>> substitues =
+                Collections.singletonMap(java.util.logging.Level.class,
+                        sun.util.logging.PlatformLogger.Level.class);
+        return Stream.of(otherClass.getMethods())
+                .filter((x) -> m.getName().equals(x.getName()))
+                .filter((x) -> canBeInvokedAs(m, x, substitues));
+    }
+
+    /**
+     * Test that the concrete Logger implementation passed as parameter
+     * overrides all the methods defined by its interface.
+     * @param julLogger A concrete implementation of System.Logger
+     *    whose backend is a JUL Logger.
+     */
+    StringBuilder testDefaultJULLogger(java.lang.System.Logger julLogger) {
+        final StringBuilder errors = new StringBuilder();
+        if (!bridgeLoggerClass.isInstance(julLogger)) {
+            final String errorMsg =
+                    "Logger returned by LoggerFactory.getLogger(\"foo\") is not a "
+                    + bridgeLoggerClass + "\n\t" + julLogger;
+            System.err.println(errorMsg);
+            errors.append(errorMsg).append('\n');
+        }
+        final Class<? extends java.lang.System.Logger> xClass = julLogger.getClass();
+        List<Method> notOverridden =
+                Stream.of(bridgeLoggerClass.getDeclaredMethods()).filter((m) -> {
+            try {
+                Method x = xClass.getDeclaredMethod(m.getName(), m.getParameterTypes());
+                return x == null;
+            } catch (NoSuchMethodException ex) {
+                return !Modifier.isStatic(m.getModifiers());
+            }
+        }).collect(Collectors.toList());
+        notOverridden.stream().filter((x) -> {
+            boolean shouldOverride = true;
+            try {
+                final Method m = xClass.getMethod(x.getName(), x.getParameterTypes());
+                Method m2 = null;
+                try {
+                    m2 = jdkLoggerClass.getDeclaredMethod(x.getName(), x.getParameterTypes());
+                } catch (Exception e) {
+
+                }
+                shouldOverride = m.isDefault() || m2 == null;
+            } catch (Exception e) {
+                // should override.
+            }
+            return shouldOverride;
+        }).forEach(x -> {
+            final String errorMsg = xClass.getName() + " should override\n\t" + x.toString();
+            System.err.println(errorMsg);
+            errors.append(errorMsg).append('\n');
+        });
+        if (notOverridden.isEmpty()) {
+            System.out.println(xClass + " overrides all methods from " + bridgeLoggerClass);
+        }
+        return errors;
+    }
+
+    public static class ResourceBundeParam extends ResourceBundle {
+        Map<String, String> map = Collections.synchronizedMap(new LinkedHashMap<>());
+        @Override
+        protected Object handleGetObject(String key) {
+            map.putIfAbsent(key, "${"+key+"}");
+            return map.get(key);
+        }
+
+        @Override
+        public Enumeration<String> getKeys() {
+            return Collections.enumeration(new LinkedHashSet<>(map.keySet()));
+        }
+
+    }
+
+    final ResourceBundle bundleParam =
+            ResourceBundle.getBundle(ResourceBundeParam.class.getName());
+
+    public static class ResourceBundeLocalized extends ResourceBundle {
+        Map<String, String> map = Collections.synchronizedMap(new LinkedHashMap<>());
+        @Override
+        protected Object handleGetObject(String key) {
+            map.putIfAbsent(key, "Localized:${"+key+"}");
+            return map.get(key);
+        }
+
+        @Override
+        public Enumeration<String> getKeys() {
+            return Collections.enumeration(new LinkedHashSet<>(map.keySet()));
+        }
+
+    }
+
+    final static ResourceBundle bundleLocalized =
+            ResourceBundle.getBundle(ResourceBundeLocalized.class.getName());
+
+    final Map<Class<?>, Object> params = new HashMap<>();
+    {
+        params.put(String.class, "TestString");
+        params.put(sun.util.logging.PlatformLogger.Level.class, sun.util.logging.PlatformLogger.Level.WARNING);
+        params.put(java.lang.System.Logger.Level.class, java.lang.System.Logger.Level.WARNING);
+        params.put(ResourceBundle.class, bundleParam);
+        params.put(Throwable.class, new Throwable("TestThrowable (Please ignore it!)"));
+        params.put(Object[].class, new Object[] {"One", "Two"});
+        params.put(Object.class, new Object() {
+            @Override public String toString() { return "I am an object!"; }
+        });
+    }
+
+    public Object[] getParamsFor(Method m) {
+        final Object[] res = new Object[m.getParameterCount()];
+        final Class<?>[] sig = m.getParameterTypes();
+        if (res.length == 0) {
+            return res;
+        }
+        for (int i=0; i<res.length; i++) {
+            Object p = params.get(sig[i]);
+            if (p == null && sig[i].equals(Supplier.class)) {
+                final String msg = "SuppliedMsg["+i+"]";
+                p = (Supplier<String>) () -> msg;
+            }
+            if (p instanceof String) {
+                res[i] = String.valueOf(p)+"["+i+"]";
+            } else {
+                res[i] = p;
+            }
+        }
+        return res;
+    }
+
+    public void invokeOn(java.lang.System.Logger logger, Method m) {
+        Object[] p = getParamsFor(m);
+        try {
+            m.invoke(logger, p);
+        } catch (Exception e) {
+            throw new RuntimeException("Failed to invoke "+m.toString(), e);
+        }
+    }
+
+    public void testAllJdkExtensionMethods(java.lang.System.Logger logger) {
+        Stream.of(jdkLoggerClass.getDeclaredMethods())
+                .filter(m -> !Modifier.isStatic(m.getModifiers()))
+                .forEach((m) -> invokeOn(logger, m));
+    }
+
+    public void testAllAPIMethods(java.lang.System.Logger logger) {
+        Stream.of(spiLoggerClass.getDeclaredMethods())
+                .filter(m -> !Modifier.isStatic(m.getModifiers()))
+                .forEach((m) -> invokeOn(logger, m));
+    }
+
+    public void testAllBridgeMethods(java.lang.System.Logger logger) {
+        Stream.of(bridgeLoggerClass.getDeclaredMethods())
+                .filter(m -> !Modifier.isStatic(m.getModifiers()))
+                .forEach((m) -> invokeOn(logger, m));
+    }
+
+    public void testAllLogProducerMethods(java.lang.System.Logger logger) {
+        Stream.of(julLogProducerClass.getDeclaredMethods())
+                .filter(m -> !Modifier.isStatic(m.getModifiers()))
+                .forEach((m) -> invokeOn(logger, m));
+    }
+
+    public StringBuilder testGetLoggerOverriddenOnSpi() {
+        final StringBuilder errors = new StringBuilder();
+        Stream.of(jdkLoggerClass.getDeclaredMethods())
+                .filter(m -> Modifier.isStatic(m.getModifiers()))
+                .filter(m -> Modifier.isPublic(m.getModifiers()))
+                .filter(m -> !m.getName().equals("getLoggerFinder"))
+                .filter(m -> {
+                    try {
+                        final Method x = bridgeLoggerClass.getDeclaredMethod(m.getName(), m.getParameterTypes());
+                        return x == null;
+                    } catch (NoSuchMethodException ex) {
+                        return true;
+                    }
+                }).forEach(m -> {
+                    final String errorMsg = bridgeLoggerClass.getName() + " should override\n\t" + m.toString();
+                    System.err.println(errorMsg);
+                    errors.append(errorMsg).append('\n');
+                });
+        if (errors.length() == 0) {
+            System.out.println(bridgeLoggerClass + " overrides all static methods from " + jdkLoggerClass);
+        } else {
+            if (errors.length() > 0) throw new RuntimeException(errors.toString());
+        }
+        return errors;
+    }
+
+    public static void main(String argv[]) throws Exception {
+        final LoggerFinderAPITest test = new LoggerFinderAPITest(false);
+        final StringBuilder errors = new StringBuilder();
+        errors.append(test.testGetLoggerOverriddenOnSpi());
+        java.lang.System.Logger julLogger =
+                java.lang.System.LoggerFinder.getLoggerFinder()
+                        .getLogger("foo", LoggerFinderAPITest.class);
+        errors.append(test.testDefaultJULLogger(julLogger));
+        if (errors.length() > 0) throw new RuntimeException(errors.toString());
+        java.lang.System.Logger julSystemLogger =
+                java.lang.System.LoggerFinder.getLoggerFinder()
+                        .getLogger("bar", Thread.class);
+        errors.append(test.testDefaultJULLogger(julSystemLogger));
+        if (errors.length() > 0) throw new RuntimeException(errors.toString());
+        java.lang.System.Logger julLocalizedLogger =
+                (java.lang.System.Logger)
+                System.getLogger("baz", bundleLocalized);
+        java.lang.System.Logger julLocalizedSystemLogger =
+                java.lang.System.LoggerFinder.getLoggerFinder()
+                        .getLocalizedLogger("oof", bundleLocalized, Thread.class);
+        final String error = errors.toString();
+        if (!error.isEmpty()) throw new RuntimeException(error);
+        for (java.lang.System.Logger logger : new java.lang.System.Logger[] {
+            julLogger, julSystemLogger, julLocalizedLogger, julLocalizedSystemLogger
+        }) {
+            test.testAllJdkExtensionMethods(logger);
+            test.testAllAPIMethods(logger);
+            test.testAllBridgeMethods(logger);
+            test.testAllLogProducerMethods(logger);
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/LoggerFinder/internal/backend/LoggerFinderBackendTest.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,2316 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @bug     8140364
+ * @author  danielfuchs
+ * @summary  JDK implementation specific unit test for JDK internal artifacts.
+ *           This test tests all the public API methods defined in the {@link
+ *           java.lang.System.Logger} interface, as well as all the JDK
+ *           internal methods defined in the
+ *           {@link sun.util.logging.PlatformLogger.Bridge}
+ *           interface, with loggers returned by  {@link
+ *           java.lang.System.LoggerFinder#getLogger(java.lang.String, java.lang.Class)}
+ *           and {@link java.lang.System.LoggerFinder#getLocalizedLogger(java.lang.String,
+ *           java.util.ResourceBundle, java.lang.Class)}
+ *           (using both a null resource bundle and a non null resource bundle).
+ *           It calls both the {@link java.lang.System} factory methods and
+ *           {@link jdk.internal.logger.LazyLoggers} to obtains those loggers,
+ *           and configure them with all possible known levels.
+ * @modules java.base/sun.util.logging
+ *          java.base/jdk.internal.logger
+ *          java.logging/sun.util.logging.internal
+ * @build LoggerFinderBackendTest SystemClassLoader
+ * @run  main/othervm -Djava.system.class.loader=SystemClassLoader -Dtest.logger.hidesProvider=true LoggerFinderBackendTest
+ * @run  main/othervm -Djava.system.class.loader=SystemClassLoader -Dtest.logger.hidesProvider=false LoggerFinderBackendTest
+ */
+
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+import java.lang.invoke.MethodType;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.ResourceBundle;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.BiFunction;
+import java.util.function.BooleanSupplier;
+import java.util.function.Function;
+import java.util.function.Supplier;
+import java.lang.System.LoggerFinder;
+import java.util.logging.ConsoleHandler;
+import java.util.logging.Handler;
+import sun.util.logging.PlatformLogger.Level;
+import java.util.logging.LogManager;
+import java.util.logging.LogRecord;
+import java.util.logging.Logger;
+import sun.util.logging.internal.LoggingProviderImpl;
+
+/**
+ * @author danielfuchs
+ */
+public class LoggerFinderBackendTest {
+
+    // whether the implementation of Logger try to do a best
+    // effort for logp... If the provider is not hidden, then
+    // the logp() implementation comes from LoggerWrapper - which does a
+    // best effort. Otherwise, it comes from the default provider
+    // which does support logp.
+    static final boolean BEST_EFFORT_FOR_LOGP =
+            !Boolean.getBoolean("test.logger.hidesProvider");
+    static final boolean VERBOSE = false;
+
+    static final Class<java.lang.System.Logger> spiLoggerClass =
+            java.lang.System.Logger.class;
+    static final Class<java.lang.System.Logger> jdkLoggerClass =
+            java.lang.System.Logger.class;
+    static final Class<sun.util.logging.PlatformLogger.Bridge> bridgeLoggerClass =
+            sun.util.logging.PlatformLogger.Bridge.class;
+
+    /** Use to retrieve the log records that were produced by the JUL backend */
+    static class LoggerTesterHandler extends Handler {
+        public final List<LogRecord> records =
+                Collections.synchronizedList(new ArrayList<>());
+
+        @Override
+        public void publish(LogRecord record) {
+            record.getSourceClassName(); record.getSourceMethodName();
+            records.add(record);
+        }
+
+        @Override
+        public void flush() {
+        }
+
+        @Override
+        public void close() throws SecurityException {
+            records.clear();
+        }
+
+        public void reset() {
+            records.clear();
+        }
+    }
+
+    /** The {@link LoggerTesterHandler} handler is added to the root logger. */
+    static final LoggerTesterHandler handler = new LoggerTesterHandler();
+    static {
+        for (Handler h : Logger.getLogger("").getHandlers()) {
+            if (h instanceof ConsoleHandler) {
+                Logger.getLogger("").removeHandler(h);
+            }
+        }
+        Logger.getLogger("").addHandler(handler);
+    }
+
+    /**
+     * A resource handler parameter that will be used when calling out the
+     * logrb-like methods - as well as when calling the level-specific
+     * methods that take a ResourceBundle parameter.
+     */
+    public static class ResourceBundeParam extends ResourceBundle {
+        Map<String, String> map = Collections.synchronizedMap(new LinkedHashMap<>());
+        @Override
+        protected Object handleGetObject(String key) {
+            map.putIfAbsent(key, "${"+key+"}");
+            return map.get(key);
+        }
+
+        @Override
+        public Enumeration<String> getKeys() {
+            return Collections.enumeration(new LinkedHashSet<>(map.keySet()));
+        }
+
+    }
+
+    final static ResourceBundle bundleParam =
+            ResourceBundle.getBundle(ResourceBundeParam.class.getName());
+
+    /**
+     * A resource handler parameter that will be used when creating localized
+     * loggers by calling {@link
+     * LoggerFinder#getLocalizedLogger(java.lang.String, java.util.ResourceBundle, java.lang.Class)}.
+     */
+    public static class ResourceBundeLocalized extends ResourceBundle {
+        Map<String, String> map = Collections.synchronizedMap(new LinkedHashMap<>());
+        @Override
+        protected Object handleGetObject(String key) {
+            map.putIfAbsent(key, "Localized:${"+key+"}");
+            return map.get(key);
+        }
+
+        @Override
+        public Enumeration<String> getKeys() {
+            return Collections.enumeration(new LinkedHashSet<>(map.keySet()));
+        }
+
+    }
+
+    /**
+     * The Levels enum is used to call all the level-specific methods on
+     * a logger instance. To minimize the amount of code it uses reflection
+     * to do so.
+     */
+    static Lookup lookup = MethodHandles.lookup();
+    public enum Levels {
+        /** Used to call all forms of Logger.log?(SEVERE, ...) */
+        SEVERE("severe", bridgeLoggerClass, Level.SEVERE, null, "error", false),
+        /** Used to call all forms of Logger.log?(WARNING,...) */
+        WARNING("warning", bridgeLoggerClass, Level.WARNING, "warning", "warning", false),
+        /** Used to call all forms of Logger.log?(INFO,...) */
+        INFO("info", bridgeLoggerClass, Level.INFO, "info", "info", false),
+        /** Used to call all forms of Logger.log?(CONFIG,...) */
+        CONFIG("config", bridgeLoggerClass, Level.CONFIG, null, "debug", false),
+        /** Used to call all forms of Logger.log?(FINE,...) */
+        FINE("fine", bridgeLoggerClass, Level.FINE, null, "debug", false),
+        /** Used to call all forms of Logger.log?(FINER,...) */
+        FINER("finer", bridgeLoggerClass, Level.FINER, null, "trace", false),
+        /** Used to call all forms of Logger.log?(FINEST,...) */
+        FINEST("finest", bridgeLoggerClass, Level.FINEST, null, "trace", false),
+        ;
+        public final String method;  // The name of the level-specific method to call
+        public final Class<?> definingClass; // which interface j.u.logger.Logger or j.u.logging.spi.Logger defines it
+        public final Level platformLevel; // The platform Level it will be mapped to in Jul when Jul is the backend
+        public final String jdkExtensionToJUL; // The name of the method called on the JUL logger when JUL is the backend
+        public final String julToJdkExtension; // The name of the method called in the jdk extension by the default impl in jdk.internal.logging.Logger
+        public final String enableMethod; // The name of the isXxxxEnabled method
+        public final boolean hasSpecificIsEnabled;
+        Levels(String method, Class<?> definingClass, Level defaultMapping,
+                String jdkExtensionToJUL, String julToJdkExtension,
+                boolean hasSpecificIsEnabled) {
+            this.method = method;
+            this.definingClass = definingClass;
+            this.platformLevel = defaultMapping;
+            this.jdkExtensionToJUL = jdkExtensionToJUL;
+            this.julToJdkExtension = julToJdkExtension;
+            this.hasSpecificIsEnabled = hasSpecificIsEnabled;
+            if (hasSpecificIsEnabled) {
+                this.enableMethod = "is" + method.substring(0,1).toUpperCase()
+                    + method.substring(1) + "Enabled";
+            } else {
+                this.enableMethod = "isLoggable";
+            }
+        }
+
+        /*
+         * calls this level specific method - e.g. if this==INFO: logger.info(msg);
+         */
+        public void level(Object logger, String msg) {
+            MethodType mt = MethodType.methodType(void.class, Level.class, String.class);
+            invoke("log", logger, mt, platformLevel, msg);
+        }
+
+        /*
+         * calls this level specific method - e.g. if this==INFO: logger.info(msgSupplier);
+         */
+        public void level(Object logger, Supplier<String> msgSupplier) {
+            MethodType mt = MethodType.methodType(void.class,  Level.class, Supplier.class);
+            invoke("log", logger, mt, platformLevel, msgSupplier);
+        }
+
+        /*
+         * calls this level specific method - e.g. if this==INFO: logger.info(msg, params);
+         */
+        public void level(Object logger, String msg, Object... params) {
+            MethodType mt = MethodType.methodType(void.class,  Level.class, String.class,
+                    Object[].class);
+            invoke("log", logger, mt, platformLevel, msg, params);
+        }
+
+        /*
+         * calls this level specific method - e.g. if this==INFO: logger.info(msg, thrown);
+         */
+        public void level(Object logger, String msg, Throwable thrown) {
+            MethodType mt = MethodType.methodType(void.class,  Level.class, String.class,
+                    Throwable.class);
+            invoke("log", logger, mt, platformLevel, msg, thrown);
+        }
+
+        /*
+         * calls this level specific method - e.g. if this==INFO: logger.info(msgSupplier, thrown);
+         */
+        public void level(Object logger, Supplier<String> msgSupplier, Throwable thrown) {
+            MethodType mt = MethodType.methodType(void.class,  Level.class,
+                     Throwable.class, Supplier.class);
+            invoke("log", logger, mt, platformLevel, thrown, msgSupplier);
+        }
+
+        /*
+         * calls this level specific method - e.g. if this==INFO: logger.info(bundle, msg);
+         */
+        public void level(Object logger, String msg, ResourceBundle bundle) {
+            MethodType mt = MethodType.methodType(void.class, Level.class,
+                    ResourceBundle.class, String.class, Object[].class);
+            invoke("logrb", logger, mt, platformLevel, bundle, msg, null);
+        }
+
+        public void level(Object logger, String msg, ResourceBundle bundle,
+                Object... params) {
+            MethodType mt = MethodType.methodType(void.class, Level.class,
+                    ResourceBundle.class, String.class, Object[].class);
+            invoke("logrb", logger, mt, platformLevel, bundle, msg, params);
+        }
+
+        public void level(Object logger, String msg, ResourceBundle bundle,
+                Throwable thrown) {
+            MethodType mt = MethodType.methodType(void.class, Level.class,
+                    ResourceBundle.class, String.class, Throwable.class);
+            invoke("logrb", logger, mt, platformLevel, bundle, msg, thrown);
+        }
+
+        public boolean isEnabled(Object logger) {
+            try {
+                if (hasSpecificIsEnabled) {
+                    MethodType mt = MethodType.methodType(boolean.class);
+                    final MethodHandle handle = lookup.findVirtual(definingClass,
+                        enableMethod, mt).bindTo(logger);
+                    return Boolean.class.cast(handle.invoke());
+                } else {
+                    MethodType mt = MethodType.methodType(boolean.class,
+                        Level.class);
+                    final MethodHandle handle = lookup.findVirtual(definingClass,
+                        enableMethod, mt).bindTo(logger);
+                    return Boolean.class.cast(handle.invoke(platformLevel));
+                }
+            } catch (Throwable ex) {
+                throw new RuntimeException(ex);
+            }
+        }
+
+        private void invoke(String method, Object logger, MethodType mt, Object... args) {
+            try {
+                final int last = mt.parameterCount()-1;
+                boolean isVarargs = mt.parameterType(last).isArray();
+                final MethodHandle handle = lookup.findVirtual(definingClass,
+                        method, mt).bindTo(logger);
+
+                final StringBuilder builder = new StringBuilder();
+                builder.append(logger.getClass().getSimpleName()).append('.')
+                        .append(method).append('(');
+                String sep = "";
+                int offset = 0;
+                Object[] params = args;
+                for (int i=0; (i-offset) < params.length; i++) {
+                    if (isVarargs && i == last) {
+                        offset = last;
+                        params = (Object[])args[i];
+                        if (params == null) break;
+                    }
+                    Object p = params[i - offset];
+                    String quote = (p instanceof String) ? "\"" : "";
+                    builder.append(sep).append(quote).append(p).append(quote);
+                    sep = ", ";
+                }
+                builder.append(')');
+                if (verbose) {
+                    System.out.println(builder);
+                }
+                handle.invokeWithArguments(args);
+            } catch (Throwable ex) {
+                throw new RuntimeException(ex);
+            }
+        }
+
+    };
+
+    static interface Checker<LogResult, L> extends BiFunction<LogResult, L, Void> {}
+    static interface JdkLogTester
+            extends BiFunction<sun.util.logging.PlatformLogger.Bridge, Level, Void> {}
+    static interface SpiLogTester
+            extends BiFunction<java.lang.System.Logger, java.lang.System.Logger.Level, Void> {}
+
+    static interface MethodInvoker<LOGGER, LEVEL> {
+        public void logX(LOGGER logger, LEVEL level, Object... args);
+    }
+
+    public enum JdkLogMethodInvoker
+           implements MethodInvoker<sun.util.logging.PlatformLogger.Bridge, Level> {
+        /**
+         * Tests {@link
+         * jdk.internal.logging.Logger#log(Level, String, Object...)};
+         **/
+        LOG_STRING_PARAMS("log", MethodType.methodType(void.class,
+                Level.class, String.class, Object[].class)),
+        /**
+         * Tests {@link
+         * jdk.internal.logging.Logger#log(Level, String, Throwable)};
+         **/
+        LOG_STRING_THROWN("log", MethodType.methodType(void.class,
+                Level.class, String.class, Throwable.class)),
+        /**
+         * Tests {@link
+         * jdk.internal.logging.Logger#log(Level, Supplier<String>)};
+         **/
+        LOG_SUPPLIER("log", MethodType.methodType(void.class,
+                Level.class, Supplier.class)),
+        /**
+         * Tests {@link
+         * jdk.internal.logging.Logger#log(Level, Throwable, Supplier<String>)};
+         **/
+        LOG_SUPPLIER_THROWN("log", MethodType.methodType(void.class,
+                Level.class, Throwable.class, Supplier.class)),
+        /**
+         * Tests {@link
+         * jdk.internal.logging.Logger#logp(Level, String, String, String)};
+         **/
+        LOGP_STRING("logp", MethodType.methodType(void.class,
+                Level.class, String.class, String.class, String.class)),
+        /**
+         * Tests {@link
+         * jdk.internal.logging.Logger#logp(Level, String, String, String, Object...)};
+         **/
+        LOGP_STRING_PARAMS("logp", MethodType.methodType(void.class,
+                Level.class, String.class, String.class, String.class, Object[].class)),
+        /**
+         * Tests {@link
+         * jdk.internal.logging.Logger#logp(Level, String, String, String, Throwable)};
+         **/
+        LOGP_STRING_THROWN("logp", MethodType.methodType(void.class,
+                Level.class, String.class, String.class, String.class, Throwable.class)),
+        /**
+         * Tests {@link
+         * jdk.internal.logging.Logger#logp(Level, String, String, Supplier<String>)};
+         **/
+        LOGP_SUPPLIER("logp", MethodType.methodType(void.class,
+                Level.class, String.class, String.class, Supplier.class)),
+        /**
+         * Tests {@link
+         * jdk.internal.logging.Logger#logp(Level, String, String, Throwable, Supplier<String>)};
+         **/
+        LOGP_SUPPLIER_THROWN("logp", MethodType.methodType(void.class,
+                Level.class, String.class, String.class,
+                Throwable.class, Supplier.class)),
+        /**
+         * Tests {@link
+         * jdk.internal.logging.Logger#logrb(Level, ResourceBundle, String, Object...)};
+         **/
+        LOGRB_STRING_PARAMS("logrb", MethodType.methodType(void.class,
+                Level.class, ResourceBundle.class, String.class, Object[].class)),
+        /**
+         * Tests {@link
+         * jdk.internal.logging.Logger#logrb(Level, ResourceBundle, String, Throwable)};
+         **/
+        LOGRB_STRING_THROWN("logrb", MethodType.methodType(void.class,
+                Level.class, ResourceBundle.class, String.class, Throwable.class)),
+        /**
+         * Tests {@link
+         * jdk.internal.logging.Logger#logrb(Level, String, String, ResourceBundle, String, Object...)};
+         **/
+        LOGRBP_STRING_PARAMS("logrb", MethodType.methodType(void.class,
+                Level.class, String.class, String.class, ResourceBundle.class,
+                String.class, Object[].class)),
+        /**
+         * Tests {@link
+         * jdk.internal.logging.Logger#logrb(Level, String, String, ResourceBundle, String, Throwable)};
+         **/
+        LOGRBP_STRING_THROWN("logrb", MethodType.methodType(void.class,
+                Level.class, String.class, String.class, ResourceBundle.class,
+                String.class, Throwable.class)),
+        ;
+        final MethodType mt;
+        final String method;
+        JdkLogMethodInvoker(String method, MethodType mt) {
+            this.mt = mt;
+            this.method = method;
+        }
+        Object[] makeArgs(Level level, Object... rest) {
+            List<Object> list = new ArrayList<>(rest == null ? 1 : rest.length + 1);
+            list.add(level);
+            if (rest != null) {
+                list.addAll(Arrays.asList(rest));
+            }
+            return list.toArray(new Object[list.size()]);
+        }
+
+        @Override
+        public void logX(sun.util.logging.PlatformLogger.Bridge logger, Level level, Object... args) {
+            try {
+                MethodHandle handle = lookup.findVirtual(bridgeLoggerClass,
+                        method, mt).bindTo(logger);
+                final int last = mt.parameterCount()-1;
+                boolean isVarargs = mt.parameterType(last).isArray();
+
+                args = makeArgs(level, args);
+
+                final StringBuilder builder = new StringBuilder();
+                builder.append(logger.getClass().getSimpleName()).append('.')
+                        .append(this.method).append('(');
+                String sep = "";
+                int offset = 0;
+                Object[] params = args;
+                for (int i=0; (i-offset) < params.length; i++) {
+                    if (isVarargs && i == last) {
+                        offset = last;
+                        params = (Object[])args[i];
+                        if (params == null) break;
+                    }
+                    Object p = params[i - offset];
+                    String quote = (p instanceof String) ? "\"" : "";
+                    p = p instanceof Level ? "Level."+p : p;
+                    builder.append(sep).append(quote).append(p).append(quote);
+                    sep = ", ";
+                }
+                builder.append(')');
+                if (verbose) System.out.println(builder);
+                handle.invokeWithArguments(args);
+            } catch (Throwable ex) {
+                throw new RuntimeException(ex);
+            }
+        }
+    }
+
+
+    public enum SpiLogMethodInvoker implements MethodInvoker<java.lang.System.Logger,
+            java.lang.System.Logger.Level> {
+        /**
+         * Tests {@link
+         * jdk.internal.logging.Logger#log(Level, String, Object...)};
+         **/
+        LOG_STRING_PARAMS("log", MethodType.methodType(void.class,
+                java.lang.System.Logger.Level.class, String.class, Object[].class)),
+        /**
+         * Tests {@link
+         * jdk.internal.logging.Logger#log(Level, String, Throwable)};
+         **/
+        LOG_STRING_THROWN("log", MethodType.methodType(void.class,
+                java.lang.System.Logger.Level.class, String.class, Throwable.class)),
+        /**
+         * Tests {@link
+         * jdk.internal.logging.Logger#log(Level, Supplier<String>)};
+         **/
+        LOG_SUPPLIER("log", MethodType.methodType(void.class,
+                java.lang.System.Logger.Level.class, Supplier.class)),
+        /**
+         * Tests {@link
+         * jdk.internal.logging.Logger#log(Level, Throwable, Supplier<String>)};
+         **/
+        LOG_SUPPLIER_THROWN("log", MethodType.methodType(void.class,
+                java.lang.System.Logger.Level.class, Supplier.class, Throwable.class)),
+        /**
+         * Tests {@link
+         * jdk.internal.logging.Logger#log(Level, Supplier<String>)};
+         **/
+        LOG_OBJECT("log", MethodType.methodType(void.class,
+                java.lang.System.Logger.Level.class, Object.class)),
+        /**
+         * Tests {@link
+         * jdk.internal.logging.Logger#logrb(Level, ResourceBundle, String, Object...)};
+         **/
+        LOGRB_STRING_PARAMS("log", MethodType.methodType(void.class,
+                java.lang.System.Logger.Level.class, ResourceBundle.class,
+                String.class, Object[].class)),
+        /**
+         * Tests {@link
+         * jdk.internal.logging.Logger#logrb(Level, ResourceBundle, String, Throwable)};
+         **/
+        LOGRB_STRING_THROWN("log", MethodType.methodType(void.class,
+                java.lang.System.Logger.Level.class, ResourceBundle.class,
+                String.class, Throwable.class)),
+        ;
+        final MethodType mt;
+        final String method;
+        SpiLogMethodInvoker(String method, MethodType mt) {
+            this.mt = mt;
+            this.method = method;
+        }
+        Object[] makeArgs(java.lang.System.Logger.Level level, Object... rest) {
+            List<Object> list = new ArrayList<>(rest == null ? 1 : rest.length + 1);
+            list.add(level);
+            if (rest != null) {
+                list.addAll(Arrays.asList(rest));
+            }
+            return list.toArray(new Object[list.size()]);
+        }
+
+        @Override
+        public void logX(java.lang.System.Logger logger,
+                java.lang.System.Logger.Level level, Object... args) {
+            try {
+                MethodHandle handle = lookup.findVirtual(spiLoggerClass,
+                        method, mt).bindTo(logger);
+                final int last = mt.parameterCount()-1;
+                boolean isVarargs = mt.parameterType(last).isArray();
+
+                args = makeArgs(level, args);
+
+                final StringBuilder builder = new StringBuilder();
+                builder.append(logger.getClass().getSimpleName()).append('.')
+                        .append(this.method).append('(');
+                String sep = "";
+                int offset = 0;
+                Object[] params = args;
+                for (int i=0; (i-offset) < params.length; i++) {
+                    if (isVarargs && i == last) {
+                        offset = last;
+                        params = (Object[])args[i];
+                        if (params == null) break;
+                    }
+                    Object p = params[i - offset];
+                    String quote = (p instanceof String) ? "\"" : "";
+                    p = p instanceof Level ? "Level."+p : p;
+                    builder.append(sep).append(quote).append(p).append(quote);
+                    sep = ", ";
+                }
+                builder.append(')');
+                if (verbose) System.out.println(builder);
+                handle.invokeWithArguments(args);
+            } catch (Throwable ex) {
+                throw new RuntimeException(ex);
+            }
+        }
+    }
+
+
+    public abstract static class BackendTester<BackendRecord> {
+        static final Level[] levelMap = {Level.ALL, Level.FINER, Level.FINE,
+            Level.INFO, Level.WARNING, Level.SEVERE, Level.OFF};
+
+        abstract class BackendAdaptor {
+            public abstract String getLoggerName(BackendRecord res);
+            public abstract Object getLevel(BackendRecord res);
+            public abstract String getMessage(BackendRecord res);
+            public abstract String getSourceClassName(BackendRecord res);
+            public abstract String getSourceMethodName(BackendRecord res);
+            public abstract Throwable getThrown(BackendRecord res);
+            public abstract ResourceBundle getResourceBundle(BackendRecord res);
+            public abstract void setLevel(java.lang.System.Logger logger,
+                    Level level);
+            public abstract void setLevel(java.lang.System.Logger logger,
+                    java.lang.System.Logger.Level level);
+            public abstract List<BackendRecord> getBackendRecords();
+            public abstract void resetBackendRecords();
+            public boolean shouldBeLoggable(Levels level, Level loggerLevel) {
+                final Level logLevel = level.platformLevel;
+                return shouldBeLoggable(logLevel, loggerLevel);
+            }
+            public boolean shouldBeLoggable(Level logLevel, Level loggerLevel) {
+                return loggerLevel.intValue() != Level.OFF.intValue()
+                        && logLevel.intValue() >= loggerLevel.intValue();
+            }
+            public boolean shouldBeLoggable(java.lang.System.Logger.Level logLevel,
+                    java.lang.System.Logger.Level loggerLevel) {
+                return loggerLevel != java.lang.System.Logger.Level.OFF
+                        && logLevel.ordinal() >= loggerLevel.ordinal();
+            }
+            public boolean isLoggable(java.lang.System.Logger logger, Level l) {
+                return bridgeLoggerClass.cast(logger).isLoggable(l);
+            }
+            public String getCallerClassName(Levels level, String clazz) {
+                return clazz != null ? clazz : Levels.class.getName();
+            }
+            public String getCallerClassName(MethodInvoker<?,?> logMethod,
+                   String clazz) {
+                return clazz != null ? clazz : logMethod.getClass().getName();
+            }
+            public String getCallerMethodName(Levels level, String method) {
+                return method != null ? method : "invoke";
+            }
+            public String getCallerMethodName(MethodInvoker<?,?> logMethod,
+                    String method) {
+                return method != null ? method : "logX";
+            }
+            public Object getMappedLevel(Object level) {
+                return level;
+            }
+
+            public Level toJUL(java.lang.System.Logger.Level level) {
+                return levelMap[level.ordinal()];
+            }
+        }
+
+        public final boolean isSystem;
+        public final Class<? extends java.lang.System.Logger> restrictedTo;
+        public final ResourceBundle localized;
+        public BackendTester(boolean isSystem) {
+            this(isSystem,null,null);
+        }
+        public BackendTester(boolean isSystem, ResourceBundle localized) {
+            this(isSystem,null,localized);
+        }
+        public BackendTester(boolean isSystem,
+                Class<? extends java.lang.System.Logger> restrictedTo) {
+            this(isSystem, restrictedTo, null);
+        }
+        public BackendTester(boolean isSystem,
+                Class<? extends java.lang.System.Logger> restrictedTo,
+                ResourceBundle localized) {
+            this.isSystem = isSystem;
+            this.restrictedTo = restrictedTo;
+            this.localized = localized;
+        }
+
+        public java.lang.System.Logger convert(java.lang.System.Logger logger) {
+            return logger;
+        }
+
+        public static Level[] LEVELS = {
+            Level.OFF,
+            Level.SEVERE, Level.WARNING, Level.INFO, Level.CONFIG,
+            Level.FINE, Level.FINER, Level.FINEST,
+            Level.ALL
+        };
+
+        abstract BackendAdaptor adaptor();
+
+        protected void checkRecord(Levels test, BackendRecord res, String loggerName,
+                Level level, String msg, String className, String methodName,
+                Throwable thrown, ResourceBundle bundle, Object... params) {
+            checkRecord(test, res, loggerName, level, ()->msg, className,
+                    methodName, thrown, bundle, params);
+
+        }
+        protected void checkRecord(Levels test, BackendRecord res, String loggerName,
+                Level level, Supplier<String> msg, String className, String methodName,
+                Throwable thrown, ResourceBundle bundle, Object... params) {
+            checkRecord(test.method, res, loggerName, level, msg,
+                    className, methodName, thrown, bundle, params);
+        }
+        protected <L> void checkRecord(String logMethod, BackendRecord res, String loggerName,
+                L level, Supplier<String> msg, String className, String methodName,
+                Throwable thrown, ResourceBundle bundle, Object... params) {
+            final BackendAdaptor analyzer = adaptor();
+            if (! Objects.equals(analyzer.getLoggerName(res), loggerName)) {
+                throw new RuntimeException(logMethod+": expected logger name "
+                        + loggerName + " got " + analyzer.getLoggerName(res));
+            }
+            if (!Objects.equals(analyzer.getLevel(res), analyzer.getMappedLevel(level))) {
+                throw new RuntimeException(logMethod+": expected level "
+                        + analyzer.getMappedLevel(level) + " got " + analyzer.getLevel(res));
+            }
+            if (!Objects.equals(analyzer.getMessage(res), msg.get())) {
+                throw new RuntimeException(logMethod+": expected message \""
+                        + msg.get() + "\" got \"" + analyzer.getMessage(res) +"\"");
+            }
+            if (!Objects.equals(analyzer.getSourceClassName(res), className)) {
+                throw new RuntimeException(logMethod
+                        + ": expected class name \"" + className
+                        + "\" got \"" + analyzer.getSourceClassName(res) +"\"");
+            }
+            if (!Objects.equals(analyzer.getSourceMethodName(res), methodName)) {
+                throw new RuntimeException(logMethod
+                        + ": expected method name \"" + methodName
+                        + "\" got \"" + analyzer.getSourceMethodName(res) +"\"");
+            }
+            final Throwable thrownRes = analyzer.getThrown(res);
+            if (!Objects.equals(thrownRes, thrown)) {
+                throw new RuntimeException(logMethod
+                        + ": expected throwable \"" + thrown
+                        + "\" got \"" + thrownRes + "\"");
+            }
+            if (!Objects.equals(analyzer.getResourceBundle(res), bundle)) {
+                throw new RuntimeException(logMethod
+                        + ": expected bundle \"" + bundle
+                        + "\" got \"" + analyzer.getResourceBundle(res) +"\"");
+            }
+        }
+
+        public void testLevel(Levels level, java.lang.System.Logger logger,
+                String msg) {
+            Runnable test = () -> level.level(logger, msg);
+            Checker<BackendRecord, Level> check = (res, l) -> {
+                checkRecord(level, res, logger.getName(), l, msg,
+                            adaptor().getCallerClassName(level, Levels.class.getName()),
+                            adaptor().getCallerMethodName(level, "invoke"),
+                            null, localized);
+                return null;
+            };
+            test("msg", level, logger, test, check);
+        }
+
+        public void testLevel(Levels level, java.lang.System.Logger logger,
+                String msg, Object... params) {
+            Runnable test = () -> level.level(logger, msg, (Object[])params);
+            Checker<BackendRecord, Level> check = (res, l) -> {
+                checkRecord(level, res, logger.getName(), l, msg,
+                            adaptor().getCallerClassName(level, Levels.class.getName()),
+                            adaptor().getCallerMethodName(level, "invoke"),
+                            null, localized, (Object[])params);
+                return null;
+            };
+            test("msg, params", level, logger, test, check);
+        }
+
+        public void testLevel(Levels level, java.lang.System.Logger logger,
+                String msg, Throwable thrown) {
+            Runnable test = () -> level.level(logger, msg, thrown);
+            Checker<BackendRecord, Level> check = (res, l) -> {
+                checkRecord(level, res, logger.getName(), l, msg,
+                            adaptor().getCallerClassName(level, Levels.class.getName()),
+                            adaptor().getCallerMethodName(level, "invoke"),
+                            thrown, localized);
+                return null;
+            };
+            test("msg, thrown", level, logger, test, check);
+        }
+
+        public void testLevel(Levels level, java.lang.System.Logger logger,
+                Supplier<String> msg) {
+            Runnable test = () -> level.level(logger, msg);
+            Checker<BackendRecord, Level> check = (res, l) -> {
+                checkRecord(level, res, logger.getName(), l, msg,
+                            adaptor().getCallerClassName(level, Levels.class.getName()),
+                            adaptor().getCallerMethodName(level, "invoke"),
+                            null, null);
+                return null;
+            };
+            test("msgSupplier", level, logger, test, check);
+        }
+
+        public void testLevel(Levels level, java.lang.System.Logger logger,
+                Supplier<String> msg, Throwable thrown) {
+            Runnable test = () -> level.level(logger, msg, thrown);
+            Checker<BackendRecord, Level> check = (res, l) -> {
+                checkRecord(level, res, logger.getName(), l, msg,
+                            adaptor().getCallerClassName(level, Levels.class.getName()),
+                            adaptor().getCallerMethodName(level, "invoke"),
+                            thrown, null);
+                return null;
+            };
+            test("throw, msgSupplier", level, logger, test, check);
+        }
+
+        public void testLevel(Levels level, java.lang.System.Logger logger,
+                String msg, ResourceBundle bundle) {
+            Runnable test = () -> level.level(logger, msg, bundle);
+            Checker<BackendRecord, Level> check = (res, l) -> {
+                checkRecord(level, res, logger.getName(), l, msg,
+                            adaptor().getCallerClassName(level, Levels.class.getName()),
+                            adaptor().getCallerMethodName(level, "invoke"),
+                            null, bundle);
+                return null;
+            };
+            test("bundle, msg", level, logger, test, check);
+        }
+
+        public void testLevel(Levels level, java.lang.System.Logger logger,
+                String msg, ResourceBundle bundle, Object... params) {
+            Runnable test = () -> level.level(logger, msg, bundle, (Object[])params);
+            Checker<BackendRecord, Level> check = (res, l) -> {
+                checkRecord(level, res, logger.getName(), l, msg,
+                            adaptor().getCallerClassName(level, Levels.class.getName()),
+                            adaptor().getCallerMethodName(level, "invoke"),
+                            null, bundle, (Object[])params);
+                return null;
+            };
+            test("bundle, msg, params", level, logger, test, check);
+        }
+
+        public void testLevel(Levels level, java.lang.System.Logger logger,
+                String msg, ResourceBundle bundle, Throwable thrown) {
+            Runnable test = () -> level.level(logger, msg, bundle, thrown);
+            Checker<BackendRecord, Level> check = (res, l) -> {
+                checkRecord(level, res, logger.getName(), l, msg,
+                            adaptor().getCallerClassName(level, Levels.class.getName()),
+                            adaptor().getCallerMethodName(level, "invoke"),
+                            thrown, bundle);
+                return null;
+            };
+            test("bundle, msg, throwable", level, logger, test, check);
+        }
+
+        // System.Logger
+        public void testSpiLog(java.lang.System.Logger logger, String msg) {
+            Checker<BackendRecord, java.lang.System.Logger.Level> check = (res, l) -> {
+                checkRecord("log", res, logger.getName(), l, () -> msg,
+                            adaptor().getCallerClassName(
+                                    SpiLogMethodInvoker.LOG_STRING_PARAMS,
+                                    SpiLogMethodInvoker.class.getName()),
+                            adaptor().getCallerMethodName(
+                                    SpiLogMethodInvoker.LOG_STRING_PARAMS,
+                                    "logX"), null, localized);
+                return null;
+            };
+            SpiLogTester tester = (x, level) -> {
+                SpiLogMethodInvoker.LOG_STRING_PARAMS.logX(x, level, msg, (Object[])null);
+                return null;
+            };
+            Function<String, String> nameProducer = (l) -> "log(Level." + l + ", \"" + msg + "\")";
+            testSpiLog(logger, tester, check, nameProducer);
+        }
+
+        public void testSpiLog(java.lang.System.Logger logger,
+                ResourceBundle bundle, String msg) {
+            Checker<BackendRecord, java.lang.System.Logger.Level> check = (res, l) -> {
+                checkRecord("log", res, logger.getName(), l, () -> msg,
+                            adaptor().getCallerClassName(
+                                    SpiLogMethodInvoker.LOGRB_STRING_PARAMS,
+                                    SpiLogMethodInvoker.class.getName()),
+                            adaptor().getCallerMethodName(
+                                    SpiLogMethodInvoker.LOGRB_STRING_PARAMS,
+                                    "logX"), null, bundle);
+                return null;
+            };
+            SpiLogTester tester = (x, level) -> {
+                SpiLogMethodInvoker.LOGRB_STRING_PARAMS.logX(x, level, bundle, msg, (Object[])null);
+                return null;
+            };
+            Function<String, String> nameProducer = (l) -> "log(Level." + l
+                    + ", bundle, \"" + msg + "\")";
+            testSpiLog(logger, tester, check, nameProducer);
+        }
+
+        public void testSpiLog(java.lang.System.Logger logger, String msg, Object... params) {
+            Checker<BackendRecord, java.lang.System.Logger.Level> check = (res, l) -> {
+                checkRecord("log", res, logger.getName(), l, () -> msg,
+                            adaptor().getCallerClassName(
+                                    SpiLogMethodInvoker.LOG_STRING_PARAMS,
+                                    SpiLogMethodInvoker.class.getName()),
+                            adaptor().getCallerMethodName(
+                                    SpiLogMethodInvoker.LOG_STRING_PARAMS,
+                                    "logX"), null, localized, params);
+                return null;
+            };
+            SpiLogTester tester = (x, level) -> {
+                SpiLogMethodInvoker.LOG_STRING_PARAMS.logX(x, level, msg, params);
+                return null;
+            };
+            Function<String, String> nameProducer = (l) -> "log(Level." + l + ", \"" + msg + "\", params...)";
+            testSpiLog(logger, tester, check, nameProducer);
+        }
+
+        public void testSpiLog(java.lang.System.Logger logger,
+                ResourceBundle bundle, String msg, Object... params) {
+            Checker<BackendRecord, java.lang.System.Logger.Level> check = (res, l) -> {
+                checkRecord("log", res, logger.getName(), l, () -> msg,
+                            adaptor().getCallerClassName(
+                                    SpiLogMethodInvoker.LOGRB_STRING_PARAMS,
+                                    SpiLogMethodInvoker.class.getName()),
+                            adaptor().getCallerMethodName(
+                                    SpiLogMethodInvoker.LOGRB_STRING_PARAMS,
+                                    "logX"), null, bundle, params);
+                return null;
+            };
+            SpiLogTester tester = (x, level) -> {
+                SpiLogMethodInvoker.LOGRB_STRING_PARAMS.logX(x, level, bundle, msg, params);
+                return null;
+            };
+            Function<String, String> nameProducer = (l) -> "log(Level." + l
+                    + ", bundle, \"" + msg + "\", params...)";
+            testSpiLog(logger, tester, check, nameProducer);
+        }
+
+        public void testSpiLog(java.lang.System.Logger logger, String msg, Throwable thrown) {
+            Checker<BackendRecord, java.lang.System.Logger.Level> check = (res, l) -> {
+                checkRecord("log", res, logger.getName(), l, () -> msg,
+                           adaptor().getCallerClassName(
+                                    SpiLogMethodInvoker.LOG_STRING_THROWN,
+                                    SpiLogMethodInvoker.class.getName()),
+                            adaptor().getCallerMethodName(
+                                    SpiLogMethodInvoker.LOG_STRING_THROWN,
+                                    "logX"), thrown, localized);
+                return null;
+            };
+            SpiLogTester tester = (x, level) -> {
+                SpiLogMethodInvoker.LOG_STRING_THROWN.logX(x, level, msg, thrown);
+                return null;
+            };
+            Function<String, String> nameProducer = (l) ->
+                    "log(Level." + l + ", \"" + msg + "\", thrown)";
+            testSpiLog(logger, tester, check, nameProducer);
+        }
+
+        public void testSpiLog(java.lang.System.Logger logger,
+                ResourceBundle bundle, String msg, Throwable thrown) {
+            Checker<BackendRecord, java.lang.System.Logger.Level> check = (res, l) -> {
+                checkRecord("log", res, logger.getName(), l, () -> msg,
+                            adaptor().getCallerClassName(
+                                    SpiLogMethodInvoker.LOGRB_STRING_THROWN,
+                                    SpiLogMethodInvoker.class.getName()),
+                            adaptor().getCallerMethodName(
+                                    SpiLogMethodInvoker.LOGRB_STRING_THROWN,
+                                    "logX"), thrown, bundle);
+                return null;
+            };
+            SpiLogTester tester = (x, level) -> {
+                SpiLogMethodInvoker.LOGRB_STRING_THROWN.logX(x, level, bundle, msg, thrown);
+                return null;
+            };
+            Function<String, String> nameProducer = (l) ->
+                    "log(Level." + l + ", bundle, \"" + msg + "\", thrown)";
+            testSpiLog(logger, tester, check, nameProducer);
+        }
+
+        public void testSpiLog(java.lang.System.Logger logger, Supplier<String> msg) {
+            Checker<BackendRecord, java.lang.System.Logger.Level> check = (res, l) -> {
+                checkRecord("log", res, logger.getName(), l, msg,
+                            adaptor().getCallerClassName(
+                                    SpiLogMethodInvoker.LOG_SUPPLIER,
+                                    SpiLogMethodInvoker.class.getName()),
+                            adaptor().getCallerMethodName(
+                                    SpiLogMethodInvoker.LOG_SUPPLIER,
+                                    "logX"), null, null);
+                return null;
+            };
+            SpiLogTester tester = (x, level) -> {
+                SpiLogMethodInvoker.LOG_SUPPLIER.logX(x, level, msg);
+                return null;
+            };
+            Function<String, String> nameProducer = (l) ->
+                    "log(Level." + l + ", () -> \"" + msg.get() + "\")";
+            testSpiLog(logger, tester, check, nameProducer);
+        }
+
+        public void testSpiLog(java.lang.System.Logger logger, Object obj) {
+            Checker<BackendRecord, java.lang.System.Logger.Level> check = (res, l) -> {
+                checkRecord("log", res, logger.getName(), l, () -> obj.toString(),
+                            adaptor().getCallerClassName(
+                                    SpiLogMethodInvoker.LOG_OBJECT,
+                                    SpiLogMethodInvoker.class.getName()),
+                            adaptor().getCallerMethodName(
+                                    SpiLogMethodInvoker.LOG_OBJECT,
+                                    "logX"), null, null);
+                return null;
+            };
+            SpiLogTester tester = (x, level) -> {
+                SpiLogMethodInvoker.LOG_OBJECT.logX(x, level, obj);
+                return null;
+            };
+            Function<String, String> nameProducer = (l) ->
+                    "log(Level." + l + ", new "+obj.getClass().getSimpleName()+"(\""
+                            + obj.toString() + "\"))";
+            testSpiLog(logger, tester, check, nameProducer);
+        }
+
+        public void testSpiLog(java.lang.System.Logger logger, Throwable thrown, Supplier<String> msg) {
+            Checker<BackendRecord, java.lang.System.Logger.Level> check = (res, l) -> {
+                checkRecord("log", res, logger.getName(), l, msg,
+                            adaptor().getCallerClassName(
+                                    SpiLogMethodInvoker.LOG_SUPPLIER_THROWN,
+                                    SpiLogMethodInvoker.class.getName()),
+                            adaptor().getCallerMethodName(
+                                    SpiLogMethodInvoker.LOG_SUPPLIER_THROWN,
+                                    "logX"), thrown, null);
+                return null;
+            };
+            SpiLogTester tester = (x, level) -> {
+                SpiLogMethodInvoker.LOG_SUPPLIER_THROWN.logX(x, level, msg, thrown);
+                return null;
+            };
+            Function<String, String> nameProducer = (l) ->
+                    "log(Level." + l + ", () -> \"" + msg.get() + "\", thrown)";
+            testSpiLog(logger, tester, check, nameProducer);
+        }
+
+
+        // JDK
+
+        public void testLog(java.lang.System.Logger logger, String msg) {
+            Checker<BackendRecord, Level> check = (res, l) -> {
+                checkRecord("log", res, logger.getName(), l, () -> msg,
+                            adaptor().getCallerClassName(
+                                    JdkLogMethodInvoker.LOG_STRING_PARAMS,
+                                    JdkLogMethodInvoker.class.getName()),
+                            adaptor().getCallerMethodName(
+                                    JdkLogMethodInvoker.LOG_STRING_PARAMS,
+                                    "logX"), null, localized);
+                return null;
+            };
+            JdkLogTester tester = (x, level) -> {
+                JdkLogMethodInvoker.LOG_STRING_PARAMS.logX(x, level, msg, (Object[])null);
+                return null;
+            };
+            Function<String, String> nameProducer = (l) -> "log(Level." + l + ", \"" + msg + "\")";
+            testJdkLog(logger, tester, check, nameProducer);
+        }
+
+        public void testLogrb(java.lang.System.Logger logger,
+                ResourceBundle bundle, String msg) {
+            Checker<BackendRecord, Level> check = (res, l) -> {
+                checkRecord("log", res, logger.getName(), l, () -> msg,
+                            adaptor().getCallerClassName(
+                                    JdkLogMethodInvoker.LOGRB_STRING_PARAMS,
+                                    JdkLogMethodInvoker.class.getName()),
+                            adaptor().getCallerMethodName(
+                                    JdkLogMethodInvoker.LOGRB_STRING_PARAMS,
+                                    "logX"), null, bundle);
+                return null;
+            };
+            JdkLogTester tester = (x, level) -> {
+                JdkLogMethodInvoker.LOGRB_STRING_PARAMS.logX(x, level, bundle, msg, (Object[])null);
+                return null;
+            };
+            Function<String, String> nameProducer = (l) -> "logrb(Level." + l
+                    + ", bundle, \"" + msg + "\")";
+            testJdkLog(logger, tester, check, nameProducer);
+        }
+
+        public void testLog(java.lang.System.Logger logger, String msg, Object... params) {
+            Checker<BackendRecord, Level> check = (res, l) -> {
+                checkRecord("log", res, logger.getName(), l, () -> msg,
+                            adaptor().getCallerClassName(
+                                    JdkLogMethodInvoker.LOG_STRING_PARAMS,
+                                    JdkLogMethodInvoker.class.getName()),
+                            adaptor().getCallerMethodName(
+                                    JdkLogMethodInvoker.LOG_STRING_PARAMS,
+                                    "logX"), null, localized, params);
+                return null;
+            };
+            JdkLogTester tester = (x, level) -> {
+                JdkLogMethodInvoker.LOG_STRING_PARAMS.logX(x, level, msg, params);
+                return null;
+            };
+            Function<String, String> nameProducer = (l) -> "log(Level." + l + ", \"" + msg + "\", params...)";
+            testJdkLog(logger, tester, check, nameProducer);
+        }
+
+        public void testLogrb(java.lang.System.Logger logger,
+                ResourceBundle bundle, String msg, Object... params) {
+            Checker<BackendRecord, Level> check = (res, l) -> {
+                checkRecord("log", res, logger.getName(), l, () -> msg,
+                            adaptor().getCallerClassName(
+                                    JdkLogMethodInvoker.LOGRB_STRING_PARAMS,
+                                    JdkLogMethodInvoker.class.getName()),
+                            adaptor().getCallerMethodName(
+                                    JdkLogMethodInvoker.LOGRB_STRING_PARAMS,
+                                    "logX"), null, bundle, params);
+                return null;
+            };
+            JdkLogTester tester = (x, level) -> {
+                JdkLogMethodInvoker.LOGRB_STRING_PARAMS.logX(x, level, bundle, msg, params);
+                return null;
+            };
+            Function<String, String> nameProducer = (l) -> "log(Level." + l
+                    + ", bundle, \"" + msg + "\", params...)";
+            testJdkLog(logger, tester, check, nameProducer);
+        }
+
+        public void testLog(java.lang.System.Logger logger, String msg, Throwable thrown) {
+            Checker<BackendRecord, Level> check = (res, l) -> {
+                checkRecord("log", res, logger.getName(), l, () -> msg,
+                           adaptor().getCallerClassName(
+                                    JdkLogMethodInvoker.LOG_STRING_THROWN,
+                                    JdkLogMethodInvoker.class.getName()),
+                            adaptor().getCallerMethodName(
+                                    JdkLogMethodInvoker.LOG_STRING_THROWN,
+                                    "logX"), thrown, localized);
+                return null;
+            };
+            JdkLogTester tester = (x, level) -> {
+                JdkLogMethodInvoker.LOG_STRING_THROWN.logX(x, level, msg, thrown);
+                return null;
+            };
+            Function<String, String> nameProducer = (l) ->
+                    "log(Level." + l + ", \"" + msg + "\", thrown)";
+            testJdkLog(logger, tester, check, nameProducer);
+        }
+
+        public void testLogrb(java.lang.System.Logger logger,
+                ResourceBundle bundle, String msg, Throwable thrown) {
+            Checker<BackendRecord, Level> check = (res, l) -> {
+                checkRecord("log", res, logger.getName(), l, () -> msg,
+                            adaptor().getCallerClassName(
+                                    JdkLogMethodInvoker.LOGRB_STRING_THROWN,
+                                    JdkLogMethodInvoker.class.getName()),
+                            adaptor().getCallerMethodName(
+                                    JdkLogMethodInvoker.LOGRB_STRING_THROWN,
+                                    "logX"), thrown, bundle);
+                return null;
+            };
+            JdkLogTester tester = (x, level) -> {
+                JdkLogMethodInvoker.LOGRB_STRING_THROWN.logX(x, level, bundle, msg, thrown);
+                return null;
+            };
+            Function<String, String> nameProducer = (l) ->
+                    "log(Level." + l + ", bundle, \"" + msg + "\", thrown)";
+            testJdkLog(logger, tester, check, nameProducer);
+        }
+
+        public void testLog(java.lang.System.Logger logger, Supplier<String> msg) {
+            Checker<BackendRecord, Level> check = (res, l) -> {
+                checkRecord("log", res, logger.getName(), l, msg,
+                            adaptor().getCallerClassName(
+                                    JdkLogMethodInvoker.LOG_SUPPLIER,
+                                    JdkLogMethodInvoker.class.getName()),
+                            adaptor().getCallerMethodName(
+                                    JdkLogMethodInvoker.LOG_SUPPLIER,
+                                    "logX"), null, null);
+                return null;
+            };
+            JdkLogTester tester = (x, level) -> {
+                JdkLogMethodInvoker.LOG_SUPPLIER.logX(x, level, msg);
+                return null;
+            };
+            Function<String, String> nameProducer = (l) ->
+                    "log(Level." + l + ", () -> \"" + msg.get() + "\")";
+            testJdkLog(logger, tester, check, nameProducer);
+        }
+
+        public void testLog(java.lang.System.Logger logger, Throwable thrown, Supplier<String> msg) {
+            Checker<BackendRecord, Level> check = (res, l) -> {
+                checkRecord("log", res, logger.getName(), l, msg,
+                            adaptor().getCallerClassName(
+                                    JdkLogMethodInvoker.LOG_SUPPLIER_THROWN,
+                                    JdkLogMethodInvoker.class.getName()),
+                            adaptor().getCallerMethodName(
+                                    JdkLogMethodInvoker.LOG_SUPPLIER_THROWN,
+                                    "logX"), thrown, null);
+                return null;
+            };
+            JdkLogTester tester = (x, level) -> {
+                JdkLogMethodInvoker.LOG_SUPPLIER_THROWN.logX(x, level, thrown, msg);
+                return null;
+            };
+            Function<String, String> nameProducer = (l) ->
+                    "log(Level." + l + ", () -> \"" + msg.get() + "\", thrown)";
+            testJdkLog(logger, tester, check, nameProducer);
+        }
+
+        static Supplier<String> logpMessage(ResourceBundle bundle,
+                String className, String methodName, Supplier<String> msg) {
+            if (BEST_EFFORT_FOR_LOGP && bundle == null
+                    && (className != null || methodName != null)) {
+                final String cName = className == null ? "" :  className;
+                final String mName = methodName == null ? "" : methodName;
+                return () -> {
+                    String m = msg.get();
+                    return String.format("[%s %s] %s", cName, mName, m == null ? "" : m);
+                };
+            } else {
+                return msg;
+            }
+        }
+
+        public void testLogp(java.lang.System.Logger logger, String className,
+                String methodName, String msg) {
+            Checker<BackendRecord, Level> check = (res, l) -> {
+                checkRecord("logp", res, logger.getName(), l,
+                            logpMessage(localized, className, methodName, () -> msg),
+                            adaptor().getCallerClassName(
+                                    JdkLogMethodInvoker.LOGP_STRING, className),
+                            adaptor().getCallerClassName(
+                                    JdkLogMethodInvoker.LOGP_STRING, methodName),
+                            null, localized);
+                return null;
+            };
+            JdkLogTester tester = (x, level) -> {
+                JdkLogMethodInvoker.LOGP_STRING.logX(x, level,
+                        className, methodName, msg);
+                return null;
+            };
+            Function<String, String> nameProducer = (l) ->
+                    "logp(Level." + l + ", class, method, \"" + msg + "\")";
+            testJdkLog(logger, tester, check, nameProducer);
+        }
+
+        public void testLogrb(java.lang.System.Logger logger, String className,
+                String methodName, ResourceBundle bundle, String msg) {
+            Checker<BackendRecord, Level> check = (res, l) -> {
+                checkRecord("logp", res, logger.getName(), l, () -> msg,
+                            adaptor().getCallerClassName(
+                                    JdkLogMethodInvoker.LOGRBP_STRING_PARAMS, className),
+                            adaptor().getCallerClassName(
+                                    JdkLogMethodInvoker.LOGRBP_STRING_PARAMS, methodName),
+                            null, bundle);
+                return null;
+            };
+            JdkLogTester tester = (x, level) -> {
+                JdkLogMethodInvoker.LOGRBP_STRING_PARAMS.logX(x, level,
+                        className, methodName, bundle, msg, (Object[])null);
+                return null;
+            };
+            Function<String, String> nameProducer = (l) ->
+                    "logp(Level." + l + ", class, method, bundle, \"" + msg + "\")";
+            testJdkLog(logger, tester, check, nameProducer);
+        }
+
+        public void testLogp(java.lang.System.Logger logger, String className,
+                String methodName, String msg, Object... params) {
+            Checker<BackendRecord, Level> check = (res, l) -> {
+                checkRecord("logp", res, logger.getName(), l,
+                            logpMessage(localized, className, methodName, () -> msg),
+                            adaptor().getCallerClassName(
+                                    JdkLogMethodInvoker.LOGP_STRING_PARAMS, className),
+                            adaptor().getCallerClassName(
+                                    JdkLogMethodInvoker.LOGP_STRING_PARAMS, methodName),
+                            null, localized, params);
+                return null;
+            };
+            JdkLogTester tester = (x, level) -> {
+                JdkLogMethodInvoker.LOGP_STRING_PARAMS.logX(x, level,
+                        className, methodName, msg, params);
+                return null;
+            };
+            Function<String, String> nameProducer = (l) ->
+                    "log(Level." + l + ", class, method, \"" + msg + "\", params...)";
+            testJdkLog(logger, tester, check, nameProducer);
+        }
+
+        public void testLogrb(java.lang.System.Logger logger, String className,
+                String methodName, ResourceBundle bundle, String msg,
+                Object... params) {
+            Checker<BackendRecord, Level> check = (res, l) -> {
+                checkRecord("logp", res, logger.getName(), l, () -> msg,
+                            adaptor().getCallerClassName(
+                                    JdkLogMethodInvoker.LOGRBP_STRING_PARAMS, className),
+                            adaptor().getCallerClassName(
+                                    JdkLogMethodInvoker.LOGRBP_STRING_PARAMS, methodName),
+                            null, bundle, params);
+                return null;
+            };
+            JdkLogTester tester = (x, level) -> {
+                JdkLogMethodInvoker.LOGRBP_STRING_PARAMS.logX(x, level,
+                        className, methodName, bundle, msg, params);
+                return null;
+            };
+            Function<String, String> nameProducer = (l) ->
+                    "log(Level." + l + ", class, method, bundle, \""
+                            + msg + "\", params...)";
+            testJdkLog(logger, tester, check, nameProducer);
+        }
+
+        public void testLogp(java.lang.System.Logger logger, String className,
+                String methodName, String msg, Throwable thrown) {
+            Checker<BackendRecord, Level> check = (res, l) -> {
+                checkRecord("log", res, logger.getName(), l,
+                            logpMessage(localized, className, methodName, () -> msg),
+                            adaptor().getCallerClassName(
+                                    JdkLogMethodInvoker.LOGP_STRING_THROWN, className),
+                            adaptor().getCallerClassName(
+                                    JdkLogMethodInvoker.LOGP_STRING_THROWN, methodName),
+                            thrown, localized);
+                return null;
+            };
+            JdkLogTester tester = (x, level) -> {
+                JdkLogMethodInvoker.LOGP_STRING_THROWN.logX(x, level,
+                        className, methodName, msg, thrown);
+                return null;
+            };
+            Function<String, String> nameProducer = (l) ->
+                    "log(Level." + l + ", class, method, \"" + msg + "\", thrown)";
+            testJdkLog(logger, tester, check, nameProducer);
+        }
+
+        public void testLogrb(java.lang.System.Logger logger, String className,
+                String methodName, ResourceBundle bundle,
+                String msg, Throwable thrown) {
+            Checker<BackendRecord, Level> check = (res, l) -> {
+                checkRecord("log", res, logger.getName(), l, () -> msg,
+                            adaptor().getCallerClassName(
+                                    JdkLogMethodInvoker.LOGRBP_STRING_THROWN, className),
+                            adaptor().getCallerClassName(
+                                    JdkLogMethodInvoker.LOGRBP_STRING_THROWN, methodName),
+                            thrown, bundle);
+                return null;
+            };
+            JdkLogTester tester = (x, level) -> {
+                JdkLogMethodInvoker.LOGRBP_STRING_THROWN.logX(x, level,
+                        className, methodName, bundle, msg, thrown);
+                return null;
+            };
+            Function<String, String> nameProducer = (l) ->
+                    "log(Level." + l + ", class, method, bundle, \"" + msg + "\", thrown)";
+            testJdkLog(logger, tester, check, nameProducer);
+
+        }
+
+        public void testLogp(java.lang.System.Logger logger, String className,
+                String methodName, Supplier<String> msg) {
+            Checker<BackendRecord, Level> check = (res, l) -> {
+                checkRecord("log", res, logger.getName(), l,
+                            logpMessage(null, className, methodName, msg),
+                            adaptor().getCallerClassName(
+                                    JdkLogMethodInvoker.LOGP_SUPPLIER, className),
+                            adaptor().getCallerClassName(
+                                    JdkLogMethodInvoker.LOGP_SUPPLIER, methodName),
+                            null, null);
+                return null;
+            };
+            JdkLogTester tester = (x, level) -> {
+                JdkLogMethodInvoker.LOGP_SUPPLIER.logX(x, level,
+                        className, methodName, msg);
+                return null;
+            };
+            Function<String, String> nameProducer = (l) ->
+                    "log(Level." + l + ", class, method, () -> \"" + msg.get() + "\")";
+            testJdkLog(logger, tester, check, nameProducer);
+        }
+
+        public void testLogp(java.lang.System.Logger logger, String className,
+                String methodName, Throwable thrown, Supplier<String> msg) {
+            Checker<BackendRecord, Level> check = (res, l) -> {
+                checkRecord("log", res, logger.getName(), l,
+                            logpMessage(null, className, methodName, msg),
+                            adaptor().getCallerClassName(
+                                    JdkLogMethodInvoker.LOGP_SUPPLIER_THROWN, className),
+                            adaptor().getCallerClassName(
+                                    JdkLogMethodInvoker.LOGP_SUPPLIER_THROWN, methodName),
+                            thrown, null);
+                return null;
+            };
+            JdkLogTester tester = (x, level) -> {
+                JdkLogMethodInvoker.LOGP_SUPPLIER_THROWN.logX(x, level,
+                        className, methodName, thrown, msg);
+                return null;
+            };
+            Function<String, String> nameProducer = (l) ->
+                    "log(Level." + l + ", class, method, () -> \"" + msg.get() + "\", thrown)";
+            testJdkLog(logger, tester, check, nameProducer);
+        }
+
+        private void testJdkLog(java.lang.System.Logger logger,
+                JdkLogTester log, Checker<BackendRecord,Level> check,
+                Function<String, String> nameProducer) {
+            if (restrictedTo != null) {
+                if (!bridgeLoggerClass.isAssignableFrom(restrictedTo)) {
+                    if (VERBOSE) {
+                        System.out.println("Skipping method from "
+                            + bridgeLoggerClass);
+                    }
+                    return;
+                }
+            }
+            System.out.println("Testing Logger." + nameProducer.apply("*")
+                     + " on " + logger);
+            final BackendAdaptor adaptor = adaptor();
+            for (Level loggerLevel : LEVELS) {
+                adaptor.setLevel(logger, loggerLevel);
+                for (Level l : LEVELS) {
+                    check(logger, () -> log.apply(bridgeLoggerClass.cast(logger), l),
+                          check, () -> adaptor.isLoggable(logger, l),
+                          () -> adaptor.shouldBeLoggable(l, loggerLevel),
+                          l, loggerLevel, nameProducer.apply(l.toString()));
+                }
+            }
+        }
+
+        private void testSpiLog(java.lang.System.Logger logger,
+                SpiLogTester log, Checker<BackendRecord, java.lang.System.Logger.Level> check,
+                Function<String, String> nameProducer) {
+            System.out.println("Testing System.Logger." + nameProducer.apply("*")
+                     + " on " + logger);
+            final BackendAdaptor adaptor = adaptor();
+            for (java.lang.System.Logger.Level loggerLevel : java.lang.System.Logger.Level.values()) {
+
+                adaptor.setLevel(logger, loggerLevel);
+                for (java.lang.System.Logger.Level l : java.lang.System.Logger.Level.values()) {
+                    check(logger, () -> log.apply(logger, l),
+                          check, () -> logger.isLoggable(l),
+                          () -> adaptor.shouldBeLoggable(l, loggerLevel),
+                          l, loggerLevel, nameProducer.apply(l.toString()));
+                }
+            }
+        }
+
+        private void test(String args, Levels level, java.lang.System.Logger logger,
+                Runnable test, Checker<BackendRecord, Level> check) {
+            if (restrictedTo != null) {
+                if (!level.definingClass.isAssignableFrom(restrictedTo)) {
+                    if (VERBOSE) {
+                        System.out.println("Skipping method from "
+                            + level.definingClass);
+                    }
+                    return;
+                }
+            }
+            String method = args.contains("bundle") ? "logrb" : "log";
+            System.out.println("Testing Logger."
+                    + method + "(Level." + level.platformLevel
+                    + ", "+ args + ")" + " on " + logger);
+            final BackendAdaptor adaptor = adaptor();
+            for (Level loggerLevel : LEVELS) {
+                adaptor.setLevel(logger, loggerLevel);
+                check(logger, test, check,
+                        () -> level.isEnabled(logger),
+                        () -> adaptor.shouldBeLoggable(level, loggerLevel),
+                        level.platformLevel, loggerLevel, level.method);
+            }
+        }
+
+        private <L> void check(java.lang.System.Logger logger,
+                Runnable test, Checker<BackendRecord,L> check,
+                BooleanSupplier checkLevelEnabled,
+                BooleanSupplier shouldBeLoggable,
+                L logLevel, L loggerLevel, String logMethod) {
+            final BackendAdaptor adaptor = adaptor();
+            adaptor.resetBackendRecords();
+            test.run();
+            final List<BackendRecord> records = adaptor.getBackendRecords();
+            if (shouldBeLoggable.getAsBoolean()) {
+                if (!checkLevelEnabled.getAsBoolean()) {
+                    throw new RuntimeException("Logger is not enabled for "
+                            + logMethod
+                            + " although logger level is " + loggerLevel);
+                }
+                if (records.size() != 1) {
+                    throw new RuntimeException(loggerLevel + " [" +
+                            logLevel + "] : Unexpected record sizes: "
+                        + records.toString());
+                }
+                BackendRecord res = records.get(0);
+                check.apply(res, logLevel);
+            } else {
+                if (checkLevelEnabled.getAsBoolean()) {
+                    throw new RuntimeException("Logger is enabled for "
+                            + logMethod
+                            + " although logger level is " + loggerLevel);
+                }
+                if (!records.isEmpty()) {
+                    throw new RuntimeException(loggerLevel + " [" +
+                            logLevel + "] : Unexpected record sizes: "
+                        + records.toString());
+                }
+            }
+        }
+    }
+
+    public static class JULBackendTester extends BackendTester<LogRecord>{
+
+        public JULBackendTester(boolean isSystem) {
+            this(isSystem,null,null);
+        }
+        public JULBackendTester(boolean isSystem, ResourceBundle localized) {
+            this(isSystem,null,localized);
+        }
+        public JULBackendTester(boolean isSystem,
+                Class<? extends java.lang.System.Logger> restrictedTo) {
+            this(isSystem, restrictedTo, null);
+        }
+        public JULBackendTester(boolean isSystem,
+                Class<? extends java.lang.System.Logger> restrictedTo,
+                ResourceBundle localized) {
+            super(isSystem, restrictedTo, localized);
+        }
+
+        Logger getBackendLogger(String name) {
+            if (isSystem) {
+                return LoggingProviderImpl.getLogManagerAccess().demandLoggerFor(
+                        LogManager.getLogManager(), name, Thread.class);
+            } else {
+                return Logger.getLogger(name);
+            }
+        }
+
+        class JULBackendAdaptor extends BackendAdaptor {
+            @Override
+            public String getLoggerName(LogRecord res) {
+                return res.getLoggerName();
+            }
+            @Override
+            public Level getLevel(LogRecord res) {
+                return Level.valueOf(res.getLevel().getName());
+            }
+            @Override
+            public String getMessage(LogRecord res) {
+                return res.getMessage();
+            }
+            @Override
+            public String getSourceClassName(LogRecord res) {
+                return res.getSourceClassName();
+            }
+            @Override
+            public String getSourceMethodName(LogRecord res) {
+                return res.getSourceMethodName();
+            }
+            @Override
+            public Throwable getThrown(LogRecord res) {
+                return res.getThrown();
+            }
+            @Override
+            public ResourceBundle getResourceBundle(LogRecord res) {
+                return res.getResourceBundle();
+            }
+            @Override
+            public void setLevel(java.lang.System.Logger logger, Level level) {
+                Logger backend = getBackendLogger(logger.getName());
+                backend.setLevel(java.util.logging.Level.parse(level.name()));
+            }
+            @Override
+            public void setLevel(java.lang.System.Logger logger, java.lang.System.Logger.Level level) {
+                setLevel(logger, toJUL(level));
+            }
+            @Override
+            public List<LogRecord> getBackendRecords() {
+                return handler.records;
+            }
+            @Override
+            public void resetBackendRecords() {
+                handler.reset();
+            }
+            @Override
+            public Level getMappedLevel(Object level) {
+                if (level instanceof java.lang.System.Logger.Level) {
+                    return toJUL((java.lang.System.Logger.Level)level);
+                }
+                return (Level)level;
+            }
+        }
+
+        final JULBackendAdaptor julAdaptor = new JULBackendAdaptor();
+
+        @Override
+        BackendAdaptor adaptor() {
+            return julAdaptor;
+        }
+
+    }
+
+    public abstract static class BackendTesterFactory {
+        public abstract BackendTester createBackendTester(boolean isSystem);
+        public abstract  BackendTester createBackendTester(boolean isSystem,
+                Class<? extends java.lang.System.Logger> restrictedTo);
+        public abstract  BackendTester createBackendTester(boolean isSystem,
+                Class<? extends java.lang.System.Logger> restrictedTo,
+                ResourceBundle bundle);
+        public abstract  BackendTester createBackendTester(boolean isSystem,
+                ResourceBundle bundle);
+    }
+
+    public static class JULBackendTesterFactory extends BackendTesterFactory {
+
+        @Override
+        public BackendTester createBackendTester(boolean isSystem) {
+            return new JULBackendTester(isSystem);
+        }
+
+        @Override
+        public BackendTester createBackendTester(boolean isSystem,
+                Class<? extends java.lang.System.Logger> restrictedTo) {
+            return new JULBackendTester(isSystem, restrictedTo);
+        }
+
+        @Override
+        public BackendTester createBackendTester(boolean isSystem,
+                Class<? extends java.lang.System.Logger> restrictedTo,
+                ResourceBundle bundle) {
+            return new JULBackendTester(isSystem, restrictedTo, bundle);
+        }
+
+        @Override
+        public BackendTester createBackendTester(boolean isSystem,
+                ResourceBundle bundle) {
+            return new JULBackendTester(isSystem, bundle);
+        }
+    }
+
+    public static class CustomLoggerFinder extends LoggerFinder {
+
+        static enum CustomLevel { OFF, FATAL, ERROR, WARN, INFO, DEBUG, TRACE, ALL };
+        static CustomLevel[] customLevelMap = { CustomLevel.ALL,
+            CustomLevel.TRACE, CustomLevel.DEBUG, CustomLevel.INFO,
+            CustomLevel.WARN, CustomLevel.ERROR, CustomLevel.OFF
+        };
+        static class CustomLogRecord {
+            public final CustomLevel logLevel;
+            public final java.lang.System.Logger logger;
+            public final String msg;
+            public final Object[] params;
+            public final Throwable thrown;
+            public final ResourceBundle bundle;
+
+            CustomLogRecord(java.lang.System.Logger producer,
+                    CustomLevel level, String msg) {
+                this(producer, level, msg, (ResourceBundle)null, (Throwable)null, (Object[])null);
+            }
+
+            CustomLogRecord(java.lang.System.Logger producer,
+                    CustomLevel level, String msg, ResourceBundle bundle,
+                    Throwable thrown, Object... params) {
+                this.logger   = producer;
+                this.logLevel = level;
+                this.msg = msg;
+                this.params = params;
+                this.thrown = thrown;
+                this.bundle = bundle;
+            }
+        }
+
+        static final List<CustomLogRecord>  records =
+                Collections.synchronizedList(new ArrayList<>());
+
+        static class CustomLogger implements java.lang.System.Logger {
+
+            final String name;
+            volatile CustomLevel level;
+            CustomLogger(String name) {
+                this.name = name;
+                this.level = CustomLevel.INFO;
+            }
+
+            @Override
+            public String getName() {
+                return name;
+            }
+
+            public void setLevel(CustomLevel level) {
+                this.level = level;
+            }
+
+
+            @Override
+            public boolean isLoggable(java.lang.System.Logger.Level level) {
+
+                return this.level != CustomLevel.OFF && this.level.ordinal()
+                        >= customLevelMap[level.ordinal()].ordinal();
+            }
+
+            @Override
+            public void log(java.lang.System.Logger.Level level, ResourceBundle bundle, String key, Throwable thrown) {
+                if (isLoggable(level)) {
+                    records.add(new CustomLogRecord(this, customLevelMap[level.ordinal()],
+                              key, bundle, thrown));
+                }
+            }
+
+            @Override
+            public void log(java.lang.System.Logger.Level level, ResourceBundle bundle, String format, Object... params) {
+                if (isLoggable(level)) {
+                    records.add(new CustomLogRecord(this, customLevelMap[level.ordinal()],
+                              format, bundle, null, params));
+                }
+            }
+
+        }
+
+        final Map<String, java.lang.System.Logger> applicationLoggers =
+                Collections.synchronizedMap(new HashMap<>());
+        final Map<String, java.lang.System.Logger> systemLoggers =
+                Collections.synchronizedMap(new HashMap<>());
+
+        @Override
+        public java.lang.System.Logger getLogger(String name, Class<?> caller) {
+            ClassLoader callerLoader = caller.getClassLoader();
+            if (callerLoader == null) {
+                systemLoggers.putIfAbsent(name, new CustomLogger(name));
+                return systemLoggers.get(name);
+            } else {
+                applicationLoggers.putIfAbsent(name, new CustomLogger(name));
+                return applicationLoggers.get(name);
+            }
+        }
+
+        CustomLevel fromJul(Level level) {
+            if (level.intValue() == Level.OFF.intValue()) {
+                return CustomLevel.OFF;
+            } else if (level.intValue() > Level.SEVERE.intValue()) {
+                return CustomLevel.ERROR;
+            } else if (level.intValue() > Level.WARNING.intValue()) {
+                return CustomLevel.ERROR;
+            } else if (level.intValue() > Level.INFO.intValue()) {
+                return CustomLevel.WARN;
+            } else if (level.intValue() > Level.CONFIG.intValue()) {
+                return CustomLevel.INFO;
+            } else if (level.intValue() > Level.FINER.intValue()) {
+                return CustomLevel.DEBUG;
+            } else if (level.intValue() > Level.FINEST.intValue()) {
+                return CustomLevel.TRACE;
+            } else if (level.intValue() == Level.ALL.intValue()) {
+                return CustomLevel.ALL;
+            } else {
+                return CustomLevel.TRACE;
+            }
+        }
+
+        Level toJul(CustomLevel level) {
+            switch(level) {
+                case OFF: return Level.OFF;
+                case FATAL: return Level.SEVERE;
+                case ERROR: return Level.SEVERE;
+                case WARN: return Level.WARNING;
+                case INFO: return Level.INFO;
+                case DEBUG: return Level.FINE;
+                case TRACE: return Level.FINER;
+                case ALL: return Level.ALL;
+                default: throw new InternalError("No such level: "+level);
+            }
+        }
+
+    }
+
+    public static class CustomBackendTester extends
+            BackendTester<CustomLoggerFinder.CustomLogRecord> {
+
+        public final CustomLoggerFinder provider;
+
+        public CustomBackendTester(boolean isSystem) {
+            this(isSystem, null, null);
+        }
+
+        public CustomBackendTester(boolean isSystem,
+                Class<? extends java.lang.System.Logger> restrictedTo) {
+            this(isSystem, restrictedTo, null);
+        }
+
+        public CustomBackendTester(boolean isSystem,
+                ResourceBundle localized) {
+            this(isSystem, null, localized);
+        }
+
+        public CustomBackendTester(boolean isSystem,
+                Class<? extends java.lang.System.Logger> restrictedTo,
+                ResourceBundle localized) {
+            super(isSystem, restrictedTo, localized);
+            provider = (CustomLoggerFinder)java.lang.System.LoggerFinder.getLoggerFinder();
+        }
+
+        @Override
+        public java.lang.System.Logger convert(java.lang.System.Logger logger) {
+            if (restrictedTo != null && restrictedTo.isInstance(logger)) {
+                return logger;
+            } else if (restrictedTo == jdkLoggerClass) {
+                return logger;
+            } else {
+                return java.lang.System.Logger.class.cast(
+                        sun.util.logging.PlatformLogger.Bridge.convert(logger));
+            }
+        }
+
+        class CustomBackendAdaptor extends BackendAdaptor {
+
+            @Override
+            public String getLoggerName(CustomLoggerFinder.CustomLogRecord res) {
+                return res.logger.getName();
+            }
+
+            @Override
+            public CustomLoggerFinder.CustomLevel getLevel(CustomLoggerFinder.CustomLogRecord res) {
+                return res.logLevel;
+            }
+
+            @Override
+            public String getMessage(CustomLoggerFinder.CustomLogRecord res) {
+                return res.msg;
+            }
+
+            @Override // we don't support source class name in our custom provider implementation
+            public String getSourceClassName(CustomLoggerFinder.CustomLogRecord res) {
+                return null;
+            }
+
+            @Override // we don't support source method name in our custom provider implementation
+            public String getSourceMethodName(CustomLoggerFinder.CustomLogRecord res) {
+                return null;
+            }
+
+            @Override
+            public Throwable getThrown(CustomLoggerFinder.CustomLogRecord res) {
+                return res.thrown;
+            }
+
+            @Override
+            public ResourceBundle getResourceBundle(CustomLoggerFinder.CustomLogRecord res) {
+                return res.bundle;
+            }
+
+            @Override
+            public void setLevel(java.lang.System.Logger logger, Level level) {
+                final CustomLoggerFinder.CustomLogger l =
+                        (CustomLoggerFinder.CustomLogger)
+                        (isSystem ? provider.getLogger(logger.getName(), Thread.class) :
+                        provider.getLogger(logger.getName(), LoggerFinderBackendTest.class));
+                l.setLevel(provider.fromJul(level));
+            }
+            @Override
+            public void setLevel(java.lang.System.Logger logger,
+                    java.lang.System.Logger.Level level) {
+                setLevel(logger, toJUL(level));
+            }
+
+            CustomLoggerFinder.CustomLevel getLevel(java.lang.System.Logger logger) {
+                final CustomLoggerFinder.CustomLogger l =
+                        (CustomLoggerFinder.CustomLogger)
+                        (isSystem ? provider.getLogger(logger.getName(), Thread.class) :
+                        provider.getLogger(logger.getName(), LoggerFinderBackendTest.class));
+                return l.level;
+            }
+
+            @Override
+            public List<CustomLoggerFinder.CustomLogRecord> getBackendRecords() {
+                return CustomLoggerFinder.records;
+            }
+
+            @Override
+            public void resetBackendRecords() {
+                CustomLoggerFinder.records.clear();
+            }
+
+            @Override
+            public boolean shouldBeLoggable(Levels level, Level loggerLevel) {
+                return loggerLevel != Level.OFF &&
+                       fromLevels(level).ordinal() <= provider.fromJul(loggerLevel).ordinal();
+            }
+
+            @Override
+            public boolean isLoggable(java.lang.System.Logger logger, Level l) {
+                return super.isLoggable(logger, l);
+            }
+
+            @Override
+            public boolean shouldBeLoggable(Level logLevel, Level loggerLevel) {
+                return loggerLevel != Level.OFF &&
+                        provider.fromJul(logLevel).ordinal() <= provider.fromJul(loggerLevel).ordinal();
+            }
+
+            @Override // we don't support source class name in our custom provider implementation
+            public String getCallerClassName(Levels level, String clazz) {
+                return null;
+            }
+
+            @Override // we don't support source method name in our custom provider implementation
+            public String getCallerMethodName(Levels level, String method) {
+                return null;
+            }
+
+            @Override // we don't support source class name in our custom provider implementation
+            public String getCallerClassName(MethodInvoker<?,?> logMethod, String clazz) {
+                return null;
+            }
+
+            @Override // we don't support source method name in our custom provider implementation
+            public String getCallerMethodName(MethodInvoker<?,?> logMethod, String method) {
+                return null;
+            }
+
+            @Override
+            public CustomLoggerFinder.CustomLevel getMappedLevel(Object level) {
+                if (level instanceof java.lang.System.Logger.Level) {
+                    final int index = ((java.lang.System.Logger.Level)level).ordinal();
+                    return CustomLoggerFinder.customLevelMap[index];
+                } else if (level instanceof Level) {
+                    return provider.fromJul((Level)level);
+                }
+                return (CustomLoggerFinder.CustomLevel) level;
+            }
+
+            CustomLoggerFinder.CustomLevel fromLevels(Levels level) {
+                switch(level) {
+                    case SEVERE:
+                        return CustomLoggerFinder.CustomLevel.ERROR;
+                    case WARNING:
+                        return CustomLoggerFinder.CustomLevel.WARN;
+                    case INFO:
+                        return CustomLoggerFinder.CustomLevel.INFO;
+                    case CONFIG: case FINE:
+                        return CustomLoggerFinder.CustomLevel.DEBUG;
+                    case FINER:  case FINEST:
+                        return CustomLoggerFinder.CustomLevel.TRACE;
+                }
+                throw new InternalError("No such level "+level);
+            }
+
+        }
+
+        @Override
+        BackendAdaptor adaptor() {
+            return new CustomBackendAdaptor();
+        }
+
+    }
+
+    public static class CustomBackendTesterFactory extends BackendTesterFactory {
+
+        @Override
+        public BackendTester createBackendTester(boolean isSystem) {
+            return new CustomBackendTester(isSystem);
+        }
+
+        @Override
+        public BackendTester createBackendTester(boolean isSystem,
+                Class<? extends java.lang.System.Logger> restrictedTo) {
+            return new CustomBackendTester(isSystem, restrictedTo);
+        }
+
+        @Override
+        public BackendTester createBackendTester(boolean isSystem,
+                Class<? extends java.lang.System.Logger> restrictedTo,
+                ResourceBundle bundle) {
+            return new CustomBackendTester(isSystem, restrictedTo, bundle);
+        }
+
+        @Override
+        public BackendTester createBackendTester(boolean isSystem,
+                ResourceBundle bundle) {
+            return new CustomBackendTester(isSystem, bundle);
+        }
+    }
+
+    static final Method getLazyLogger;
+    static final Method accessLoggerFinder;
+    static {
+        // jdk.internal.logger.LazyLoggers.getLazyLogger(name, caller);
+        try {
+            Class<?> lazyLoggers = jdk.internal.logger.LazyLoggers.class;
+            getLazyLogger = lazyLoggers.getMethod("getLazyLogger",
+                    String.class, Class.class);
+            getLazyLogger.setAccessible(true);
+            Class<?> loggerFinderLoader =
+                    Class.forName("java.lang.System$LoggerFinder");
+            accessLoggerFinder = loggerFinderLoader.getDeclaredMethod("accessProvider");
+            accessLoggerFinder.setAccessible(true);
+        } catch (Throwable ex) {
+            throw new ExceptionInInitializerError(ex);
+        }
+    }
+
+    static java.lang.System.Logger getSystemLogger(String name, Class<?> caller) throws Exception {
+        try {
+            return java.lang.System.Logger.class.cast(getLazyLogger.invoke(null, name, caller));
+        } catch (InvocationTargetException x) {
+            Throwable t = x.getTargetException();
+            if (t instanceof Exception) {
+                throw (Exception)t;
+            } else {
+                throw (Error)t;
+            }
+        }
+    }
+    static java.lang.System.Logger getSystemLogger(String name,
+            ResourceBundle bundle, Class<?> caller) throws Exception {
+        try {
+            LoggerFinder provider = LoggerFinder.class.cast(accessLoggerFinder.invoke(null));
+            return provider.getLocalizedLogger(name, bundle, caller);
+        } catch (InvocationTargetException x) {
+            Throwable t = x.getTargetException();
+            if (t instanceof Exception) {
+                throw (Exception)t;
+            } else {
+                throw (Error)t;
+            }
+        }
+    }
+
+    // Change this to 'true' to get more traces...
+    public static boolean verbose = false;
+
+    public static void main(String[] argv) throws Exception {
+
+        final AtomicInteger nb = new AtomicInteger(0);
+        final boolean hidesProvider = Boolean.getBoolean("test.logger.hidesProvider");
+        System.out.println(ClassLoader.getSystemClassLoader());
+        final BackendTesterFactory factory;
+        if (java.lang.System.LoggerFinder.getLoggerFinder() instanceof CustomLoggerFinder) {
+            if (hidesProvider) {
+                System.err.println("Custom backend "
+                        + java.lang.System.LoggerFinder.getLoggerFinder()
+                        + " should have been hidden!");
+                throw new RuntimeException(
+                        "Custom backend should have been hidden: "
+                        + "check value of java.system.class.loader property");
+            }
+            System.out.println("Using custom backend");
+            factory = new CustomBackendTesterFactory();
+        } else {
+            if (!hidesProvider) {
+                System.err.println("Default JUL backend "
+                        + java.lang.System.LoggerFinder.getLoggerFinder()
+                        + " should have been hidden!");
+                throw new RuntimeException(
+                        "Default JUL backend should have been hidden: "
+                        + "check value of java.system.class.loader property");
+            }
+            System.out.println("Using JUL backend");
+            factory = new JULBackendTesterFactory();
+        }
+
+        testBackend(nb, factory);
+    }
+
+    public static void testBackend(AtomicInteger nb, BackendTesterFactory factory) throws Exception {
+
+        // Tests all level specifics methods with loggers configured with
+        // all possible levels and loggers obtained with all possible
+        // entry points from LoggerFactory and JdkLoggerFactory, with
+        // JUL as backend.
+
+        // Test a simple application logger with JUL backend
+        final BackendTester tester = factory.createBackendTester(false);
+        final java.lang.System.Logger logger =
+                java.lang.System.LoggerFinder.getLoggerFinder()
+                        .getLogger("foo", LoggerFinderBackendTest.class);
+
+        testLogger(tester, logger, nb);
+
+        // Test a simple system logger with JUL backend
+        final java.lang.System.Logger system =
+                java.lang.System.LoggerFinder.getLoggerFinder()
+                        .getLogger("bar", Thread.class);
+        final BackendTester systemTester = factory.createBackendTester(true);
+        testLogger(systemTester, system, nb);
+
+        // Test a localized application logger with null resource bundle and
+        // JUL backend
+        final java.lang.System.Logger noBundleLogger =
+                java.lang.System.LoggerFinder.getLoggerFinder()
+                        .getLocalizedLogger("baz", null, LoggerFinderBackendTest.class);
+        final BackendTester noBundleTester =
+                factory.createBackendTester(false, spiLoggerClass);
+        testLogger(noBundleTester, noBundleLogger, nb);
+
+        // Test a localized system logger with null resource bundle and JUL
+        // backend
+        final java.lang.System.Logger noBundleSysLogger =
+                java.lang.System.LoggerFinder.getLoggerFinder()
+                        .getLocalizedLogger("oof", null, Thread.class);
+        final BackendTester noBundleSysTester =
+                factory.createBackendTester(true, spiLoggerClass);
+        testLogger(noBundleSysTester, noBundleSysLogger, nb);
+
+        // Test a localized application logger with null resource bundle and
+        // JUL backend
+        try {
+            System.getLogger("baz", null);
+            throw new RuntimeException("Expected NullPointerException not thrown");
+        } catch (NullPointerException x) {
+            System.out.println("System.Loggers.getLogger(\"baz\", null): got expected " + x);
+        }
+        final java.lang.System.Logger noBundleExtensionLogger =
+                getSystemLogger("baz", null, LoggerFinderBackendTest.class);
+        final BackendTester noBundleExtensionTester =
+                factory.createBackendTester(false, jdkLoggerClass);
+        testLogger(noBundleExtensionTester, noBundleExtensionLogger, nb);
+
+        // Test a simple system logger with JUL backend
+        final java.lang.System.Logger sysExtensionLogger =
+                getSystemLogger("oof", Thread.class);
+        final BackendTester sysExtensionTester =
+                factory.createBackendTester(true, jdkLoggerClass);
+        testLogger(sysExtensionTester, sysExtensionLogger, nb);
+
+        // Test a localized system logger with null resource bundle and JUL
+        // backend
+        final java.lang.System.Logger noBundleSysExtensionLogger =
+                getSystemLogger("oof", null, Thread.class);
+        final BackendTester noBundleSysExtensionTester =
+                factory.createBackendTester(true, jdkLoggerClass);
+        testLogger(noBundleSysExtensionTester, noBundleSysExtensionLogger, nb);
+
+        // Test a localized application logger converted to JDK with null
+        // resource bundle and JUL backend
+        final java.lang.System.Logger noBundleConvertedLogger =
+                (java.lang.System.Logger)
+                sun.util.logging.PlatformLogger.Bridge.convert(noBundleLogger);
+        final BackendTester noBundleJdkTester = factory.createBackendTester(false);
+        testLogger(noBundleJdkTester, noBundleConvertedLogger, nb);
+
+        // Test a localized system logger converted to JDK with null resource
+        // bundle and JUL backend
+        final java.lang.System.Logger noBundleConvertedSysLogger =
+                (java.lang.System.Logger)
+                sun.util.logging.PlatformLogger.Bridge.convert(noBundleSysLogger);
+        final BackendTester noBundleJdkSysTester = factory.createBackendTester(true);
+        testLogger(noBundleJdkSysTester, noBundleConvertedSysLogger, nb);
+
+        // Test a localized application logger with resource bundle and JUL
+        // backend
+        final ResourceBundle bundle =
+                ResourceBundle.getBundle(ResourceBundeLocalized.class.getName());
+        final java.lang.System.Logger bundleLogger =
+                java.lang.System.LoggerFinder.getLoggerFinder()
+                        .getLocalizedLogger("toto", bundle, LoggerFinderBackendTest.class);
+        final BackendTester bundleTester =
+                factory.createBackendTester(false, spiLoggerClass, bundle);
+        testLogger(bundleTester, bundleLogger, nb);
+
+        // Test a localized system logger with resource bundle and JUL backend
+        final java.lang.System.Logger bundleSysLogger =
+                java.lang.System.LoggerFinder.getLoggerFinder()
+                        .getLocalizedLogger("titi", bundle, Thread.class);
+        final BackendTester bundleSysTester =
+                factory.createBackendTester(true, spiLoggerClass, bundle);
+        testLogger(bundleSysTester, bundleSysLogger, nb);
+
+        // Test a localized Jdk application logger with resource bundle and JUL
+        // backend
+        final java.lang.System.Logger bundleExtensionLogger =
+                System.getLogger("tita", bundle);
+        final BackendTester bundleExtensionTester =
+                factory.createBackendTester(false, jdkLoggerClass, bundle);
+        testLogger(bundleExtensionTester, bundleExtensionLogger, nb);
+
+        // Test a localized Jdk system logger with resource bundle and JUL
+        // backend
+        final java.lang.System.Logger bundleExtensionSysLogger =
+                getSystemLogger("titu", bundle, Thread.class);
+        final BackendTester bundleExtensionSysTester =
+                factory.createBackendTester(true, jdkLoggerClass, bundle);
+        testLogger(bundleExtensionSysTester, bundleExtensionSysLogger, nb);
+
+        // Test a localized application logger converted to JDK with resource
+        // bundle and JUL backend
+        final BackendTester bundleJdkTester =
+                factory.createBackendTester(false, bundle);
+        final java.lang.System.Logger bundleConvertedLogger =
+                (java.lang.System.Logger)
+                sun.util.logging.PlatformLogger.Bridge.convert(bundleLogger);
+        testLogger(bundleJdkTester, bundleConvertedLogger, nb);
+
+        // Test a localized Jdk system logger converted to JDK with resource
+        // bundle and JUL backend
+        final BackendTester bundleJdkSysTester =
+                factory.createBackendTester(true, bundle);
+        final java.lang.System.Logger bundleConvertedSysLogger =
+                (java.lang.System.Logger)
+                sun.util.logging.PlatformLogger.Bridge.convert(bundleSysLogger);
+        testLogger(bundleJdkSysTester, bundleConvertedSysLogger, nb);
+
+        // Now need to add tests for all the log/logp/logrb methods...
+
+    }
+
+    private static class FooObj {
+        final String s;
+        FooObj(String s) {
+            this.s = s;
+        }
+
+        @Override
+        public String toString() {
+            return super.toString() +": "+s;
+        }
+
+    }
+
+    public static void testLogger(BackendTester tester,
+            java.lang.System.Logger spiLogger, AtomicInteger nb) {
+
+        // Test all level-specific method forms:
+        // fatal(...) error(...) severe(...) etc...
+        java.lang.System.Logger jdkLogger = tester.convert(spiLogger);
+        for (Levels l : Levels.values()) {
+            java.lang.System.Logger logger =
+                    l.definingClass.equals(spiLoggerClass) ? spiLogger : jdkLogger;
+            tester.testLevel(l, logger, l.method + "[" + logger.getName()+ "]-"
+                    + nb.incrementAndGet());
+            tester.testLevel(l, logger, l.method + "[" + logger.getName()+ "]-"
+                    + nb.incrementAndGet(),
+                    bundleParam);
+            final int nbb = nb.incrementAndGet();
+            tester.testLevel(l, logger, () -> l.method + "[" + logger.getName()
+                    + "]-" + nbb);
+        }
+        for (Levels l : Levels.values()) {
+            java.lang.System.Logger logger =
+                    l.definingClass.equals(spiLoggerClass) ? spiLogger : jdkLogger;
+            tester.testLevel(l, logger,
+                    l.method + "[" + logger.getName()+ "]({0},{1})-"
+                    + nb.incrementAndGet(),
+                    "One", "Two");
+            tester.testLevel(l, logger,
+                    l.method + "[" + logger.getName()+ "]({0},{1})-"
+                    + nb.incrementAndGet(),
+                    bundleParam, "One", "Two");
+        }
+        final Throwable thrown = new RuntimeException("Test");
+        for (Levels l : Levels.values()) {
+            java.lang.System.Logger logger =
+                    l.definingClass.equals(spiLoggerClass) ? spiLogger : jdkLogger;
+            tester.testLevel(l, logger, l.method + "[" + logger.getName()+ "]-"
+                    + nb.incrementAndGet(),
+                    thrown);
+            tester.testLevel(l, logger, l.method + "[" + logger.getName()+ "]-"
+                    + nb.incrementAndGet(),
+                    bundleParam, thrown);
+            final int nbb = nb.incrementAndGet();
+            tester.testLevel(l, logger, ()->l.method + "[" + logger.getName()+ "]-"
+                    + nbb, thrown);
+        }
+
+        java.lang.System.Logger logger = jdkLogger;
+
+         // test System.Logger methods
+       tester.testSpiLog(logger, "[" + logger.getName()+ "]-"
+                + nb.incrementAndGet());
+        tester.testSpiLog(logger, bundleParam, "[" + logger.getName()+ "]-"
+                + nb.incrementAndGet());
+        tester.testSpiLog(logger, "[" + logger.getName()+ "]-({0},{1})"
+                + nb.incrementAndGet(), "One", "Two");
+        tester.testSpiLog(logger, bundleParam, "[" + logger.getName()+ "]-({0},{1})"
+                + nb.incrementAndGet(), "One", "Two");
+        tester.testSpiLog(logger, "[" + logger.getName()+ "]-"
+                + nb.incrementAndGet(), thrown);
+        tester.testSpiLog(logger, bundleParam, "[" + logger.getName()+ "]-"
+                + nb.incrementAndGet(), thrown);
+        final int nbb01 = nb.incrementAndGet();
+        tester.testSpiLog(logger, () -> "[" + logger.getName()+ "]-" + nbb01);
+        final int nbb02 = nb.incrementAndGet();
+        tester.testSpiLog(logger, thrown, () -> "[" + logger.getName()+ "]-" + nbb02);
+        final int nbb03 = nb.incrementAndGet();
+        tester.testSpiLog(logger, new FooObj("[" + logger.getName()+ "]-" + nbb03));
+
+        // Test all log method forms:
+        // jdk.internal.logging.Logger.log(...)
+        tester.testLog(logger, "[" + logger.getName()+ "]-"
+                + nb.incrementAndGet());
+        tester.testLogrb(logger, bundleParam, "[" + logger.getName()+ "]-"
+                + nb.incrementAndGet());
+        tester.testLog(logger, "[" + logger.getName()+ "]-({0},{1})"
+                + nb.incrementAndGet(), "One", "Two");
+        tester.testLogrb(logger, bundleParam, "[" + logger.getName()+ "]-({0},{1})"
+                + nb.incrementAndGet(), "One", "Two");
+        tester.testLog(logger, "[" + logger.getName()+ "]-"
+                + nb.incrementAndGet(), thrown);
+        tester.testLogrb(logger, bundleParam, "[" + logger.getName()+ "]-"
+                + nb.incrementAndGet(), thrown);
+        final int nbb1 = nb.incrementAndGet();
+        tester.testLog(logger, () -> "[" + logger.getName()+ "]-" + nbb1);
+        final int nbb2 = nb.incrementAndGet();
+        tester.testLog(logger, thrown, () -> "[" + logger.getName()+ "]-" + nbb2);
+
+        // Test all logp method forms
+        // jdk.internal.logging.Logger.logp(...)
+        tester.testLogp(logger, "clazz" + nb.incrementAndGet(),
+                "method" + nb.incrementAndGet(),
+                "[" + logger.getName()+ "]-"
+                + nb.incrementAndGet());
+        tester.testLogrb(logger, "clazz" + nb.incrementAndGet(),
+                "method" + nb.incrementAndGet(), bundleParam,
+                "[" + logger.getName()+ "]-"
+                + nb.incrementAndGet());
+        tester.testLogp(logger, "clazz" + nb.incrementAndGet(),
+                "method" + nb.incrementAndGet(),
+                "[" + logger.getName()+ "]-({0},{1})"
+                + nb.incrementAndGet(), "One", "Two");
+        tester.testLogrb(logger, "clazz" + nb.incrementAndGet(),
+                "method" + nb.incrementAndGet(), bundleParam,
+                "[" + logger.getName()+ "]-({0},{1})"
+                + nb.incrementAndGet(), "One", "Two");
+        tester.testLogp(logger, "clazz" + nb.incrementAndGet(),
+                "method" + nb.incrementAndGet(),
+                "[" + logger.getName()+ "]-"
+                + nb.incrementAndGet(), thrown);
+        tester.testLogrb(logger, "clazz" + nb.incrementAndGet(),
+                "method" + nb.incrementAndGet(), bundleParam,
+                "[" + logger.getName()+ "]-"
+                + nb.incrementAndGet(), thrown);
+        final int nbb3 = nb.incrementAndGet();
+        tester.testLogp(logger, "clazz" + nb.incrementAndGet(),
+                "method" + nb.incrementAndGet(),
+                () -> "[" + logger.getName()+ "]-" + nbb3);
+        final int nbb4 = nb.incrementAndGet();
+        tester.testLogp(logger, "clazz" + nb.incrementAndGet(),
+                "method" + nb.incrementAndGet(),
+                thrown, () -> "[" + logger.getName()+ "]-" + nbb4);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/LoggerFinder/internal/backend/META-INF/services/java.lang.System$LoggerFinder	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,2 @@
+LoggerFinderBackendTest$CustomLoggerFinder
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/LoggerFinder/internal/backend/SystemClassLoader.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.Enumeration;
+import java.lang.System.LoggerFinder;
+
+/**
+ * A custom class loader which can hide the registered LoggerProvider
+ * depending on the value of a test.logger.hidesProvider system property.
+ * @author danielfuchs
+ */
+public class SystemClassLoader extends ClassLoader {
+
+    final public boolean hidesProvider;
+
+    public SystemClassLoader() {
+        hidesProvider = Boolean.getBoolean("test.logger.hidesProvider");
+    }
+    public SystemClassLoader(ClassLoader parent) {
+        super(parent);
+        hidesProvider = Boolean.getBoolean("test.logger.hidesProvider");
+    }
+
+    boolean accept(String name) {
+        final boolean res = !name.endsWith(LoggerFinder.class.getName());
+        if (res == false) {
+            System.out.println("Hiding " + name);
+        }
+        return res;
+    }
+
+    @Override
+    public URL getResource(String name) {
+        if (hidesProvider && !accept(name)) {
+            return null;
+        } else {
+            return super.getResource(name);
+        }
+    }
+
+    class Enumerator implements Enumeration<URL> {
+        final Enumeration<URL> enumeration;
+        volatile URL next;
+        Enumerator(Enumeration<URL> enumeration) {
+            this.enumeration = enumeration;
+        }
+
+        @Override
+        public boolean hasMoreElements() {
+            if (next != null) return true;
+            if (!enumeration.hasMoreElements()) return false;
+            if (hidesProvider == false) return true;
+            next = enumeration.nextElement();
+            if (accept(next.getPath())) return true;
+            next = null;
+            return hasMoreElements();
+        }
+
+        @Override
+        public URL nextElement() {
+            final URL res = next == null ? enumeration.nextElement() : next;
+            next = null;
+            if (hidesProvider == false || accept(res.getPath())) return res;
+            return nextElement();
+        }
+    }
+
+    @Override
+    public Enumeration<URL> getResources(String name) throws IOException {
+        final Enumeration<URL> enumeration = super.getResources(name);
+        return hidesProvider ? new Enumerator(enumeration) : enumeration;
+    }
+
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/LoggerFinder/jdk/DefaultLoggerBridgeTest/DefaultLoggerBridgeTest.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,850 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+import java.security.AccessControlException;
+import java.security.CodeSource;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.Permissions;
+import java.security.Policy;
+import java.security.ProtectionDomain;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Queue;
+import java.util.ResourceBundle;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.Supplier;
+import java.util.logging.Handler;
+import java.util.logging.LogManager;
+import sun.util.logging.PlatformLogger;
+import java.util.logging.LogRecord;
+import java.lang.System.LoggerFinder;
+import java.lang.System.Logger;
+import java.util.stream.Stream;
+import sun.util.logging.internal.LoggingProviderImpl;
+
+/**
+ * @test
+ * @bug     8140364
+ * @summary JDK implementation specific unit test for JDK internal artifacts.
+ *          Tests all internal bridge methods with the default LoggerFinder
+ *          JUL backend.
+ * @modules java.base/sun.util.logging
+ *          java.base/jdk.internal.logger
+ *          java.logging/sun.util.logging.internal
+ * @run  main/othervm DefaultLoggerBridgeTest
+ * @author danielfuchs
+ */
+public class DefaultLoggerBridgeTest {
+
+    final static AtomicLong sequencer = new AtomicLong();
+    final static boolean VERBOSE = false;
+    static final ThreadLocal<AtomicBoolean> allowControl = new ThreadLocal<AtomicBoolean>() {
+        @Override
+        protected AtomicBoolean initialValue() {
+            return  new AtomicBoolean(false);
+        }
+    };
+    static final ThreadLocal<AtomicBoolean> allowAccess = new ThreadLocal<AtomicBoolean>() {
+        @Override
+        protected AtomicBoolean initialValue() {
+            return  new AtomicBoolean(false);
+        }
+    };
+    static final ThreadLocal<AtomicBoolean> allowAll = new ThreadLocal<AtomicBoolean>() {
+        @Override
+        protected AtomicBoolean initialValue() {
+            return  new AtomicBoolean(false);
+        }
+    };
+
+    public static final Queue<LogEvent> eventQueue = new ArrayBlockingQueue<>(128);
+
+    public static final class LogEvent implements Cloneable {
+
+        public LogEvent() {
+            this(sequencer.getAndIncrement());
+        }
+
+        LogEvent(long sequenceNumber) {
+            this.sequenceNumber = sequenceNumber;
+        }
+
+        long sequenceNumber;
+        boolean isLoggable;
+        String loggerName;
+        java.util.logging.Level level;
+        ResourceBundle bundle;
+        Throwable thrown;
+        Object[] args;
+        String msg;
+        String className;
+        String methodName;
+
+        Object[] toArray() {
+            return new Object[] {
+                sequenceNumber,
+                isLoggable,
+                loggerName,
+                level,
+                bundle,
+                thrown,
+                args,
+                msg,
+                className,
+                methodName,
+            };
+        }
+
+        @Override
+        public String toString() {
+            return Arrays.deepToString(toArray());
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            return obj instanceof LogEvent
+                    && Objects.deepEquals(this.toArray(), ((LogEvent)obj).toArray());
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(toArray());
+        }
+
+        public LogEvent cloneWith(long sequenceNumber)
+                throws CloneNotSupportedException {
+            LogEvent cloned = (LogEvent)super.clone();
+            cloned.sequenceNumber = sequenceNumber;
+            return cloned;
+        }
+
+        public static LogEvent of(long sequenceNumber,
+                boolean isLoggable, String name,
+                java.util.logging.Level level, ResourceBundle bundle,
+                String key, Throwable thrown, Object... params) {
+            return LogEvent.of(sequenceNumber, isLoggable, name,
+                    DefaultLoggerBridgeTest.class.getName(),
+                    "testLogger", level, bundle, key,
+                    thrown, params);
+        }
+        public static LogEvent of(long sequenceNumber,
+                boolean isLoggable, String name,
+                String className, String methodName,
+                java.util.logging.Level level, ResourceBundle bundle,
+                String key, Throwable thrown, Object... params) {
+            LogEvent evt = new LogEvent(sequenceNumber);
+            evt.loggerName = name;
+            evt.level = level;
+            evt.args = params;
+            evt.bundle = bundle;
+            evt.thrown = thrown;
+            evt.msg = key;
+            evt.isLoggable = isLoggable;
+            evt.className = className;
+            evt.methodName = methodName;
+            return evt;
+        }
+
+    }
+
+    static final java.util.logging.Level[] julLevels = {
+        java.util.logging.Level.ALL,
+        java.util.logging.Level.FINEST,
+        java.util.logging.Level.FINER,
+        java.util.logging.Level.FINE,
+        java.util.logging.Level.CONFIG,
+        java.util.logging.Level.INFO,
+        java.util.logging.Level.WARNING,
+        java.util.logging.Level.SEVERE,
+        java.util.logging.Level.OFF,
+    };
+
+
+    public static class MyBundle extends ResourceBundle {
+
+        final ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();
+
+        @Override
+        protected Object handleGetObject(String key) {
+            if (key.contains(" (translated)")) {
+                throw new RuntimeException("Unexpected key: " + key);
+            }
+            return map.computeIfAbsent(key, k -> k + " (translated)");
+        }
+
+        @Override
+        public Enumeration<String> getKeys() {
+            return Collections.enumeration(map.keySet());
+        }
+
+    }
+
+    public static class MyHandler extends Handler {
+
+        @Override
+        public java.util.logging.Level getLevel() {
+            return java.util.logging.Level.ALL;
+        }
+
+        @Override
+        public void publish(LogRecord record) {
+            eventQueue.add(LogEvent.of(sequencer.getAndIncrement(),
+                    true, record.getLoggerName(),
+                    record.getSourceClassName(),
+                    record.getSourceMethodName(),
+                    record.getLevel(),
+                    record.getResourceBundle(), record.getMessage(),
+                    record.getThrown(), record.getParameters()));
+        }
+        @Override
+        public void flush() {
+        }
+        @Override
+        public void close() throws SecurityException {
+        }
+
+    }
+
+    public static class MyLoggerBundle extends MyBundle {
+
+    }
+
+    static PlatformLogger.Bridge convert(Logger logger) {
+        boolean old = allowAccess.get().get();
+        allowAccess.get().set(true);
+        try {
+            return PlatformLogger.Bridge.convert(logger);
+        } finally {
+            allowAccess.get().set(old);
+        }
+    }
+
+    static Logger getLogger(String name, Class<?> caller) {
+        boolean old = allowAccess.get().get();
+        allowAccess.get().set(true);
+        try {
+            return jdk.internal.logger.LazyLoggers.getLogger(name, caller);
+        } finally {
+            allowAccess.get().set(old);
+        }
+    }
+
+    static enum TestCases {NOSECURITY, NOPERMISSIONS, WITHPERMISSIONS};
+
+    static void setSecurityManager() {
+        if (System.getSecurityManager() == null) {
+            Policy.setPolicy(new SimplePolicy(allowControl, allowAccess, allowAll));
+            System.setSecurityManager(new SecurityManager());
+        }
+    }
+
+    public static void main(String[] args) {
+        if (args.length == 0)
+            args = new String[] {
+                "NOSECURITY",
+                "NOPERMISSIONS",
+                "WITHPERMISSIONS"
+            };
+
+        Stream.of(args).map(TestCases::valueOf).forEach((testCase) -> {
+            LoggerFinder provider;
+            switch (testCase) {
+                case NOSECURITY:
+                    System.out.println("\n*** Without Security Manager\n");
+                    test(true);
+                    System.out.println("Tetscase count: " + sequencer.get());
+                    break;
+                case NOPERMISSIONS:
+                    System.out.println("\n*** With Security Manager, without permissions\n");
+                    setSecurityManager();
+                    test(false);
+                    System.out.println("Tetscase count: " + sequencer.get());
+                    break;
+                case WITHPERMISSIONS:
+                    System.out.println("\n*** With Security Manager, with control permission\n");
+                    setSecurityManager();
+                    final boolean control = allowControl.get().get();
+                    try {
+                        allowControl.get().set(true);
+                        test(true);
+                    } finally {
+                        allowControl.get().set(control);
+                    }
+                    break;
+                default:
+                    throw new RuntimeException("Unknown test case: " + testCase);
+            }
+        });
+        System.out.println("\nPASSED: Tested " + sequencer.get() + " cases.");
+    }
+
+    public static void test(boolean hasRequiredPermissions) {
+
+        ResourceBundle loggerBundle =
+                ResourceBundle.getBundle(MyLoggerBundle.class.getName());
+        final Map<Object, String> loggerDescMap = new HashMap<>();
+
+        Logger sysLogger1a = getLogger("foo", Thread.class);
+        loggerDescMap.put(sysLogger1a, "jdk.internal.logger.LazyLoggers.getLogger(\"foo\", Thread.class)");
+
+        Logger appLogger1 = System.getLogger("foo");
+        loggerDescMap.put(appLogger1, "System.getLogger(\"foo\")");
+
+        LoggerFinder provider;
+        try {
+            provider = LoggerFinder.getLoggerFinder();
+            if (!hasRequiredPermissions) {
+                throw new RuntimeException("Expected exception not raised");
+            }
+        } catch (AccessControlException x) {
+            if (hasRequiredPermissions) {
+                throw new RuntimeException("Unexpected permission check", x);
+            }
+            if (!SimplePolicy.LOGGERFINDER_PERMISSION.equals(x.getPermission())) {
+                throw new RuntimeException("Unexpected permission in exception: " + x, x);
+            }
+            final boolean control = allowControl.get().get();
+            try {
+                allowControl.get().set(true);
+                provider = LoggerFinder.getLoggerFinder();
+            } finally {
+                allowControl.get().set(control);
+            }
+        }
+
+        Logger sysLogger1b = null;
+        try {
+            sysLogger1b = provider.getLogger("foo", Thread.class);
+            if (sysLogger1b != sysLogger1a) {
+                loggerDescMap.put(sysLogger1b, "provider.getLogger(\"foo\", Thread.class)");
+            }
+            if (!hasRequiredPermissions) {
+                throw new RuntimeException("Managed to obtain a system logger without permission");
+            }
+        } catch (AccessControlException acx) {
+            if (hasRequiredPermissions) {
+                throw new RuntimeException("Unexpected security exception: ", acx);
+            }
+            if (!acx.getPermission().equals(SimplePolicy.LOGGERFINDER_PERMISSION)) {
+                throw new RuntimeException("Unexpected permission in exception: " + acx, acx);
+            }
+            System.out.println("Got expected exception for system logger: " + acx);
+        }
+
+        Logger appLogger2 = System.getLogger("foo", loggerBundle);
+        loggerDescMap.put(appLogger2, "System.getLogger(\"foo\", loggerBundle)");
+
+        if (appLogger2 == appLogger1) {
+            throw new RuntimeException("identical loggers");
+        }
+
+        Logger sysLogger2 = null;
+        try {
+            sysLogger2 = provider.getLocalizedLogger("foo", loggerBundle, Thread.class);
+            loggerDescMap.put(sysLogger2, "provider.getLocalizedLogger(\"foo\", loggerBundle, Thread.class)");
+            if (!hasRequiredPermissions) {
+                throw new RuntimeException("Managed to obtain a system logger without permission");
+            }
+        } catch (AccessControlException acx) {
+            if (hasRequiredPermissions) {
+                throw new RuntimeException("Unexpected security exception: ", acx);
+            }
+            if (!acx.getPermission().equals(SimplePolicy.LOGGERFINDER_PERMISSION)) {
+                throw new RuntimeException("Unexpected permission in exception: " + acx, acx);
+            }
+            System.out.println("Got expected exception for localized system logger: " + acx);
+        }
+        if (hasRequiredPermissions && appLogger2 == sysLogger2) {
+            throw new RuntimeException("identical loggers");
+        }
+        if (hasRequiredPermissions && sysLogger2 == sysLogger1a) {
+            throw new RuntimeException("identical loggers");
+        }
+
+        final java.util.logging.Logger appSink;
+        final java.util.logging.Logger sysSink;
+        final MyHandler appHandler;
+        final MyHandler sysHandler;
+        final boolean old = allowAll.get().get();
+        allowAll.get().set(true);
+        try {
+            sysSink = LoggingProviderImpl.getLogManagerAccess().demandLoggerFor(
+                    LogManager.getLogManager(), "foo", Thread.class);
+            appSink = LoggingProviderImpl.getLogManagerAccess().demandLoggerFor(
+                    LogManager.getLogManager(), "foo", DefaultLoggerBridgeTest.class);
+            if (appSink == sysSink) {
+                throw new RuntimeException("identical backend loggers");
+            }
+            appSink.addHandler(appHandler = new MyHandler());
+            sysSink.addHandler(sysHandler = new MyHandler());
+            appSink.setUseParentHandlers(VERBOSE);
+            sysSink.setUseParentHandlers(VERBOSE);
+        } finally {
+            allowAll.get().set(old);
+        }
+
+        try {
+            testLogger(provider, loggerDescMap, "foo", null, convert(sysLogger1a), sysSink);
+            testLogger(provider, loggerDescMap, "foo", null, convert(appLogger1), appSink);
+            testLogger(provider, loggerDescMap, "foo", loggerBundle, convert(appLogger2), appSink);
+            if (sysLogger1b != null && sysLogger1b != sysLogger1a) {
+                testLogger(provider, loggerDescMap, "foo", null, convert(sysLogger1b), sysSink);
+            }
+            if (sysLogger2 != null) {
+                testLogger(provider, loggerDescMap, "foo", loggerBundle, convert(sysLogger2), sysSink);
+            }
+        } finally {
+            allowAll.get().set(true);
+            try {
+                appSink.removeHandler(appHandler);
+                sysSink.removeHandler(sysHandler);
+            } finally {
+                allowAll.get().set(old);
+            }
+        }
+    }
+
+    public static class Foo {
+
+    }
+
+    static void verbose(String msg) {
+       if (VERBOSE) {
+           System.out.println(msg);
+       }
+    }
+
+    static void checkLogEvent(LoggerFinder provider, String desc,
+            LogEvent expected) {
+        LogEvent actual =  eventQueue.poll();
+        if (!expected.equals(actual)) {
+            throw new RuntimeException("mismatch for " + desc
+                    + "\n\texpected=" + expected
+                    + "\n\t  actual=" + actual);
+        } else {
+            verbose("Got expected results for "
+                    + desc + "\n\t" + expected);
+        }
+    }
+
+    static void checkLogEvent(LoggerFinder provider, String desc,
+            LogEvent expected, boolean expectNotNull) {
+        LogEvent actual =  eventQueue.poll();
+        if (actual == null && !expectNotNull) return;
+        if (actual != null && !expectNotNull) {
+            throw new RuntimeException("Unexpected log event found for " + desc
+                + "\n\tgot: " + actual);
+        }
+        if (!expected.equals(actual)) {
+            throw new RuntimeException("mismatch for " + desc
+                    + "\n\texpected=" + expected
+                    + "\n\t  actual=" + actual);
+        } else {
+            verbose("Got expected results for "
+                    + desc + "\n\t" + expected);
+        }
+    }
+
+    static void setLevel(java.util.logging.Logger sink, java.util.logging.Level loggerLevel) {
+        boolean before = allowAll.get().get();
+        try {
+            allowAll.get().set(true);
+            sink.setLevel(loggerLevel);
+        } finally {
+            allowAll.get().set(before);
+        }
+    }
+
+    static sun.util.logging.PlatformLogger.Level toPlatformLevel(java.util.logging.Level level) {
+        boolean old = allowAccess.get().get();
+        allowAccess.get().set(true);
+        try {
+            return sun.util.logging.PlatformLogger.Level.valueOf(level.getName());
+        } finally {
+            allowAccess.get().set(old);
+        }
+    }
+
+    // Calls the methods defined on LogProducer and verify the
+    // parameters received by the underlying logger.
+    private static void testLogger(LoggerFinder provider,
+            Map<Object, String> loggerDescMap,
+            String name,
+            ResourceBundle loggerBundle,
+            PlatformLogger.Bridge logger,
+            java.util.logging.Logger sink) {
+
+        if (loggerDescMap.get(logger) == null) {
+            throw new RuntimeException("Missing description for " + logger);
+        }
+        System.out.println("Testing " + loggerDescMap.get(logger) + " [" + logger + "]");
+        final java.util.logging.Level OFF = java.util.logging.Level.OFF;
+
+        Foo foo = new Foo();
+        String fooMsg = foo.toString();
+        System.out.println("\tlogger.log(messageLevel, fooMsg)");
+        System.out.println("\tlogger.<level>(fooMsg)");
+        for (java.util.logging.Level loggerLevel : julLevels) {
+            setLevel(sink, loggerLevel);
+            for (java.util.logging.Level messageLevel :julLevels) {
+                String desc = "logger.log(messageLevel, fooMsg): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                LogEvent expected =
+                        LogEvent.of(
+                            sequencer.get(),
+                            loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
+                            name, messageLevel, loggerBundle,
+                            fooMsg, (Throwable)null, (Object[])null);
+                logger.log(toPlatformLevel(messageLevel), fooMsg);
+                checkLogEvent(provider, desc, expected, expected.isLoggable);
+            }
+        }
+
+        Supplier<String> supplier = new Supplier<String>() {
+            @Override
+            public String get() {
+                return this.toString();
+            }
+        };
+        System.out.println("\tlogger.log(messageLevel, supplier)");
+        System.out.println("\tlogger.<level>(supplier)");
+        for (java.util.logging.Level loggerLevel : julLevels) {
+            setLevel(sink, loggerLevel);
+            for (java.util.logging.Level messageLevel :julLevels) {
+                String desc = "logger.log(messageLevel, supplier): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                LogEvent expected =
+                        LogEvent.of(
+                            sequencer.get(),
+                            loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
+                            name, messageLevel, null,
+                            supplier.get(), (Throwable)null, (Object[])null);
+                logger.log(toPlatformLevel(messageLevel), supplier);
+                checkLogEvent(provider, desc, expected, expected.isLoggable);
+            }
+        }
+
+        String format = "two params [{1} {2}]";
+        Object arg1 = foo;
+        Object arg2 = fooMsg;
+        System.out.println("\tlogger.log(messageLevel, format, arg1, arg2)");
+        for (java.util.logging.Level loggerLevel : julLevels) {
+            setLevel(sink, loggerLevel);
+            for (java.util.logging.Level messageLevel :julLevels) {
+                String desc = "logger.log(messageLevel, format, foo, fooMsg): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                LogEvent expected =
+                        LogEvent.of(
+                            sequencer.get(),
+                            loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
+                            name, messageLevel, loggerBundle,
+                            format, (Throwable)null, arg1, arg2);
+                logger.log(toPlatformLevel(messageLevel), format, arg1, arg2);
+                checkLogEvent(provider, desc, expected, expected.isLoggable);
+            }
+        }
+
+        Throwable thrown = new Exception("OK: log me!");
+        System.out.println("\tlogger.log(messageLevel, fooMsg, thrown)");
+        for (java.util.logging.Level loggerLevel : julLevels) {
+            setLevel(sink, loggerLevel);
+            for (java.util.logging.Level messageLevel :julLevels) {
+                String desc = "logger.log(messageLevel, fooMsg, thrown): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                LogEvent expected =
+                        LogEvent.of(
+                            sequencer.get(),
+                            loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
+                            name, messageLevel, loggerBundle,
+                            fooMsg, thrown, (Object[])null);
+                logger.log(toPlatformLevel(messageLevel), fooMsg, thrown);
+                checkLogEvent(provider, desc, expected, expected.isLoggable);
+            }
+        }
+
+        System.out.println("\tlogger.log(messageLevel, thrown, supplier)");
+        for (java.util.logging.Level loggerLevel : julLevels) {
+            setLevel(sink, loggerLevel);
+            for (java.util.logging.Level messageLevel :julLevels) {
+                String desc = "logger.log(messageLevel, thrown, supplier): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                LogEvent expected =
+                        LogEvent.of(
+                            sequencer.get(),
+                            loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
+                            name, messageLevel, null,
+                            supplier.get(), thrown, (Object[])null);
+                logger.log(toPlatformLevel(messageLevel), thrown, supplier);
+                checkLogEvent(provider, desc, expected, expected.isLoggable);
+            }
+        }
+
+        String sourceClass = "blah.Blah";
+        String sourceMethod = "blih";
+        System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, fooMsg)");
+        for (java.util.logging.Level loggerLevel : julLevels) {
+            setLevel(sink, loggerLevel);
+            for (java.util.logging.Level messageLevel :julLevels) {
+                String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, fooMsg): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                LogEvent expected =
+                        LogEvent.of(
+                            sequencer.get(),
+                            loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
+                            name, sourceClass, sourceMethod, messageLevel, loggerBundle,
+                            fooMsg, (Throwable)null, (Object[])null);
+                logger.logp(toPlatformLevel(messageLevel), sourceClass, sourceMethod, fooMsg);
+                checkLogEvent(provider, desc, expected, expected.isLoggable);
+            }
+        }
+
+        System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, supplier)");
+        for (java.util.logging.Level loggerLevel : julLevels) {
+            setLevel(sink, loggerLevel);
+            for (java.util.logging.Level messageLevel :julLevels) {
+                String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, supplier): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                LogEvent expected =
+                        LogEvent.of(
+                            sequencer.get(),
+                            loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
+                            name, sourceClass, sourceMethod, messageLevel, null,
+                            supplier.get(), (Throwable)null, (Object[])null);
+                logger.logp(toPlatformLevel(messageLevel), sourceClass, sourceMethod, supplier);
+                checkLogEvent(provider, desc, expected, expected.isLoggable);
+            }
+        }
+
+        System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, format, arg1, arg2)");
+        for (java.util.logging.Level loggerLevel : julLevels) {
+            setLevel(sink, loggerLevel);
+            for (java.util.logging.Level messageLevel :julLevels) {
+                String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, format, arg1, arg2): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                LogEvent expected =
+                        LogEvent.of(
+                            sequencer.get(),
+                            loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
+                            name, sourceClass, sourceMethod, messageLevel, loggerBundle,
+                            format, (Throwable)null, arg1, arg2);
+                logger.logp(toPlatformLevel(messageLevel), sourceClass, sourceMethod, format, arg1, arg2);
+                checkLogEvent(provider, desc, expected, expected.isLoggable);
+            }
+        }
+
+        System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, fooMsg, thrown)");
+        for (java.util.logging.Level loggerLevel : julLevels) {
+            setLevel(sink, loggerLevel);
+            for (java.util.logging.Level messageLevel :julLevels) {
+                String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, fooMsg, thrown): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                LogEvent expected =
+                        LogEvent.of(
+                            sequencer.get(),
+                            loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
+                            name, sourceClass, sourceMethod, messageLevel, loggerBundle,
+                            fooMsg, thrown, (Object[])null);
+                logger.logp(toPlatformLevel(messageLevel), sourceClass, sourceMethod, fooMsg, thrown);
+                checkLogEvent(provider, desc, expected, expected.isLoggable);
+            }
+        }
+
+        System.out.println("\tlogger.logp(messageLevel, sourceClass, sourceMethod, thrown, supplier)");
+        for (java.util.logging.Level loggerLevel : julLevels) {
+            setLevel(sink, loggerLevel);
+            for (java.util.logging.Level messageLevel :julLevels) {
+                String desc = "logger.logp(messageLevel, sourceClass, sourceMethod, thrown, supplier): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                LogEvent expected =
+                        LogEvent.of(
+                            sequencer.get(),
+                            loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
+                            name, sourceClass, sourceMethod, messageLevel, null,
+                            supplier.get(), thrown, (Object[])null);
+                logger.logp(toPlatformLevel(messageLevel), sourceClass, sourceMethod, thrown, supplier);
+                checkLogEvent(provider, desc, expected, expected.isLoggable);
+            }
+        }
+
+        ResourceBundle bundle = ResourceBundle.getBundle(MyBundle.class.getName());
+        System.out.println("\tlogger.logrb(messageLevel, bundle, format, arg1, arg2)");
+        for (java.util.logging.Level loggerLevel : julLevels) {
+            setLevel(sink, loggerLevel);
+            for (java.util.logging.Level messageLevel :julLevels) {
+                String desc = "logger.logrb(messageLevel, bundle, format, arg1, arg2): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                LogEvent expected =
+                        LogEvent.of(
+                            sequencer.get(),
+                            loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
+                            name, messageLevel, bundle,
+                            format, (Throwable)null, arg1, arg2);
+                logger.logrb(toPlatformLevel(messageLevel), bundle, format, arg1, arg2);
+                checkLogEvent(provider, desc, expected, expected.isLoggable);
+            }
+        }
+
+        System.out.println("\tlogger.logrb(messageLevel, bundle, msg, thrown)");
+        for (java.util.logging.Level loggerLevel : julLevels) {
+            setLevel(sink, loggerLevel);
+            for (java.util.logging.Level messageLevel :julLevels) {
+                String desc = "logger.logrb(messageLevel, bundle, msg, thrown): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                LogEvent expected =
+                        LogEvent.of(
+                            sequencer.get(),
+                            loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
+                            name, messageLevel, bundle,
+                            fooMsg, thrown, (Object[])null);
+                logger.logrb(toPlatformLevel(messageLevel), bundle, fooMsg, thrown);
+                checkLogEvent(provider, desc, expected, expected.isLoggable);
+            }
+        }
+
+        System.out.println("\tlogger.logrb(messageLevel, sourceClass, sourceMethod, bundle, format, arg1, arg2)");
+        for (java.util.logging.Level loggerLevel : julLevels) {
+            setLevel(sink, loggerLevel);
+            for (java.util.logging.Level messageLevel :julLevels) {
+                String desc = "logger.logrb(messageLevel, sourceClass, sourceMethod, bundle, format, arg1, arg2): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                LogEvent expected =
+                        LogEvent.of(
+                            sequencer.get(),
+                            loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
+                            name, sourceClass, sourceMethod, messageLevel, bundle,
+                            format, (Throwable)null, arg1, arg2);
+                logger.logrb(toPlatformLevel(messageLevel), sourceClass, sourceMethod, bundle, format, arg1, arg2);
+                checkLogEvent(provider, desc, expected, expected.isLoggable);
+            }
+        }
+
+        System.out.println("\tlogger.logrb(messageLevel, sourceClass, sourceMethod, bundle, msg, thrown)");
+        for (java.util.logging.Level loggerLevel : julLevels) {
+            setLevel(sink, loggerLevel);
+            for (java.util.logging.Level messageLevel :julLevels) {
+                String desc = "logger.logrb(messageLevel, sourceClass, sourceMethod, bundle, msg, thrown): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                LogEvent expected =
+                        LogEvent.of(
+                            sequencer.get(),
+                            loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
+                            name, sourceClass, sourceMethod, messageLevel, bundle,
+                            fooMsg, thrown, (Object[])null);
+                logger.logrb(toPlatformLevel(messageLevel), sourceClass, sourceMethod, bundle, fooMsg, thrown);
+                checkLogEvent(provider, desc, expected, expected.isLoggable);
+            }
+        }
+    }
+
+    final static class PermissionsBuilder {
+        final Permissions perms;
+        public PermissionsBuilder() {
+            this(new Permissions());
+        }
+        public PermissionsBuilder(Permissions perms) {
+            this.perms = perms;
+        }
+        public PermissionsBuilder add(Permission p) {
+            perms.add(p);
+            return this;
+        }
+        public PermissionsBuilder addAll(PermissionCollection col) {
+            if (col != null) {
+                for (Enumeration<Permission> e = col.elements(); e.hasMoreElements(); ) {
+                    perms.add(e.nextElement());
+                }
+            }
+            return this;
+        }
+        public Permissions toPermissions() {
+            final PermissionsBuilder builder = new PermissionsBuilder();
+            builder.addAll(perms);
+            return builder.perms;
+        }
+    }
+
+    public static class SimplePolicy extends Policy {
+        public static final RuntimePermission LOGGERFINDER_PERMISSION =
+                new RuntimePermission("loggerFinder");
+        final static RuntimePermission ACCESS_LOGGER = new RuntimePermission("accessClassInPackage.jdk.internal.logger");
+        final static RuntimePermission ACCESS_LOGGING = new RuntimePermission("accessClassInPackage.sun.util.logging");
+
+        final Permissions permissions;
+        final Permissions allPermissions;
+        final ThreadLocal<AtomicBoolean> allowControl;
+        final ThreadLocal<AtomicBoolean> allowAccess;
+        final ThreadLocal<AtomicBoolean> allowAll;
+        public SimplePolicy(ThreadLocal<AtomicBoolean> allowControl,
+                ThreadLocal<AtomicBoolean> allowAccess,
+                ThreadLocal<AtomicBoolean> allowAll) {
+            this.allowControl = allowControl;
+            this.allowAccess = allowAccess;
+            this.allowAll = allowAll;
+            permissions = new Permissions();
+            allPermissions = new PermissionsBuilder()
+                    .add(new java.security.AllPermission())
+                    .toPermissions();
+        }
+
+        Permissions getPermissions() {
+            if (allowControl.get().get() || allowAccess.get().get() || allowAll.get().get()) {
+                PermissionsBuilder builder =  new PermissionsBuilder()
+                        .addAll(permissions);
+                if (allowControl.get().get()) {
+                    builder.add(LOGGERFINDER_PERMISSION);
+                }
+                if (allowAccess.get().get()) {
+                    builder.add(ACCESS_LOGGER);
+                    builder.add(ACCESS_LOGGING);
+                }
+                if (allowAll.get().get()) {
+                    builder.addAll(allPermissions);
+                }
+                return builder.toPermissions();
+            }
+            return permissions;
+        }
+
+        @Override
+        public boolean implies(ProtectionDomain domain, Permission permission) {
+            return getPermissions().implies(permission);
+        }
+
+        @Override
+        public PermissionCollection getPermissions(CodeSource codesource) {
+            return new PermissionsBuilder().addAll(getPermissions()).toPermissions();
+        }
+
+        @Override
+        public PermissionCollection getPermissions(ProtectionDomain domain) {
+            return new PermissionsBuilder().addAll(getPermissions()).toPermissions();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/System/LoggerFinder/jdk/DefaultPlatformLoggerTest/DefaultPlatformLoggerTest.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,544 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+import java.security.CodeSource;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.Permissions;
+import java.security.Policy;
+import java.security.ProtectionDomain;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Queue;
+import java.util.ResourceBundle;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.logging.Handler;
+import java.util.logging.LogManager;
+import java.util.logging.LogRecord;
+import java.lang.System.LoggerFinder;
+import sun.util.logging.PlatformLogger;
+import sun.util.logging.internal.LoggingProviderImpl;
+
+/**
+ * @test
+ * @bug     8140364
+ * @summary Tests all PlatformLogger methods with the default LoggerFinder JUL backend.
+ * @modules java.base/sun.util.logging java.logging/sun.util.logging.internal
+ * @run  main/othervm DefaultPlatformLoggerTest
+ * @author danielfuchs
+ */
+public class DefaultPlatformLoggerTest {
+
+    final static AtomicLong sequencer = new AtomicLong();
+    final static boolean VERBOSE = false;
+    static final ThreadLocal<AtomicBoolean> allowControl = new ThreadLocal<AtomicBoolean>() {
+        @Override
+        protected AtomicBoolean initialValue() {
+            return  new AtomicBoolean(false);
+        }
+    };
+    static final ThreadLocal<AtomicBoolean> allowAll = new ThreadLocal<AtomicBoolean>() {
+        @Override
+        protected AtomicBoolean initialValue() {
+            return  new AtomicBoolean(false);
+        }
+    };
+
+    public static final Queue<LogEvent> eventQueue = new ArrayBlockingQueue<>(128);
+
+    public static final class LogEvent implements Cloneable {
+
+        public LogEvent() {
+            this(sequencer.getAndIncrement());
+        }
+
+        LogEvent(long sequenceNumber) {
+            this.sequenceNumber = sequenceNumber;
+        }
+
+        long sequenceNumber;
+        boolean isLoggable;
+        String loggerName;
+        java.util.logging.Level level;
+        ResourceBundle bundle;
+        Throwable thrown;
+        Object[] args;
+        String msg;
+        String className;
+        String methodName;
+
+        Object[] toArray() {
+            return new Object[] {
+                sequenceNumber,
+                isLoggable,
+                loggerName,
+                level,
+                bundle,
+                thrown,
+                args,
+                msg,
+                className,
+                methodName,
+            };
+        }
+
+        @Override
+        public String toString() {
+            return Arrays.deepToString(toArray());
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            return obj instanceof LogEvent
+                    && Objects.deepEquals(this.toArray(), ((LogEvent)obj).toArray());
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(toArray());
+        }
+
+        public LogEvent cloneWith(long sequenceNumber)
+                throws CloneNotSupportedException {
+            LogEvent cloned = (LogEvent)super.clone();
+            cloned.sequenceNumber = sequenceNumber;
+            return cloned;
+        }
+
+        public static LogEvent of(long sequenceNumber,
+                boolean isLoggable, String name,
+                java.util.logging.Level level, ResourceBundle bundle,
+                String key, Throwable thrown, Object... params) {
+            return LogEvent.of(sequenceNumber, isLoggable, name,
+                    DefaultPlatformLoggerTest.class.getName(),
+                    "testLogger", level, bundle, key,
+                    thrown, params);
+        }
+        public static LogEvent of(long sequenceNumber,
+                boolean isLoggable, String name,
+                String className, String methodName,
+                java.util.logging.Level level, ResourceBundle bundle,
+                String key, Throwable thrown, Object... params) {
+            LogEvent evt = new LogEvent(sequenceNumber);
+            evt.loggerName = name;
+            evt.level = level;
+            evt.args = params;
+            evt.bundle = bundle;
+            evt.thrown = thrown;
+            evt.msg = key;
+            evt.isLoggable = isLoggable;
+            evt.className = className;
+            evt.methodName = methodName;
+            return evt;
+        }
+
+    }
+
+    static final java.util.logging.Level[] julLevels = {
+        java.util.logging.Level.ALL,
+        new java.util.logging.Level("FINER_THAN_FINEST", java.util.logging.Level.FINEST.intValue() - 10) {},
+        java.util.logging.Level.FINEST,
+        new java.util.logging.Level("FINER_THAN_FINER", java.util.logging.Level.FINER.intValue() - 10) {},
+        java.util.logging.Level.FINER,
+        new java.util.logging.Level("FINER_THAN_FINE", java.util.logging.Level.FINE.intValue() - 10) {},
+        java.util.logging.Level.FINE,
+        new java.util.logging.Level("FINER_THAN_CONFIG", java.util.logging.Level.FINE.intValue() + 10) {},
+        java.util.logging.Level.CONFIG,
+        new java.util.logging.Level("FINER_THAN_INFO", java.util.logging.Level.INFO.intValue() - 10) {},
+        java.util.logging.Level.INFO,
+        new java.util.logging.Level("FINER_THAN_WARNING", java.util.logging.Level.INFO.intValue() + 10) {},
+        java.util.logging.Level.WARNING,
+        new java.util.logging.Level("FINER_THAN_SEVERE", java.util.logging.Level.SEVERE.intValue() - 10) {},
+        java.util.logging.Level.SEVERE,
+        new java.util.logging.Level("FATAL", java.util.logging.Level.SEVERE.intValue() + 10) {},
+        java.util.logging.Level.OFF,
+    };
+
+    static final java.util.logging.Level[] julPlatformLevels = {
+        java.util.logging.Level.FINEST,
+        java.util.logging.Level.FINER,
+        java.util.logging.Level.FINE,
+        java.util.logging.Level.CONFIG,
+        java.util.logging.Level.INFO,
+        java.util.logging.Level.WARNING,
+        java.util.logging.Level.SEVERE,
+    };
+
+
+    public static class MyBundle extends ResourceBundle {
+
+        final ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();
+
+        @Override
+        protected Object handleGetObject(String key) {
+            if (key.contains(" (translated)")) {
+                throw new RuntimeException("Unexpected key: " + key);
+            }
+            return map.computeIfAbsent(key, k -> k + " (translated)");
+        }
+
+        @Override
+        public Enumeration<String> getKeys() {
+            return Collections.enumeration(map.keySet());
+        }
+
+    }
+
+    public static class MyHandler extends Handler {
+
+        @Override
+        public java.util.logging.Level getLevel() {
+            return java.util.logging.Level.ALL;
+        }
+
+        @Override
+        public void publish(LogRecord record) {
+            eventQueue.add(LogEvent.of(sequencer.getAndIncrement(),
+                    true, record.getLoggerName(),
+                    record.getSourceClassName(),
+                    record.getSourceMethodName(),
+                    record.getLevel(),
+                    record.getResourceBundle(), record.getMessage(),
+                    record.getThrown(), record.getParameters()));
+        }
+        @Override
+        public void flush() {
+        }
+        @Override
+        public void close() throws SecurityException {
+        }
+
+    }
+
+    public static class MyLoggerBundle extends MyBundle {
+
+    }
+
+    public static void main(String[] args) throws Exception {
+        LoggerFinder provider = LoggerFinder.getLoggerFinder();
+        java.util.logging.Logger appSink = LoggingProviderImpl.getLogManagerAccess()
+                .demandLoggerFor(LogManager.getLogManager(), "foo",
+                        DefaultPlatformLoggerTest.class);
+        java.util.logging.Logger sysSink = LoggingProviderImpl.getLogManagerAccess()
+                .demandLoggerFor(LogManager.getLogManager(),"foo", Thread.class);
+        appSink.addHandler(new MyHandler());
+        sysSink.addHandler(new MyHandler());
+        appSink.setUseParentHandlers(VERBOSE);
+        sysSink.setUseParentHandlers(VERBOSE);
+
+        System.out.println("\n*** Without Security Manager\n");
+        test(provider, true, appSink, sysSink);
+        System.out.println("Tetscase count: " + sequencer.get());
+
+        Policy.setPolicy(new SimplePolicy(allowAll, allowControl));
+        System.setSecurityManager(new SecurityManager());
+
+        System.out.println("\n*** With Security Manager, without permissions\n");
+        test(provider, false, appSink, sysSink);
+        System.out.println("Tetscase count: " + sequencer.get());
+
+        System.out.println("\n*** With Security Manager, with control permission\n");
+        allowControl.get().set(true);
+        test(provider, true, appSink, sysSink);
+
+        System.out.println("\nPASSED: Tested " + sequencer.get() + " cases.");
+    }
+
+    public static void test(LoggerFinder provider, boolean hasRequiredPermissions,
+            java.util.logging.Logger appSink, java.util.logging.Logger sysSink) throws Exception {
+
+        // No way to giva a resource bundle to a platform logger.
+        // ResourceBundle loggerBundle = ResourceBundle.getBundle(MyLoggerBundle.class.getName());
+        final Map<PlatformLogger, String> loggerDescMap = new HashMap<>();
+
+        PlatformLogger platform = PlatformLogger.getLogger("foo");
+        loggerDescMap.put(platform, "PlatformLogger.getLogger(\"foo\")");
+
+        testLogger(provider, loggerDescMap, "foo", null, platform, sysSink);
+    }
+
+    public static class Foo {
+
+    }
+
+    static void verbose(String msg) {
+       if (VERBOSE) {
+           System.out.println(msg);
+       }
+    }
+
+    static void checkLogEvent(LoggerFinder provider, String desc,
+            LogEvent expected) {
+        LogEvent actual =  eventQueue.poll();
+        if (!expected.equals(actual)) {
+            throw new RuntimeException("mismatch for " + desc
+                    + "\n\texpected=" + expected
+                    + "\n\t  actual=" + actual);
+        } else {
+            verbose("Got expected results for "
+                    + desc + "\n\t" + expected);
+        }
+    }
+
+    static void checkLogEvent(LoggerFinder provider, String desc,
+            LogEvent expected, boolean expectNotNull) {
+        LogEvent actual =  eventQueue.poll();
+        if (actual == null && !expectNotNull) return;
+        if (actual != null && !expectNotNull) {
+            throw new RuntimeException("Unexpected log event found for " + desc
+                + "\n\tgot: " + actual);
+        }
+        if (!expected.equals(actual)) {
+            throw new RuntimeException("mismatch for " + desc
+                    + "\n\texpected=" + expected
+                    + "\n\t  actual=" + actual);
+        } else {
+            verbose("Got expected results for "
+                    + desc + "\n\t" + expected);
+        }
+    }
+
+    static void setLevel(java.util.logging.Logger sink, java.util.logging.Level loggerLevel) {
+        boolean before = allowAll.get().get();
+        try {
+            allowAll.get().set(true);
+            sink.setLevel(loggerLevel);
+        } finally {
+            allowAll.get().set(before);
+        }
+    }
+
+    // Calls the methods defined on LogProducer and verify the
+    // parameters received by the underlying logger.
+    private static void testLogger(LoggerFinder provider,
+            Map<PlatformLogger, String> loggerDescMap,
+            String name,
+            ResourceBundle loggerBundle,
+            PlatformLogger logger,
+            java.util.logging.Logger sink) throws Exception {
+
+        System.out.println("Testing " + loggerDescMap.get(logger));
+        final java.util.logging.Level OFF = java.util.logging.Level.OFF;
+
+        Foo foo = new Foo();
+        String fooMsg = foo.toString();
+        System.out.println("\tlogger.<level>(fooMsg)");
+        for (java.util.logging.Level loggerLevel : julLevels) {
+            setLevel(sink, loggerLevel);
+            for (java.util.logging.Level messageLevel : julPlatformLevels) {
+                LogEvent expected =
+                        LogEvent.of(
+                            sequencer.get(),
+                            loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
+                            name, messageLevel, loggerBundle,
+                            fooMsg, (Throwable)null, (Object[])null);
+                String desc2 = "logger." + messageLevel.toString().toLowerCase()
+                        + "(fooMsg): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                if (messageLevel == java.util.logging.Level.FINEST) {
+                    logger.finest(fooMsg);
+                    checkLogEvent(provider, desc2, expected, expected.isLoggable);
+                } else if (messageLevel == java.util.logging.Level.FINER) {
+                    logger.finer(fooMsg);
+                    checkLogEvent(provider, desc2, expected, expected.isLoggable);
+                } else if (messageLevel == java.util.logging.Level.FINE) {
+                    logger.fine(fooMsg);
+                    checkLogEvent(provider, desc2, expected, expected.isLoggable);
+                } else if (messageLevel == java.util.logging.Level.CONFIG) {
+                    logger.config(fooMsg);
+                    checkLogEvent(provider, desc2, expected, expected.isLoggable);
+                } else if (messageLevel == java.util.logging.Level.INFO) {
+                    logger.info(fooMsg);
+                    checkLogEvent(provider, desc2, expected, expected.isLoggable);
+                } else if (messageLevel == java.util.logging.Level.WARNING) {
+                    logger.warning(fooMsg);
+                    checkLogEvent(provider, desc2, expected, expected.isLoggable);
+                } else if (messageLevel == java.util.logging.Level.SEVERE) {
+                    logger.severe(fooMsg);
+                    checkLogEvent(provider, desc2, expected, expected.isLoggable);
+                }
+            }
+        }
+
+        Throwable thrown = new Exception("OK: log me!");
+        System.out.println("\tlogger.<level>(msg, thrown)");
+        for (java.util.logging.Level loggerLevel : julLevels) {
+            setLevel(sink, loggerLevel);
+            for (java.util.logging.Level messageLevel :julPlatformLevels) {
+                LogEvent expected =
+                        LogEvent.of(
+                            sequencer.get(),
+                            loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
+                            name, messageLevel, loggerBundle,
+                            fooMsg, thrown, (Object[])null);
+                String desc2 = "logger." + messageLevel.toString().toLowerCase()
+                        + "(msg, thrown): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                if (messageLevel == java.util.logging.Level.FINEST) {
+                    logger.finest(fooMsg, thrown);
+                    checkLogEvent(provider, desc2, expected, expected.isLoggable);
+                } else if (messageLevel == java.util.logging.Level.FINER) {
+                    logger.finer(fooMsg, thrown);
+                    checkLogEvent(provider, desc2, expected, expected.isLoggable);
+                } else if (messageLevel == java.util.logging.Level.FINE) {
+                    logger.fine(fooMsg, thrown);
+                    checkLogEvent(provider, desc2, expected, expected.isLoggable);
+                } else if (messageLevel == java.util.logging.Level.CONFIG) {
+                    logger.config(fooMsg, thrown);
+                    checkLogEvent(provider, desc2, expected, expected.isLoggable);
+                } else if (messageLevel == java.util.logging.Level.INFO) {
+                    logger.info(fooMsg, thrown);
+                    checkLogEvent(provider, desc2, expected, expected.isLoggable);
+                } else if (messageLevel == java.util.logging.Level.WARNING) {
+                    logger.warning(fooMsg, thrown);
+                    checkLogEvent(provider, desc2, expected, expected.isLoggable);
+                } else if (messageLevel == java.util.logging.Level.SEVERE) {
+                    logger.severe(fooMsg, thrown);
+                    checkLogEvent(provider, desc2, expected, expected.isLoggable);
+                }
+            }
+        }
+
+        String format = "two params [{1} {2}]";
+        Object arg1 = foo;
+        Object arg2 = fooMsg;
+        System.out.println("\tlogger.<level>(format, arg1, arg2)");
+        for (java.util.logging.Level loggerLevel : julLevels) {
+            setLevel(sink, loggerLevel);
+            for (java.util.logging.Level messageLevel : julPlatformLevels) {
+                LogEvent expected =
+                        LogEvent.of(
+                            sequencer.get(),
+                            loggerLevel != OFF && messageLevel.intValue() >= loggerLevel.intValue(),
+                            name, messageLevel, loggerBundle,
+                            format, (Throwable)null, foo, fooMsg);
+                String desc2 = "logger." + messageLevel.toString().toLowerCase()
+                        + "(format, foo, fooMsg): loggerLevel="
+                        + loggerLevel+", messageLevel="+messageLevel;
+                if (messageLevel == java.util.logging.Level.FINEST) {
+                    logger.finest(format, foo, fooMsg);
+                    checkLogEvent(provider, desc2, expected, expected.isLoggable);
+                } else if (messageLevel == java.util.logging.Level.FINER) {
+                    logger.finer(format, foo, fooMsg);
+                    checkLogEvent(provider, desc2, expected, expected.isLoggable);
+                } else if (messageLevel == java.util.logging.Level.FINE) {
+                    logger.fine(format, foo, fooMsg);
+                    checkLogEvent(provider, desc2, expected, expected.isLoggable);
+                } else if (messageLevel == java.util.logging.Level.CONFIG) {
+                    logger.config(format, foo, fooMsg);
+                    checkLogEvent(provider, desc2, expected, expected.isLoggable);
+                } else if (messageLevel == java.util.logging.Level.INFO) {
+                    logger.info(format, foo, fooMsg);
+                    checkLogEvent(provider, desc2, expected, expected.isLoggable);
+                } else if (messageLevel == java.util.logging.Level.WARNING) {
+                    logger.warning(format, foo, fooMsg);
+                    checkLogEvent(provider, desc2, expected, expected.isLoggable);
+                } else if (messageLevel == java.util.logging.Level.SEVERE) {
+                    logger.severe(format, foo, fooMsg);
+                    checkLogEvent(provider, desc2, expected, expected.isLoggable);
+                }
+            }
+        }
+
+    }
+
+    final static class PermissionsBuilder {
+        final Permissions perms;
+        public PermissionsBuilder() {
+            this(new Permissions());
+        }
+        public PermissionsBuilder(Permissions perms) {
+            this.perms = perms;
+        }
+        public PermissionsBuilder add(Permission p) {
+            perms.add(p);
+            return this;
+        }
+        public PermissionsBuilder addAll(PermissionCollection col) {
+            if (col != null) {
+                for (Enumeration<Permission> e = col.elements(); e.hasMoreElements(); ) {
+                    perms.add(e.nextElement());
+                }
+            }
+            return this;
+        }
+        public Permissions toPermissions() {
+            final PermissionsBuilder builder = new PermissionsBuilder();
+            builder.addAll(perms);
+            return builder.perms;
+        }
+    }
+
+    public static class SimplePolicy extends Policy {
+        public static final RuntimePermission LOGGERFINDER_PERMISSION =
+                new RuntimePermission("loggerFinder");
+
+        final Permissions permissions;
+        final Permissions withControlPermissions;
+        final Permissions allPermissions;
+        final ThreadLocal<AtomicBoolean> allowAll;
+        final ThreadLocal<AtomicBoolean> allowControl;
+        public SimplePolicy(ThreadLocal<AtomicBoolean> allowAll,
+                ThreadLocal<AtomicBoolean> allowControl) {
+            this.allowAll = allowAll;
+            this.allowControl = allowControl;
+            permissions = new Permissions();
+
+            withControlPermissions = new Permissions();
+            withControlPermissions.add(LOGGERFINDER_PERMISSION);
+
+            // these are used for configuring the test itself...
+            allPermissions = new Permissions();
+            allPermissions.add(new java.security.AllPermission());
+        }
+
+        @Override
+        public boolean implies(ProtectionDomain domain, Permission permission) {
+            if (allowAll.get().get()) return allPermissions.implies(permission);
+            if (allowControl.get().get()) return withControlPermissions.implies(permission);
+            return permissions.implies(permission);
+        }
+
+        @Override
+        public PermissionCollection getPermissions(CodeSource codesource) {
+            return new PermissionsBuilder().addAll(
+                    allowAll.get().get() ? allPermissions :
+                    allowControl.get().get()
+                    ? withControlPermissions : permissions).toPermissions();
+        }
+
+        @Override
+        public PermissionCollection getPermissions(ProtectionDomain domain) {
+            return new PermissionsBuilder().addAll(
+                    allowAll.get().get() ? allPermissions :
+                    allowControl.get().get()
+                    ? withControlPermissions : permissions).toPermissions();
+        }
+    }
+}
--- a/jdk/test/java/lang/invoke/AccessControlTest.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/test/java/lang/invoke/AccessControlTest.java	Mon Nov 23 14:37:04 2015 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -225,6 +225,31 @@
                 System.out.println(this+" willAccess "+lc+" m1="+m1+" m2="+m2+" => "+((m2 & m1) != 0));
             return (m2 & m1) != 0;
         }
+
+        /** Predict the success or failure of accessing this class. */
+        public boolean willAccessClass(Class<?> c2, boolean load) {
+            Class<?> c1 = lookupClass();
+            if (load && c1.getClassLoader() == null) {
+                return false;
+            }
+            LookupCase lc = this.in(c2);
+            int m1 = lc.lookupModes();
+            boolean r = false;
+            if (m1 == 0) {
+                r = false;
+            } else {
+                int m2 = fixMods(c2.getModifiers());
+                if ((m2 & PUBLIC) != 0) {
+                    r = true;
+                } else if ((m1 & PACKAGE) != 0 && c1.getPackage() == c2.getPackage()) {
+                    r = true;
+                }
+            }
+            if (verbosity >= 2) {
+                System.out.println(this+" willAccessClass "+lc+" c1="+c1+" c2="+c2+" => "+r);
+            }
+            return r;
+        }
     }
 
     private static Class<?> topLevelClass(Class<?> cls) {
@@ -342,6 +367,8 @@
                 Method method = targetMethod(targetClass, targetAccess, methodType);
                 // Try to access target method from various contexts.
                 for (LookupCase sourceCase : CASES) {
+                    testOneAccess(sourceCase, method, "findClass");
+                    testOneAccess(sourceCase, method, "accessClass");
                     testOneAccess(sourceCase, method, "find");
                     testOneAccess(sourceCase, method, "unreflect");
                 }
@@ -356,11 +383,19 @@
         Class<?> targetClass = method.getDeclaringClass();
         String methodName = method.getName();
         MethodType methodType = methodType(method.getReturnType(), method.getParameterTypes());
-        boolean willAccess = sourceCase.willAccess(method);
+        boolean isFindOrAccessClass = "findClass".equals(kind) || "accessClass".equals(kind);
+        boolean willAccess = isFindOrAccessClass ?
+                sourceCase.willAccessClass(targetClass, "findClass".equals(kind)) : sourceCase.willAccess(method);
         boolean didAccess = false;
         ReflectiveOperationException accessError = null;
         try {
             switch (kind) {
+            case "accessClass":
+                sourceCase.lookup().accessClass(targetClass);
+                break;
+            case "findClass":
+                sourceCase.lookup().findClass(targetClass.getName());
+                break;
             case "find":
                 if ((method.getModifiers() & Modifier.STATIC) != 0)
                     sourceCase.lookup().findStatic(targetClass, methodName, methodType);
@@ -378,8 +413,8 @@
             accessError = ex;
         }
         if (willAccess != didAccess) {
-            System.out.println(sourceCase+" => "+targetClass.getSimpleName()+"."+methodName+methodType);
-            System.out.println("fail on "+method+" ex="+accessError);
+            System.out.println(sourceCase+" => "+targetClass.getSimpleName()+(isFindOrAccessClass?"":"."+methodName+methodType));
+            System.out.println("fail "+(isFindOrAccessClass?kind:"on "+method)+" ex="+accessError);
             assertEquals(willAccess, didAccess);
         }
         testCount++;
--- a/jdk/test/java/lang/invoke/BigArityTest.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/test/java/lang/invoke/BigArityTest.java	Mon Nov 23 14:37:04 2015 -0500
@@ -58,6 +58,8 @@
         return x == null ? dflt : x;
     }
 
+    static final MethodType MT_A = MethodType.methodType(Object.class, Object.class, Object[].class, Object.class);
+
     static Object hashArguments(Object... args) {
         return Objects.hash(args);
     }
@@ -108,9 +110,36 @@
             }
         }
         // Sizes not in the above array are good:
-        target.asCollector(Object[].class, minbig-1);
+        target.asCollector(Object[].class, minbig - 1);
         for (int i = 2; i <= 10; i++)
-            target.asCollector(Object[].class, minbig-i);
+            target.asCollector(Object[].class, minbig - i);
+    }
+
+    static void asciae02target(Object[] a, Object b) {
+        // naught
+    }
+
+    @Test
+    public void asCollectorIAE02() throws ReflectiveOperationException {
+        final int[] INVALID_ARRAY_LENGTHS = {
+            Integer.MIN_VALUE, Integer.MIN_VALUE + 1, -2, -1, 254, 255, Integer.MAX_VALUE - 1, Integer.MAX_VALUE
+        };
+        MethodHandle target = MethodHandles.lookup().findStatic(BigArityTest.class, "asciae02target",
+                MethodType.methodType(void.class, Object[].class, Object.class));
+        int minbig = Integer.MAX_VALUE;
+        for (int invalidLength : INVALID_ARRAY_LENGTHS) {
+            if (minbig > invalidLength && invalidLength > 100) minbig = invalidLength;
+            try {
+                target.asCollector(0, Object[].class, invalidLength);
+                assert(false) : invalidLength;
+            } catch (IllegalArgumentException ex) {
+                System.out.println("OK: "+ex);
+            }
+        }
+        // Sizes not in the above array are good:
+        for (int i = 1; i <= 10; ++i) {
+            target.asCollector(0, Object[].class, minbig - i);
+        }
     }
 
     @Test
@@ -216,51 +245,86 @@
             Class<? extends Object[]> cls = (Class<? extends Object[]>) cls0;
             //Class<? extends Object[]> cls = Object[].class.asSubclass(cls0);
             int nargs = args.length, skip;
+            Object hr;
             MethodHandle smh = mh.asSpreader(cls, nargs - (skip = 0));
+            MethodHandle hsmh = mh.asSpreader(0, cls, nargs - skip);
             Object[] tail = Arrays.copyOfRange(args, skip, nargs, cls);
-            if (cls == Object[].class)
+            Object[] head = Arrays.copyOfRange(args, 0, nargs - skip, cls);
+            if (cls == Object[].class) {
                 r = smh.invokeExact(tail);
-            else if (cls == Integer[].class)
+                hr = hsmh.invokeExact(head);
+            } else if (cls == Integer[].class) {
                 r = smh.invokeExact((Integer[]) tail); //warning OK, see 8019340
-            else
+                hr = hsmh.invokeExact((Integer[]) head);
+            } else {
                 r = smh.invoke(tail);
+                hr = hsmh.invoke(head);
+            }
             assertEquals(r0, r);
+            assertEquals(r0, hr);
             smh = mh.asSpreader(cls, nargs - (skip = 1));
+            hsmh = mh.asSpreader(0, cls, nargs - skip);
             tail = Arrays.copyOfRange(args, skip, nargs, cls);
-            if (cls == Object[].class)
+            head = Arrays.copyOfRange(args, 0, nargs - skip, cls);
+            if (cls == Object[].class) {
                 r = smh.invokeExact(args[0], tail);
-            else if (cls == Integer[].class)
+                hr = hsmh.invokeExact(head, args[2]);
+            } else if (cls == Integer[].class) {
                 r = smh.invokeExact(args[0], (Integer[]) tail);
-            else
+                hr = hsmh.invokeExact((Integer[]) head, args[2]);
+            } else {
                 r = smh.invoke(args[0], tail);
+                hr = hsmh.invoke(head, args[2]);
+            }
             assertEquals(r0, r);
+            assertEquals(r0, hr);
             smh = mh.asSpreader(cls, nargs - (skip = 2));
+            hsmh = mh.asSpreader(0, cls, nargs - skip);
             tail = Arrays.copyOfRange(args, skip, nargs, cls);
-            if (cls == Object[].class)
+            head = Arrays.copyOfRange(args, 0, nargs - skip, cls);
+            if (cls == Object[].class) {
                 r = smh.invokeExact(args[0], args[1], tail);
-            else if (cls == Integer[].class)
+                hr = hsmh.invokeExact(head, args[1], args[2]);
+            } else if (cls == Integer[].class) {
                 r = smh.invokeExact(args[0], args[1], (Integer[]) tail);
-            else
+                hr = hsmh.invokeExact((Integer[]) head, args[1], args[2]);
+            } else {
                 r = smh.invoke(args[0], args[1], tail);
+                hr = hsmh.invoke(head, args[1], args[2]);
+            }
             assertEquals(r0, r);
+            assertEquals(r0, hr);
             smh = mh.asSpreader(cls, nargs - (skip = 3));
+            hsmh = mh.asSpreader(0, cls, nargs - skip);
             tail = Arrays.copyOfRange(args, skip, nargs, cls);
-            if (cls == Object[].class)
+            head = Arrays.copyOfRange(args, 0, nargs - skip, cls);
+            if (cls == Object[].class) {
                 r = smh.invokeExact(args[0], args[1], args[2], tail);
-            else if (cls == Integer[].class)
+                hr = hsmh.invokeExact(head, args[0], args[1], args[2]);
+            } else if (cls == Integer[].class) {
                 r = smh.invokeExact(args[0], args[1], args[2], (Integer[]) tail);
-            else
+                hr = hsmh.invokeExact((Integer[]) head, args[0], args[1], args[2]);
+            } else {
                 r = smh.invoke(args[0], args[1], args[2], tail);
+                hr = hsmh.invoke(head, args[0], args[1], args[2]);
+            }
             assertEquals(r0, r);
+            assertEquals(r0, hr);
             // Try null array in addition to zero-length array:
             tail = null;
-            if (cls == Object[].class)
+            head = null;
+            if (cls == Object[].class) {
                 r = smh.invokeExact(args[0], args[1], args[2], tail);
-            else if (cls == Integer[].class)
+                hr = hsmh.invokeExact(head, args[0], args[1], args[2]);
+            } else if (cls == Integer[].class) {
                 r = smh.invokeExact(args[0], args[1], args[2], (Integer[]) tail);
-            else
+                hr = hsmh.invokeExact((Integer[]) head, args[0], args[1], args[2]);
+            } else {
                 r = smh.invoke(args[0], args[1], args[2], tail);
+                hr = hsmh.invoke(head, args[0], args[1], args[2]);
+            }
             assertEquals(r0, r);
+            assertEquals(r0, hr);
         }
     }
 
@@ -292,7 +356,7 @@
     @Test
     public void testArities() throws Throwable {
         System.out.println("testing spreaders and collectors on high arities...");
-            int iterations = ITERATION_COUNT;
+        int iterations = ITERATION_COUNT;
         testArities(Object[].class, MIN_ARITY-10, MIN_ARITY-1, iterations / 1000);
         testArities(Object[].class, MIN_ARITY, SLOW_ARITY-1, iterations);
         testArities(Object[].class, SLOW_ARITY, MAX_ARITY, iterations / 1000);
@@ -307,8 +371,13 @@
             Class<? extends Object[]> cls = (Class<? extends Object[]>) cls0;
             System.out.println("array class: "+cls.getSimpleName());
             int iterations = ITERATION_COUNT / 1000;
-            testArities(cls, MIN_ARITY, SLOW_ARITY-1, iterations);
-            testArities(cls, SLOW_ARITY, MAX_ARITY, iterations / 100);
+            try {
+                testArities(cls, MIN_ARITY, SLOW_ARITY - 1, iterations);
+                testArities(cls, SLOW_ARITY, MAX_ARITY, iterations / 100);
+            } catch (Throwable t) {
+                t.printStackTrace();
+                throw t;
+            }
         }
     }
 
@@ -321,11 +390,14 @@
             if (verbose)  System.out.println("arity="+arity);
             MethodHandle mh = MH_hashArguments(cls, arity);
             MethodHandle mh_VA = mh.asSpreader(cls, arity);
+            MethodHandle mh_VA_h = mh.asSpreader(0, cls, arity-1);
             assert(mh_VA.type().parameterType(0) == cls);
-            testArities(cls, arity, iterations, verbose, mh, mh_VA);
+            assert(mh_VA_h.type().parameterType(0) == cls);
+            testArities(cls, arity, iterations, verbose, mh, mh_VA, mh_VA_h);
             // mh_CA will collect arguments of a particular type and pass them to mh_VA
             MethodHandle mh_CA = mh_VA.asCollector(cls, arity);
             MethodHandle mh_VA2 = mh_CA.asSpreader(cls, arity);
+            MethodHandle mh_VA2_h = mh_CA.asSpreader(0, cls, arity-1);
             assert(mh_CA.type().equals(mh.type()));
             assert(mh_VA2.type().equals(mh_VA.type()));
             if (cls != Object[].class) {
@@ -336,7 +408,7 @@
                 }
             }
             int iterations_VA = iterations / 100;
-            testArities(cls, arity, iterations_VA, false, mh_CA, mh_VA2);
+            testArities(cls, arity, iterations_VA, false, mh_CA, mh_VA2, mh_VA2_h);
         }
     }
 
@@ -357,13 +429,16 @@
      * @param verbose are we printing extra output?
      * @param mh      a fixed-arity version of {@code hashArguments}
      * @param mh_VA   a variable-arity version of {@code hashArguments}, accepting the given array type {@code cls}
+     * @param mh_VA_h a version of {@code hashArguments} that has a leading {@code cls} array and one final {@code cls}
+    *                 argument
      */
     private void testArities(Class<? extends Object[]> cls,
                              int arity,
                              int iterations,
                              boolean verbose,
                              MethodHandle mh,
-                             MethodHandle mh_VA
+                             MethodHandle mh_VA,
+                             MethodHandle mh_VA_h
                              ) throws Throwable {
         if (iterations < 4)  iterations = 4;
         final int MAX_MH_ARITY      = MAX_JVM_ARITY - 1;  // mh.invoke(arg*[N])
@@ -373,6 +448,7 @@
             args = Arrays.copyOf(args, arity, cls);
         Object r0 = Objects.hash(args);
         Object r;
+        Object hr;
         MethodHandle ximh = null;
         MethodHandle gimh = null;
         if (arity <= MAX_INVOKER_ARITY) {
@@ -397,13 +473,18 @@
         Object[] mh_args = cat(mh, args);
         assert(arity <= MAX_MH_ARITY);
         for (int i = 0; i < iterations; ++i) {
-            if (cls == Object[].class)
+            if (cls == Object[].class) {
                 r = mh_VA.invokeExact(args);
-            else if (cls == Integer[].class)
-                r = mh_VA.invokeExact((Integer[])args); //warning OK, see 8019340
-            else
+                hr = mh_VA_h.invokeExact(Arrays.copyOfRange(args, 0, arity - 1), args[arity - 1]);
+            } else if (cls == Integer[].class) {
+                r = mh_VA.invokeExact((Integer[]) args); //warning OK, see 8019340
+                hr = mh_VA_h.invokeExact((Integer[]) Arrays.copyOfRange(args, 0, arity - 1), (Integer) args[arity - 1]);
+            } else {
                 r = mh_VA.invoke(args);
+                hr = mh_VA_h.invoke(Arrays.copyOfRange(args, 0, arity - 1), args[arity - 1]);
+            }
             assertEquals(r0, r);
+            assertEquals(r0, hr);
             r = mh.invokeWithArguments(args);
             assertEquals(r0, r);
             if (ximh != null) {
@@ -473,6 +554,43 @@
     // </editor-fold>
     xF8, xF9, xFA, xFB);
     }
+    static Object hashArguments_252_a(Object x00, Object[] x01_FA, Object xFB) {
+        return Objects.hash(
+                // <editor-fold defaultstate="collapsed" desc="x00, x01_FA[0], x01_FA[1], x01_FA[2], ...">
+                x00, x01_FA[0], x01_FA[1], x01_FA[2], x01_FA[3], x01_FA[4], x01_FA[5], x01_FA[6], x01_FA[7], x01_FA[8],
+                x01_FA[9], x01_FA[10], x01_FA[11], x01_FA[12], x01_FA[13], x01_FA[14], x01_FA[15], x01_FA[16],
+                x01_FA[17], x01_FA[18], x01_FA[19], x01_FA[20], x01_FA[21], x01_FA[22], x01_FA[23], x01_FA[24],
+                x01_FA[25], x01_FA[26], x01_FA[27], x01_FA[28], x01_FA[29], x01_FA[30], x01_FA[31], x01_FA[32],
+                x01_FA[33], x01_FA[34], x01_FA[35], x01_FA[36], x01_FA[37], x01_FA[38], x01_FA[39], x01_FA[40],
+                x01_FA[41], x01_FA[42], x01_FA[43], x01_FA[44], x01_FA[45], x01_FA[46], x01_FA[47], x01_FA[48],
+                x01_FA[49], x01_FA[50], x01_FA[51], x01_FA[52], x01_FA[53], x01_FA[54], x01_FA[55], x01_FA[56],
+                x01_FA[57], x01_FA[58], x01_FA[59], x01_FA[60], x01_FA[61], x01_FA[62], x01_FA[63], x01_FA[64],
+                x01_FA[65], x01_FA[66], x01_FA[67], x01_FA[68], x01_FA[69], x01_FA[70], x01_FA[71], x01_FA[72],
+                x01_FA[73], x01_FA[74], x01_FA[75], x01_FA[76], x01_FA[77], x01_FA[78], x01_FA[79], x01_FA[80],
+                x01_FA[81], x01_FA[82], x01_FA[83], x01_FA[84], x01_FA[85], x01_FA[86], x01_FA[87], x01_FA[88],
+                x01_FA[89], x01_FA[90], x01_FA[91], x01_FA[92], x01_FA[93], x01_FA[94], x01_FA[95], x01_FA[96],
+                x01_FA[97], x01_FA[98], x01_FA[99], x01_FA[100], x01_FA[101], x01_FA[102], x01_FA[103], x01_FA[104],
+                x01_FA[105], x01_FA[106], x01_FA[107], x01_FA[108], x01_FA[109], x01_FA[110], x01_FA[111], x01_FA[112],
+                x01_FA[113], x01_FA[114], x01_FA[115], x01_FA[116], x01_FA[117], x01_FA[118], x01_FA[119], x01_FA[120],
+                x01_FA[121], x01_FA[122], x01_FA[123], x01_FA[124], x01_FA[125], x01_FA[126], x01_FA[127], x01_FA[128],
+                x01_FA[129], x01_FA[130], x01_FA[131], x01_FA[132], x01_FA[133], x01_FA[134], x01_FA[135], x01_FA[136],
+                x01_FA[137], x01_FA[138], x01_FA[139], x01_FA[140], x01_FA[141], x01_FA[142], x01_FA[143], x01_FA[144],
+                x01_FA[145], x01_FA[146], x01_FA[147], x01_FA[148], x01_FA[149], x01_FA[150], x01_FA[151], x01_FA[152],
+                x01_FA[153], x01_FA[154], x01_FA[155], x01_FA[156], x01_FA[157], x01_FA[158], x01_FA[159], x01_FA[160],
+                x01_FA[161], x01_FA[162], x01_FA[163], x01_FA[164], x01_FA[165], x01_FA[166], x01_FA[167], x01_FA[168],
+                x01_FA[169], x01_FA[170], x01_FA[171], x01_FA[172], x01_FA[173], x01_FA[174], x01_FA[175], x01_FA[176],
+                x01_FA[177], x01_FA[178], x01_FA[179], x01_FA[180], x01_FA[181], x01_FA[182], x01_FA[183], x01_FA[184],
+                x01_FA[185], x01_FA[186], x01_FA[187], x01_FA[188], x01_FA[189], x01_FA[190], x01_FA[191], x01_FA[192],
+                x01_FA[193], x01_FA[194], x01_FA[195], x01_FA[196], x01_FA[197], x01_FA[198], x01_FA[199], x01_FA[200],
+                x01_FA[201], x01_FA[202], x01_FA[203], x01_FA[204], x01_FA[205], x01_FA[206], x01_FA[207], x01_FA[208],
+                x01_FA[209], x01_FA[210], x01_FA[211], x01_FA[212], x01_FA[213], x01_FA[214], x01_FA[215], x01_FA[216],
+                x01_FA[217], x01_FA[218], x01_FA[219], x01_FA[220], x01_FA[221], x01_FA[222], x01_FA[223], x01_FA[224],
+                x01_FA[225], x01_FA[226], x01_FA[227], x01_FA[228], x01_FA[229], x01_FA[230], x01_FA[231], x01_FA[232],
+                x01_FA[233], x01_FA[234], x01_FA[235], x01_FA[236], x01_FA[237], x01_FA[238], x01_FA[239], x01_FA[240],
+                x01_FA[241], x01_FA[242], x01_FA[243], x01_FA[244], x01_FA[245], x01_FA[246], x01_FA[247], x01_FA[248],
+                // </editor-fold>
+                x01_FA[249], xFB);
+    }
 
     @Test
     public void test252() throws Throwable {
@@ -507,6 +625,8 @@
         test252(mh, a, r0);
         MethodHandle mh_CA = MH_hashArguments_VA.asFixedArity().asCollector(Object[].class, ARITY);
         test252(mh_CA, a, r0);
+        MethodHandle mh_a = MethodHandles.lookup().findStatic(BigArityTest.class, "hashArguments_"+ARITY+"_a", MT_A).asCollector(1, Object[].class, ARITY-2);
+        test252(mh_a, a, r0);
     }
     public void test252(MethodHandle mh, Object[] a, Object r0) throws Throwable {
         Object r;
@@ -686,6 +806,43 @@
     // </editor-fold>
     xF8, xF9, xFA, xFB, xFC);
     }
+    static Object hashArguments_253_a(Object x00, Object[] x01_FB, Object xFC) {
+        return Objects.hash(
+                // <editor-fold defaultstate="collapsed" desc="x00, x01_FB[0], x01_FB[1], x01_FB[2], ...">
+                x00, x01_FB[0], x01_FB[1], x01_FB[2], x01_FB[3], x01_FB[4], x01_FB[5], x01_FB[6], x01_FB[7], x01_FB[8],
+                x01_FB[9], x01_FB[10], x01_FB[11], x01_FB[12], x01_FB[13], x01_FB[14], x01_FB[15], x01_FB[16],
+                x01_FB[17], x01_FB[18], x01_FB[19], x01_FB[20], x01_FB[21], x01_FB[22], x01_FB[23], x01_FB[24],
+                x01_FB[25], x01_FB[26], x01_FB[27], x01_FB[28], x01_FB[29], x01_FB[30], x01_FB[31], x01_FB[32],
+                x01_FB[33], x01_FB[34], x01_FB[35], x01_FB[36], x01_FB[37], x01_FB[38], x01_FB[39], x01_FB[40],
+                x01_FB[41], x01_FB[42], x01_FB[43], x01_FB[44], x01_FB[45], x01_FB[46], x01_FB[47], x01_FB[48],
+                x01_FB[49], x01_FB[50], x01_FB[51], x01_FB[52], x01_FB[53], x01_FB[54], x01_FB[55], x01_FB[56],
+                x01_FB[57], x01_FB[58], x01_FB[59], x01_FB[60], x01_FB[61], x01_FB[62], x01_FB[63], x01_FB[64],
+                x01_FB[65], x01_FB[66], x01_FB[67], x01_FB[68], x01_FB[69], x01_FB[70], x01_FB[71], x01_FB[72],
+                x01_FB[73], x01_FB[74], x01_FB[75], x01_FB[76], x01_FB[77], x01_FB[78], x01_FB[79], x01_FB[80],
+                x01_FB[81], x01_FB[82], x01_FB[83], x01_FB[84], x01_FB[85], x01_FB[86], x01_FB[87], x01_FB[88],
+                x01_FB[89], x01_FB[90], x01_FB[91], x01_FB[92], x01_FB[93], x01_FB[94], x01_FB[95], x01_FB[96],
+                x01_FB[97], x01_FB[98], x01_FB[99], x01_FB[100], x01_FB[101], x01_FB[102], x01_FB[103], x01_FB[104],
+                x01_FB[105], x01_FB[106], x01_FB[107], x01_FB[108], x01_FB[109], x01_FB[110], x01_FB[111], x01_FB[112],
+                x01_FB[113], x01_FB[114], x01_FB[115], x01_FB[116], x01_FB[117], x01_FB[118], x01_FB[119], x01_FB[120],
+                x01_FB[121], x01_FB[122], x01_FB[123], x01_FB[124], x01_FB[125], x01_FB[126], x01_FB[127], x01_FB[128],
+                x01_FB[129], x01_FB[130], x01_FB[131], x01_FB[132], x01_FB[133], x01_FB[134], x01_FB[135], x01_FB[136],
+                x01_FB[137], x01_FB[138], x01_FB[139], x01_FB[140], x01_FB[141], x01_FB[142], x01_FB[143], x01_FB[144],
+                x01_FB[145], x01_FB[146], x01_FB[147], x01_FB[148], x01_FB[149], x01_FB[150], x01_FB[151], x01_FB[152],
+                x01_FB[153], x01_FB[154], x01_FB[155], x01_FB[156], x01_FB[157], x01_FB[158], x01_FB[159], x01_FB[160],
+                x01_FB[161], x01_FB[162], x01_FB[163], x01_FB[164], x01_FB[165], x01_FB[166], x01_FB[167], x01_FB[168],
+                x01_FB[169], x01_FB[170], x01_FB[171], x01_FB[172], x01_FB[173], x01_FB[174], x01_FB[175], x01_FB[176],
+                x01_FB[177], x01_FB[178], x01_FB[179], x01_FB[180], x01_FB[181], x01_FB[182], x01_FB[183], x01_FB[184],
+                x01_FB[185], x01_FB[186], x01_FB[187], x01_FB[188], x01_FB[189], x01_FB[190], x01_FB[191], x01_FB[192],
+                x01_FB[193], x01_FB[194], x01_FB[195], x01_FB[196], x01_FB[197], x01_FB[198], x01_FB[199], x01_FB[200],
+                x01_FB[201], x01_FB[202], x01_FB[203], x01_FB[204], x01_FB[205], x01_FB[206], x01_FB[207], x01_FB[208],
+                x01_FB[209], x01_FB[210], x01_FB[211], x01_FB[212], x01_FB[213], x01_FB[214], x01_FB[215], x01_FB[216],
+                x01_FB[217], x01_FB[218], x01_FB[219], x01_FB[220], x01_FB[221], x01_FB[222], x01_FB[223], x01_FB[224],
+                x01_FB[225], x01_FB[226], x01_FB[227], x01_FB[228], x01_FB[229], x01_FB[230], x01_FB[231], x01_FB[232],
+                x01_FB[233], x01_FB[234], x01_FB[235], x01_FB[236], x01_FB[237], x01_FB[238], x01_FB[239], x01_FB[240],
+                x01_FB[241], x01_FB[242], x01_FB[243], x01_FB[244], x01_FB[245], x01_FB[246], x01_FB[247], x01_FB[248],
+                // </editor-fold>
+                x01_FB[249], x01_FB[250], xFC);
+    }
 
     @Test
     public void test253() throws Throwable {
@@ -720,6 +877,8 @@
         test253(mh, a, r0);
         MethodHandle mh_CA = MH_hashArguments_VA.asFixedArity().asCollector(Object[].class, ARITY);
         test253(mh_CA, a, r0);
+        MethodHandle mh_a = MethodHandles.lookup().findStatic(BigArityTest.class, "hashArguments_"+ARITY+"_a", MT_A).asCollector(1, Object[].class, ARITY-2);
+        test253(mh_a, a, r0);
     }
     public void test253(MethodHandle mh, Object[] a, Object r0) throws Throwable {
         Object r;
@@ -899,6 +1058,43 @@
     // </editor-fold>
     xF8, xF9, xFA, xFB, xFC, xFD);
     }
+    static Object hashArguments_254_a(Object x00, Object[] x01_FC, Object xFD) {
+        return Objects.hash(
+                // <editor-fold defaultstate="collapsed" desc="x00, x01_FC[0], x01_FC[1], x01_FC[2], ...">
+                x00, x01_FC[0], x01_FC[1], x01_FC[2], x01_FC[3], x01_FC[4], x01_FC[5], x01_FC[6], x01_FC[7], x01_FC[8],
+                x01_FC[9], x01_FC[10], x01_FC[11], x01_FC[12], x01_FC[13], x01_FC[14], x01_FC[15], x01_FC[16],
+                x01_FC[17], x01_FC[18], x01_FC[19], x01_FC[20], x01_FC[21], x01_FC[22], x01_FC[23], x01_FC[24],
+                x01_FC[25], x01_FC[26], x01_FC[27], x01_FC[28], x01_FC[29], x01_FC[30], x01_FC[31], x01_FC[32],
+                x01_FC[33], x01_FC[34], x01_FC[35], x01_FC[36], x01_FC[37], x01_FC[38], x01_FC[39], x01_FC[40],
+                x01_FC[41], x01_FC[42], x01_FC[43], x01_FC[44], x01_FC[45], x01_FC[46], x01_FC[47], x01_FC[48],
+                x01_FC[49], x01_FC[50], x01_FC[51], x01_FC[52], x01_FC[53], x01_FC[54], x01_FC[55], x01_FC[56],
+                x01_FC[57], x01_FC[58], x01_FC[59], x01_FC[60], x01_FC[61], x01_FC[62], x01_FC[63], x01_FC[64],
+                x01_FC[65], x01_FC[66], x01_FC[67], x01_FC[68], x01_FC[69], x01_FC[70], x01_FC[71], x01_FC[72],
+                x01_FC[73], x01_FC[74], x01_FC[75], x01_FC[76], x01_FC[77], x01_FC[78], x01_FC[79], x01_FC[80],
+                x01_FC[81], x01_FC[82], x01_FC[83], x01_FC[84], x01_FC[85], x01_FC[86], x01_FC[87], x01_FC[88],
+                x01_FC[89], x01_FC[90], x01_FC[91], x01_FC[92], x01_FC[93], x01_FC[94], x01_FC[95], x01_FC[96],
+                x01_FC[97], x01_FC[98], x01_FC[99], x01_FC[100], x01_FC[101], x01_FC[102], x01_FC[103], x01_FC[104],
+                x01_FC[105], x01_FC[106], x01_FC[107], x01_FC[108], x01_FC[109], x01_FC[110], x01_FC[111], x01_FC[112],
+                x01_FC[113], x01_FC[114], x01_FC[115], x01_FC[116], x01_FC[117], x01_FC[118], x01_FC[119], x01_FC[120],
+                x01_FC[121], x01_FC[122], x01_FC[123], x01_FC[124], x01_FC[125], x01_FC[126], x01_FC[127], x01_FC[128],
+                x01_FC[129], x01_FC[130], x01_FC[131], x01_FC[132], x01_FC[133], x01_FC[134], x01_FC[135], x01_FC[136],
+                x01_FC[137], x01_FC[138], x01_FC[139], x01_FC[140], x01_FC[141], x01_FC[142], x01_FC[143], x01_FC[144],
+                x01_FC[145], x01_FC[146], x01_FC[147], x01_FC[148], x01_FC[149], x01_FC[150], x01_FC[151], x01_FC[152],
+                x01_FC[153], x01_FC[154], x01_FC[155], x01_FC[156], x01_FC[157], x01_FC[158], x01_FC[159], x01_FC[160],
+                x01_FC[161], x01_FC[162], x01_FC[163], x01_FC[164], x01_FC[165], x01_FC[166], x01_FC[167], x01_FC[168],
+                x01_FC[169], x01_FC[170], x01_FC[171], x01_FC[172], x01_FC[173], x01_FC[174], x01_FC[175], x01_FC[176],
+                x01_FC[177], x01_FC[178], x01_FC[179], x01_FC[180], x01_FC[181], x01_FC[182], x01_FC[183], x01_FC[184],
+                x01_FC[185], x01_FC[186], x01_FC[187], x01_FC[188], x01_FC[189], x01_FC[190], x01_FC[191], x01_FC[192],
+                x01_FC[193], x01_FC[194], x01_FC[195], x01_FC[196], x01_FC[197], x01_FC[198], x01_FC[199], x01_FC[200],
+                x01_FC[201], x01_FC[202], x01_FC[203], x01_FC[204], x01_FC[205], x01_FC[206], x01_FC[207], x01_FC[208],
+                x01_FC[209], x01_FC[210], x01_FC[211], x01_FC[212], x01_FC[213], x01_FC[214], x01_FC[215], x01_FC[216],
+                x01_FC[217], x01_FC[218], x01_FC[219], x01_FC[220], x01_FC[221], x01_FC[222], x01_FC[223], x01_FC[224],
+                x01_FC[225], x01_FC[226], x01_FC[227], x01_FC[228], x01_FC[229], x01_FC[230], x01_FC[231], x01_FC[232],
+                x01_FC[233], x01_FC[234], x01_FC[235], x01_FC[236], x01_FC[237], x01_FC[238], x01_FC[239], x01_FC[240],
+                x01_FC[241], x01_FC[242], x01_FC[243], x01_FC[244], x01_FC[245], x01_FC[246], x01_FC[247], x01_FC[248],
+                // </editor-fold>
+                x01_FC[249], x01_FC[250], x01_FC[251], xFD);
+    }
 
     @Test
     public void test254() throws Throwable {
@@ -933,6 +1129,8 @@
         test254(mh, a, r0);
         MethodHandle mh_CA = MH_hashArguments_VA.asFixedArity().asCollector(Object[].class, ARITY);
         test254(mh_CA, a, r0);
+        MethodHandle mh_a = MethodHandles.lookup().findStatic(BigArityTest.class, "hashArguments_"+ARITY+"_a", MT_A).asCollector(1, Object[].class, ARITY-2);
+        test254(mh_a, a, r0);
     }
     public void test254(MethodHandle mh, Object[] a, Object r0) throws Throwable {
         Object r;
@@ -1094,6 +1292,43 @@
     // </editor-fold>
     xF8, xF9, xFA, xFB, xFC, xFD, xFE);
     }
+    static Object hashArguments_255_a(Object x00, Object[] x01_FD, Object xFE) {
+        return Objects.hash(
+                // <editor-fold defaultstate="collapsed" desc="x00, x01_FD[0], x01_FD[1], x01_FD[2], ...">
+                x00, x01_FD[0], x01_FD[1], x01_FD[2], x01_FD[3], x01_FD[4], x01_FD[5], x01_FD[6], x01_FD[7], x01_FD[8],
+                x01_FD[9], x01_FD[10], x01_FD[11], x01_FD[12], x01_FD[13], x01_FD[14], x01_FD[15], x01_FD[16],
+                x01_FD[17], x01_FD[18], x01_FD[19], x01_FD[20], x01_FD[21], x01_FD[22], x01_FD[23], x01_FD[24],
+                x01_FD[25], x01_FD[26], x01_FD[27], x01_FD[28], x01_FD[29], x01_FD[30], x01_FD[31], x01_FD[32],
+                x01_FD[33], x01_FD[34], x01_FD[35], x01_FD[36], x01_FD[37], x01_FD[38], x01_FD[39], x01_FD[40],
+                x01_FD[41], x01_FD[42], x01_FD[43], x01_FD[44], x01_FD[45], x01_FD[46], x01_FD[47], x01_FD[48],
+                x01_FD[49], x01_FD[50], x01_FD[51], x01_FD[52], x01_FD[53], x01_FD[54], x01_FD[55], x01_FD[56],
+                x01_FD[57], x01_FD[58], x01_FD[59], x01_FD[60], x01_FD[61], x01_FD[62], x01_FD[63], x01_FD[64],
+                x01_FD[65], x01_FD[66], x01_FD[67], x01_FD[68], x01_FD[69], x01_FD[70], x01_FD[71], x01_FD[72],
+                x01_FD[73], x01_FD[74], x01_FD[75], x01_FD[76], x01_FD[77], x01_FD[78], x01_FD[79], x01_FD[80],
+                x01_FD[81], x01_FD[82], x01_FD[83], x01_FD[84], x01_FD[85], x01_FD[86], x01_FD[87], x01_FD[88],
+                x01_FD[89], x01_FD[90], x01_FD[91], x01_FD[92], x01_FD[93], x01_FD[94], x01_FD[95], x01_FD[96],
+                x01_FD[97], x01_FD[98], x01_FD[99], x01_FD[100], x01_FD[101], x01_FD[102], x01_FD[103], x01_FD[104],
+                x01_FD[105], x01_FD[106], x01_FD[107], x01_FD[108], x01_FD[109], x01_FD[110], x01_FD[111], x01_FD[112],
+                x01_FD[113], x01_FD[114], x01_FD[115], x01_FD[116], x01_FD[117], x01_FD[118], x01_FD[119], x01_FD[120],
+                x01_FD[121], x01_FD[122], x01_FD[123], x01_FD[124], x01_FD[125], x01_FD[126], x01_FD[127], x01_FD[128],
+                x01_FD[129], x01_FD[130], x01_FD[131], x01_FD[132], x01_FD[133], x01_FD[134], x01_FD[135], x01_FD[136],
+                x01_FD[137], x01_FD[138], x01_FD[139], x01_FD[140], x01_FD[141], x01_FD[142], x01_FD[143], x01_FD[144],
+                x01_FD[145], x01_FD[146], x01_FD[147], x01_FD[148], x01_FD[149], x01_FD[150], x01_FD[151], x01_FD[152],
+                x01_FD[153], x01_FD[154], x01_FD[155], x01_FD[156], x01_FD[157], x01_FD[158], x01_FD[159], x01_FD[160],
+                x01_FD[161], x01_FD[162], x01_FD[163], x01_FD[164], x01_FD[165], x01_FD[166], x01_FD[167], x01_FD[168],
+                x01_FD[169], x01_FD[170], x01_FD[171], x01_FD[172], x01_FD[173], x01_FD[174], x01_FD[175], x01_FD[176],
+                x01_FD[177], x01_FD[178], x01_FD[179], x01_FD[180], x01_FD[181], x01_FD[182], x01_FD[183], x01_FD[184],
+                x01_FD[185], x01_FD[186], x01_FD[187], x01_FD[188], x01_FD[189], x01_FD[190], x01_FD[191], x01_FD[192],
+                x01_FD[193], x01_FD[194], x01_FD[195], x01_FD[196], x01_FD[197], x01_FD[198], x01_FD[199], x01_FD[200],
+                x01_FD[201], x01_FD[202], x01_FD[203], x01_FD[204], x01_FD[205], x01_FD[206], x01_FD[207], x01_FD[208],
+                x01_FD[209], x01_FD[210], x01_FD[211], x01_FD[212], x01_FD[213], x01_FD[214], x01_FD[215], x01_FD[216],
+                x01_FD[217], x01_FD[218], x01_FD[219], x01_FD[220], x01_FD[221], x01_FD[222], x01_FD[223], x01_FD[224],
+                x01_FD[225], x01_FD[226], x01_FD[227], x01_FD[228], x01_FD[229], x01_FD[230], x01_FD[231], x01_FD[232],
+                x01_FD[233], x01_FD[234], x01_FD[235], x01_FD[236], x01_FD[237], x01_FD[238], x01_FD[239], x01_FD[240],
+                x01_FD[241], x01_FD[242], x01_FD[243], x01_FD[244], x01_FD[245], x01_FD[246], x01_FD[247], x01_FD[248],
+                // </editor-fold>
+                x01_FD[249], x01_FD[250], x01_FD[251], x01_FD[252], xFE);
+    }
 
     @Test
     public void test255() throws Throwable {
@@ -1163,5 +1398,38 @@
         } catch (IllegalArgumentException ex) {
             System.out.println("OK: "+ex);
         }
+        MethodHandle mh_a;
+        try {
+            mh_a = MethodHandles.lookup().findStatic(BigArityTest.class, "hashArguments_"+ARITY+"_a", MT_A).asCollector(1, Object[].class, ARITY-2);
+            throw new AssertionError("should not create an arity 255 collector method handle");
+        } catch (IllegalArgumentException ex) {
+            System.out.println("OK: "+ex);
+            mh_a = MethodHandles.lookup().findStatic(BigArityTest.class, "hashArguments_"+ARITY+"_a", MT_A).asCollector(1, Object[].class, ARITY-3);
+        }
+        try {
+            r = mh_a.invokeExact(
+                    // <editor-fold defaultstate="collapsed" desc="a[0x00], a[0x01], a[0x02], a[0x03], a[0x04], ...">
+                    a[0x00], a[0x01], a[0x02], a[0x03], a[0x04], a[0x05], a[0x06], a[0x07], a[0x08], a[0x09], a[0x0A], a[0x0B], a[0x0C], a[0x0D], a[0x0E], a[0x0F],
+                    a[0x10], a[0x11], a[0x12], a[0x13], a[0x14], a[0x15], a[0x16], a[0x17], a[0x18], a[0x19], a[0x1A], a[0x1B], a[0x1C], a[0x1D], a[0x1E], a[0x1F],
+                    a[0x20], a[0x21], a[0x22], a[0x23], a[0x24], a[0x25], a[0x26], a[0x27], a[0x28], a[0x29], a[0x2A], a[0x2B], a[0x2C], a[0x2D], a[0x2E], a[0x2F],
+                    a[0x30], a[0x31], a[0x32], a[0x33], a[0x34], a[0x35], a[0x36], a[0x37], a[0x38], a[0x39], a[0x3A], a[0x3B], a[0x3C], a[0x3D], a[0x3E], a[0x3F],
+                    a[0x40], a[0x41], a[0x42], a[0x43], a[0x44], a[0x45], a[0x46], a[0x47], a[0x48], a[0x49], a[0x4A], a[0x4B], a[0x4C], a[0x4D], a[0x4E], a[0x4F],
+                    a[0x50], a[0x51], a[0x52], a[0x53], a[0x54], a[0x55], a[0x56], a[0x57], a[0x58], a[0x59], a[0x5A], a[0x5B], a[0x5C], a[0x5D], a[0x5E], a[0x5F],
+                    a[0x60], a[0x61], a[0x62], a[0x63], a[0x64], a[0x65], a[0x66], a[0x67], a[0x68], a[0x69], a[0x6A], a[0x6B], a[0x6C], a[0x6D], a[0x6E], a[0x6F],
+                    a[0x70], a[0x71], a[0x72], a[0x73], a[0x74], a[0x75], a[0x76], a[0x77], a[0x78], a[0x79], a[0x7A], a[0x7B], a[0x7C], a[0x7D], a[0x7E], a[0x7F],
+                    a[0x80], a[0x81], a[0x82], a[0x83], a[0x84], a[0x85], a[0x86], a[0x87], a[0x88], a[0x89], a[0x8A], a[0x8B], a[0x8C], a[0x8D], a[0x8E], a[0x8F],
+                    a[0x90], a[0x91], a[0x92], a[0x93], a[0x94], a[0x95], a[0x96], a[0x97], a[0x98], a[0x99], a[0x9A], a[0x9B], a[0x9C], a[0x9D], a[0x9E], a[0x9F],
+                    a[0xA0], a[0xA1], a[0xA2], a[0xA3], a[0xA4], a[0xA5], a[0xA6], a[0xA7], a[0xA8], a[0xA9], a[0xAA], a[0xAB], a[0xAC], a[0xAD], a[0xAE], a[0xAF],
+                    a[0xB0], a[0xB1], a[0xB2], a[0xB3], a[0xB4], a[0xB5], a[0xB6], a[0xB7], a[0xB8], a[0xB9], a[0xBA], a[0xBB], a[0xBC], a[0xBD], a[0xBE], a[0xBF],
+                    a[0xC0], a[0xC1], a[0xC2], a[0xC3], a[0xC4], a[0xC5], a[0xC6], a[0xC7], a[0xC8], a[0xC9], a[0xCA], a[0xCB], a[0xCC], a[0xCD], a[0xCE], a[0xCF],
+                    a[0xD0], a[0xD1], a[0xD2], a[0xD3], a[0xD4], a[0xD5], a[0xD6], a[0xD7], a[0xD8], a[0xD9], a[0xDA], a[0xDB], a[0xDC], a[0xDD], a[0xDE], a[0xDF],
+                    a[0xE0], a[0xE1], a[0xE2], a[0xE3], a[0xE4], a[0xE5], a[0xE6], a[0xE7], a[0xE8], a[0xE9], a[0xEA], a[0xEB], a[0xEC], a[0xED], a[0xEE], a[0xEF],
+                    a[0xF0], a[0xF1], a[0xF2], a[0xF3], a[0xF4], a[0xF5], a[0xF6], a[0xF7],
+                    // </editor-fold>
+                    a[0xF8], a[0xF9], a[0xFA], a[0xFB], a[0xFC], a[0xFD], a[0xFE]);
+            throw new AssertionError("should not call an arity 255 collector method handle");
+        } catch (LinkageError ex) {
+            System.out.println("OK: "+ex);
+        }
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/invoke/CompileThresholdBootstrapTest.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8143232
+ * @summary Test verifies that LF bootstraps properly when run with COMPILE_THRESHOLD set
+ * @compile CompileThresholdBootstrapTest.java
+ * @run testng/othervm -Djava.lang.invoke.MethodHandle.COMPILE_THRESHOLD=30 test.java.lang.invoke.CompileThresholdBootstrapTest
+ */
+package test.java.lang.invoke;
+
+import java.lang.invoke.MethodHandles;
+import org.testng.*;
+import org.testng.annotations.*;
+
+public final class CompileThresholdBootstrapTest {
+
+    @Test
+    public void testBootstrap() throws Throwable {
+        Assert.assertEquals(0, (int)MethodHandles.constant(int.class, (int)0).invokeExact());
+    }
+
+    public static void main(String ... args) {
+        try {
+            CompileThresholdBootstrapTest test = new CompileThresholdBootstrapTest();
+            test.testBootstrap();
+        } catch (Throwable t) {
+            t.printStackTrace();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/invoke/FindClassSecurityManager.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test
+ * @run main/othervm/policy=findclass.security.policy/secure=java.lang.SecurityManager -ea -esa test.java.lang.invoke.FindClassSecurityManager
+ */
+
+package test.java.lang.invoke;
+
+import java.lang.invoke.MethodHandles;
+
+public class FindClassSecurityManager {
+    public static void main(String[] args) throws Throwable {
+        assert null != System.getSecurityManager();
+        Class<?> thisClass = FindClassSecurityManager.class;
+        MethodHandles.Lookup lookup = MethodHandles.lookup();
+        Class<?> lookedUp = lookup.findClass(thisClass.getName());
+        assert thisClass == lookedUp;
+        Class<?> accessed = lookup.accessClass(thisClass);
+        assert thisClass == accessed;
+    }
+}
--- a/jdk/test/java/lang/invoke/MethodHandlesTest.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/test/java/lang/invoke/MethodHandlesTest.java	Mon Nov 23 14:37:04 2015 -0500
@@ -32,6 +32,7 @@
 
 import test.java.lang.invoke.remote.RemoteExample;
 import java.lang.invoke.*;
+import static java.lang.invoke.MethodType.methodType;
 import java.lang.invoke.MethodHandles.Lookup;
 import java.lang.reflect.*;
 import java.util.*;
@@ -448,6 +449,7 @@
     }
     public static interface IntExample {
         public void            v0();
+        public default void    vd() { called("vd", this); }
         public static class Impl implements IntExample {
             public void        v0()     { called("Int/v0", this); }
             final String name;
@@ -719,9 +721,10 @@
     public void testFindSpecial0() throws Throwable {
         if (CAN_SKIP_WORKING)  return;
         startTest("findSpecial");
-        testFindSpecial(SubExample.class, Example.class, void.class, "v0");
-        testFindSpecial(SubExample.class, Example.class, void.class, "pkg_v0");
-        testFindSpecial(RemoteExample.class, PubExample.class, void.class, "Pub/pro_v0");
+        testFindSpecial(SubExample.class, Example.class, void.class, false, "v0");
+        testFindSpecial(SubExample.class, Example.class, void.class, false, "pkg_v0");
+        testFindSpecial(RemoteExample.class, PubExample.class, void.class, false, "Pub/pro_v0");
+        testFindSpecial(Example.class, IntExample.class, void.class, true, "vd");
         // Do some negative testing:
         for (Lookup lookup : new Lookup[]{ PRIVATE, EXAMPLE, PACKAGE, PUBLIC }) {
             testFindSpecial(false, lookup, Object.class, Example.class, void.class, "v0");
@@ -729,11 +732,12 @@
             testFindSpecial(false, lookup, SubExample.class, Example.class, void.class, "<init>", int.class);
             testFindSpecial(false, lookup, SubExample.class, Example.class, void.class, "<init>", Void.class);
             testFindSpecial(false, lookup, SubExample.class, Example.class, void.class, "s0");
+            testFindSpecial(false, lookup, Example.class, IntExample.class, void.class, "v0");
         }
     }
 
     void testFindSpecial(Class<?> specialCaller,
-                         Class<?> defc, Class<?> ret, String name, Class<?>... params) throws Throwable {
+                         Class<?> defc, Class<?> ret, boolean dflt, String name, Class<?>... params) throws Throwable {
         if (specialCaller == RemoteExample.class) {
             testFindSpecial(false, EXAMPLE,  specialCaller, defc, ret, name, params);
             testFindSpecial(false, PRIVATE,  specialCaller, defc, ret, name, params);
@@ -742,11 +746,11 @@
             testFindSpecial(false, PUBLIC,   specialCaller, defc, ret, name, params);
             return;
         }
-        testFindSpecial(true,  EXAMPLE,  specialCaller, defc, ret, name, params);
-        testFindSpecial(true,  PRIVATE,  specialCaller, defc, ret, name, params);
-        testFindSpecial(false, PACKAGE,  specialCaller, defc, ret, name, params);
-        testFindSpecial(false, SUBCLASS, specialCaller, defc, ret, name, params);
-        testFindSpecial(false, PUBLIC,   specialCaller, defc, ret, name, params);
+        testFindSpecial(true,          EXAMPLE,  specialCaller, defc, ret, name, params);
+        testFindSpecial(true,          PRIVATE,  specialCaller, defc, ret, name, params);
+        testFindSpecial(false || dflt, PACKAGE,  specialCaller, defc, ret, name, params);
+        testFindSpecial(false,         SUBCLASS, specialCaller, defc, ret, name, params);
+        testFindSpecial(false,         PUBLIC,   specialCaller, defc, ret, name, params);
     }
     void testFindSpecial(boolean positive, Lookup lookup, Class<?> specialCaller,
                          Class<?> defc, Class<?> ret, String name, Class<?>... params) throws Throwable {
@@ -1834,6 +1838,7 @@
     @Test // SLOW
     public void testSpreadArguments() throws Throwable {
         CodeCacheOverflowProcessor.runMHTest(this::testSpreadArguments0);
+        CodeCacheOverflowProcessor.runMHTest(this::testSpreadArguments1);
     }
 
     public void testSpreadArguments0() throws Throwable {
@@ -1842,26 +1847,97 @@
         for (Class<?> argType : new Class<?>[]{Object.class, Integer.class, int.class}) {
             if (verbosity >= 3)
                 System.out.println("spreadArguments "+argType);
+            Class<?> arrayType = java.lang.reflect.Array.newInstance(argType, 0).getClass();
             for (int nargs = 0; nargs < 50; nargs++) {
                 if (CAN_TEST_LIGHTLY && nargs > 11)  break;
                 for (int pos = 0; pos <= nargs; pos++) {
                     if (CAN_TEST_LIGHTLY && pos > 2 && pos < nargs-2)  continue;
                     if (nargs > 10 && pos > 4 && pos < nargs-4 && pos % 10 != 3)
                         continue;
-                    testSpreadArguments(argType, pos, nargs);
+                    testSpreadArguments(argType, arrayType, pos, nargs);
                 }
             }
         }
     }
-    public void testSpreadArguments(Class<?> argType, int pos, int nargs) throws Throwable {
+    public void testSpreadArguments(Class<?> argType, Class<?> arrayType, int pos, int nargs) throws Throwable {
         countTest();
-        Class<?> arrayType = java.lang.reflect.Array.newInstance(argType, 0).getClass();
         MethodHandle target2 = varargsArray(arrayType, nargs);
         MethodHandle target = target2.asType(target2.type().generic());
         if (verbosity >= 3)
             System.out.println("spread into "+target2+" ["+pos+".."+nargs+"]");
         Object[] args = randomArgs(target2.type().parameterArray());
         // make sure the target does what we think it does:
+        checkTarget(argType, pos, nargs, target, args);
+        List<Class<?>> newParams = new ArrayList<>(target2.type().parameterList());
+        {   // modify newParams in place
+            List<Class<?>> spreadParams = newParams.subList(pos, nargs);
+            spreadParams.clear(); spreadParams.add(arrayType);
+        }
+        MethodType newType = MethodType.methodType(arrayType, newParams);
+        MethodHandle result = target2.asSpreader(arrayType, nargs-pos);
+        assert(result.type() == newType) : Arrays.asList(result, newType);
+        result = result.asType(newType.generic());
+        Object returnValue;
+        if (pos == 0) {
+            Object args2 = ValueConversions.changeArrayType(arrayType, Arrays.copyOfRange(args, pos, args.length));
+            returnValue = result.invokeExact(args2);
+        } else {
+            Object[] args1 = Arrays.copyOfRange(args, 0, pos+1);
+            args1[pos] = ValueConversions.changeArrayType(arrayType, Arrays.copyOfRange(args, pos, args.length));
+            returnValue = result.invokeWithArguments(args1);
+        }
+        checkReturnValue(argType, args, result, returnValue);
+    }
+    public void testSpreadArguments1() throws Throwable {
+        if (CAN_SKIP_WORKING)  return;
+        startTest("spreadArguments/pos");
+        for (Class<?> argType : new Class<?>[]{Object.class, Integer.class, int.class}) {
+            if (verbosity >= 3)
+                System.out.println("spreadArguments "+argType);
+            Class<?> arrayType = java.lang.reflect.Array.newInstance(argType, 0).getClass();
+            for (int nargs = 0; nargs < 50; nargs++) {
+                if (CAN_TEST_LIGHTLY && nargs > 11)  break;
+                for (int pos = 0; pos <= nargs; pos++) {
+                    if (CAN_TEST_LIGHTLY && pos > 2 && pos < nargs-2)  continue;
+                    if (nargs > 10 && pos > 4 && pos < nargs-4 && pos % 10 != 3)
+                        continue;
+                    for (int spr = 1; spr < nargs - pos; ++spr) {
+                        if (spr > 4 && spr != 7 && spr != 11 && spr != 20 && spr < nargs - pos - 4) continue;
+                        testSpreadArguments(argType, arrayType, pos, spr, nargs);
+                    }
+                }
+            }
+        }
+    }
+    public void testSpreadArguments(Class<?> argType, Class<?> arrayType, int pos, int spread, int nargs) throws Throwable {
+        countTest();
+        MethodHandle target2 = varargsArray(arrayType, nargs);
+        MethodHandle target = target2.asType(target2.type().generic());
+        if (verbosity >= 3)
+            System.out.println("spread into " + target2 + " [" + pos + ".." + (pos + spread) + "[");
+        Object[] args = randomArgs(target2.type().parameterArray());
+        // make sure the target does what we think it does:
+        checkTarget(argType, pos, nargs, target, args);
+        List<Class<?>> newParams = new ArrayList<>(target2.type().parameterList());
+        {   // modify newParams in place
+            List<Class<?>> spreadParams = newParams.subList(pos, pos + spread);
+            spreadParams.clear();
+            spreadParams.add(arrayType);
+        }
+        MethodType newType = MethodType.methodType(arrayType, newParams);
+        MethodHandle result = target2.asSpreader(pos, arrayType, spread);
+        assert (result.type() == newType) : Arrays.asList(result, newType);
+        result = result.asType(newType.generic());
+        // args1 has nargs-spread entries, plus one for the to-be-spread array
+        int args1Length = nargs - (spread - 1);
+        Object[] args1 = new Object[args1Length];
+        System.arraycopy(args, 0, args1, 0, pos);
+        args1[pos] = ValueConversions.changeArrayType(arrayType, Arrays.copyOfRange(args, pos, pos + spread));
+        System.arraycopy(args, pos + spread, args1, pos + 1, nargs - spread - pos);
+        Object returnValue = result.invokeWithArguments(args1);
+        checkReturnValue(argType, args, result, returnValue);
+    }
+    private static void checkTarget(Class<?> argType, int pos, int nargs, MethodHandle target, Object[] args) throws Throwable {
         if (pos == 0 && nargs < 5 && !argType.isPrimitive()) {
             Object[] check = (Object[]) target.invokeWithArguments(args);
             assertArrayEquals(args, check);
@@ -1880,24 +1956,8 @@
                     break;
             }
         }
-        List<Class<?>> newParams = new ArrayList<>(target2.type().parameterList());
-        {   // modify newParams in place
-            List<Class<?>> spreadParams = newParams.subList(pos, nargs);
-            spreadParams.clear(); spreadParams.add(arrayType);
-        }
-        MethodType newType = MethodType.methodType(arrayType, newParams);
-        MethodHandle result = target2.asSpreader(arrayType, nargs-pos);
-        assert(result.type() == newType) : Arrays.asList(result, newType);
-        result = result.asType(newType.generic());
-        Object returnValue;
-        if (pos == 0) {
-            Object args2 = ValueConversions.changeArrayType(arrayType, Arrays.copyOfRange(args, pos, args.length));
-            returnValue = result.invokeExact(args2);
-        } else {
-            Object[] args1 = Arrays.copyOfRange(args, 0, pos+1);
-            args1[pos] = ValueConversions.changeArrayType(arrayType, Arrays.copyOfRange(args, pos, args.length));
-            returnValue = result.invokeWithArguments(args1);
-        }
+    }
+    private static void checkReturnValue(Class<?> argType, Object[] args, MethodHandle result, Object returnValue) {
         String argstr = Arrays.toString(args);
         if (!argType.isPrimitive()) {
             Object[] rv = (Object[]) returnValue;
@@ -1932,6 +1992,7 @@
     @Test // SLOW
     public void testAsCollector() throws Throwable {
         CodeCacheOverflowProcessor.runMHTest(this::testAsCollector0);
+        CodeCacheOverflowProcessor.runMHTest(this::testAsCollector1);
     }
 
     public void testAsCollector0() throws Throwable {
@@ -1974,6 +2035,51 @@
 //        collectedArgs[pos] = Arrays.asList((Object[]) collectedArgs[pos]);
         assertArrayEquals(collectedArgs, returnValue);
     }
+    public void testAsCollector1() throws Throwable {
+        if (CAN_SKIP_WORKING)  return;
+        startTest("asCollector/pos");
+        for (Class<?> argType : new Class<?>[]{Object.class, Integer.class, int.class}) {
+            if (verbosity >= 3)
+                System.out.println("asCollector/pos "+argType);
+            for (int nargs = 0; nargs < 50; nargs++) {
+                if (CAN_TEST_LIGHTLY && nargs > 11)  break;
+                for (int pos = 0; pos <= nargs; pos++) {
+                    if (CAN_TEST_LIGHTLY && pos > 2 && pos < nargs-2)  continue;
+                    if (nargs > 10 && pos > 4 && pos < nargs-4 && pos % 10 != 3)
+                        continue;
+                    for (int coll = 1; coll < nargs - pos; ++coll) {
+                        if (coll > 4 && coll != 7 && coll != 11 && coll != 20 && coll < nargs - pos - 4) continue;
+                        testAsCollector(argType, pos, coll, nargs);
+                    }
+                }
+            }
+        }
+    }
+    public void testAsCollector(Class<?> argType, int pos, int collect, int nargs) throws Throwable {
+        countTest();
+        // fake up a MH with the same type as the desired adapter:
+        MethodHandle fake = varargsArray(nargs);
+        fake = changeArgTypes(fake, argType);
+        MethodType newType = fake.type();
+        Object[] args = randomArgs(newType.parameterArray());
+        // here is what should happen:
+        // new arg list has "collect" less arguments, but one extra for collected arguments array
+        int collectedLength = nargs-(collect-1);
+        Object[] collectedArgs = new Object[collectedLength];
+        System.arraycopy(args, 0, collectedArgs, 0, pos);
+        collectedArgs[pos] = Arrays.copyOfRange(args, pos, pos+collect);
+        System.arraycopy(args, pos+collect, collectedArgs, pos+1, args.length-(pos+collect));
+        // here is the MH which will witness the collected argument part (not tail!):
+        MethodHandle target = varargsArray(collectedLength);
+        target = changeArgTypes(target, 0, pos, argType);
+        target = changeArgTypes(target, pos, pos+1, Object[].class);
+        target = changeArgTypes(target, pos+1, collectedLength, argType);
+        if (verbosity >= 3)
+            System.out.println("collect "+collect+" from "+Arrays.asList(args)+" ["+pos+".."+(pos+collect)+"[");
+        MethodHandle result = target.asCollector(pos, Object[].class, collect).asType(newType);
+        Object[] returnValue = (Object[]) result.invokeWithArguments(args);
+        assertArrayEquals(collectedArgs, returnValue);
+    }
 
     @Test // SLOW
     public void testInsertArguments() throws Throwable {
@@ -2117,21 +2223,29 @@
     public void testCollectArguments0() throws Throwable {
         if (CAN_SKIP_WORKING)  return;
         startTest("collectArguments");
-        testFoldOrCollectArguments(true);
+        testFoldOrCollectArguments(true, false);
     }
 
     @Test
     public void testFoldArguments() throws Throwable {
         CodeCacheOverflowProcessor.runMHTest(this::testFoldArguments0);
+        CodeCacheOverflowProcessor.runMHTest(this::testFoldArguments1);
     }
 
     public void testFoldArguments0() throws Throwable {
         if (CAN_SKIP_WORKING)  return;
         startTest("foldArguments");
-        testFoldOrCollectArguments(false);
+        testFoldOrCollectArguments(false, false);
     }
 
-    void testFoldOrCollectArguments(boolean isCollect) throws Throwable {
+    public void testFoldArguments1() throws Throwable {
+        if (CAN_SKIP_WORKING) return;
+        startTest("foldArguments/pos");
+        testFoldOrCollectArguments(false, true);
+    }
+
+    void testFoldOrCollectArguments(boolean isCollect, boolean withFoldPos) throws Throwable {
+        assert !(isCollect && withFoldPos); // exclude illegal argument combination
         for (Class<?> lastType : new Class<?>[]{ Object.class, String.class, int.class }) {
             for (Class<?> collectType : new Class<?>[]{ Object.class, String.class, int.class, void.class }) {
                 int maxArity = 10;
@@ -2146,7 +2260,7 @@
                         if (!mixArgs(argTypes, mix, argTypesSeen))  continue;
                         for (int collect = 0; collect <= nargs; collect++) {
                             for (int pos = 0; pos <= nargs - collect; pos++) {
-                                testFoldOrCollectArguments(argTypes, pos, collect, collectType, lastType, isCollect);
+                                testFoldOrCollectArguments(argTypes, pos, collect, collectType, lastType, isCollect, withFoldPos);
                             }
                         }
                     }
@@ -2186,13 +2300,14 @@
                                     int pos, int fold, // position and length of the folded arguments
                                     Class<?> combineType, // type returned from the combiner
                                     Class<?> lastType,  // type returned from the target
-                                    boolean isCollect) throws Throwable {
+                                    boolean isCollect,
+                                    boolean withFoldPos) throws Throwable {
         int nargs = argTypes.size();
-        if (pos != 0 && !isCollect)  return;  // can fold only at pos=0 for now
+        if (pos != 0 && !isCollect && !withFoldPos)  return;  // test MethodHandles.foldArguments(MH,MH) only for pos=0
         countTest();
         List<Class<?>> combineArgTypes = argTypes.subList(pos, pos + fold);
         List<Class<?>> targetArgTypes = new ArrayList<>(argTypes);
-        if (isCollect)  // does targret see arg[pos..pos+cc-1]?
+        if (isCollect)  // does target see arg[pos..pos+cc-1]?
             targetArgTypes.subList(pos, pos + fold).clear();
         if (combineType != void.class)
             targetArgTypes.add(pos, combineType);
@@ -2205,7 +2320,7 @@
         if (isCollect)
             target2 = MethodHandles.collectArguments(target, pos, combine);
         else
-            target2 = MethodHandles.foldArguments(target, combine);
+            target2 = withFoldPos ? MethodHandles.foldArguments(target, pos, combine) : MethodHandles.foldArguments(target, combine);
         // Simulate expected effect of combiner on arglist:
         List<Object> expectedList = new ArrayList<>(argsToPass);
         List<Object> argsToFold = expectedList.subList(pos, pos + fold);
@@ -2541,6 +2656,203 @@
     }
 
     @Test
+    public void testGenericLoopCombinator() throws Throwable {
+        CodeCacheOverflowProcessor.runMHTest(this::testGenericLoopCombinator0);
+    }
+    public void testGenericLoopCombinator0() throws Throwable {
+        if (CAN_SKIP_WORKING) return;
+        startTest("loop");
+        // Test as follows:
+        // * Have an increasing number of loop-local state. Local state type diversity grows with the number.
+        // * Initializers set the starting value of loop-local state from the corresponding loop argument.
+        // * For each local state element, there is a predicate - for all state combinations, exercise all predicates.
+        // * Steps modify each local state element in each iteration.
+        // * Finalizers group all local state elements into a resulting array. Verify end values.
+        // * Exercise both pre- and post-checked loops.
+        // Local state types, start values, predicates, and steps:
+        // * int a, 0, a < 7, a = a + 1
+        // * double b, 7.0, b > 0.5, b = b / 2.0
+        // * String c, "start", c.length <= 9, c = c + a
+        final Class<?>[] argTypes = new Class<?>[] {int.class, double.class, String.class};
+        final Object[][] args = new Object[][] {
+            new Object[]{0              },
+            new Object[]{0, 7.0         },
+            new Object[]{0, 7.0, "start"}
+        };
+        // These are the expected final state tuples for argument type tuple / predicate combinations, for pre- and
+        // post-checked loops:
+        final Object[][] preCheckedResults = new Object[][] {
+            new Object[]{7                           }, // (int) / int
+            new Object[]{7, 0.0546875                }, // (int,double) / int
+            new Object[]{5, 0.4375                   }, // (int,double) / double
+            new Object[]{7, 0.0546875, "start1234567"}, // (int,double,String) / int
+            new Object[]{5, 0.4375,    "start1234"   }, // (int,double,String) / double
+            new Object[]{6, 0.109375,  "start12345"  }  // (int,double,String) / String
+        };
+        final Object[][] postCheckedResults = new Object[][] {
+            new Object[]{7                         }, // (int) / int
+            new Object[]{7, 0.109375               }, // (int,double) / int
+            new Object[]{4, 0.4375                 }, // (int,double) / double
+            new Object[]{7, 0.109375, "start123456"}, // (int,double,String) / int
+            new Object[]{4, 0.4375,   "start123"   }, // (int,double,String) / double
+            new Object[]{5, 0.21875,  "start12345" }  // (int,double,String) / String
+        };
+        final Lookup l = MethodHandles.lookup();
+        final Class<?> MHT = MethodHandlesTest.class;
+        final Class<?> B = boolean.class;
+        final Class<?> I = int.class;
+        final Class<?> D = double.class;
+        final Class<?> S = String.class;
+        final MethodHandle hip = l.findStatic(MHT, "loopIntPred", methodType(B, I));
+        final MethodHandle hdp = l.findStatic(MHT, "loopDoublePred", methodType(B, I, D));
+        final MethodHandle hsp = l.findStatic(MHT, "loopStringPred", methodType(B, I, D, S));
+        final MethodHandle his = l.findStatic(MHT, "loopIntStep", methodType(I, I));
+        final MethodHandle hds = l.findStatic(MHT, "loopDoubleStep", methodType(D, I, D));
+        final MethodHandle hss = l.findStatic(MHT, "loopStringStep", methodType(S, I, D, S));
+        final MethodHandle[] preds = new MethodHandle[] {hip, hdp, hsp};
+        final MethodHandle[] steps = new MethodHandle[] {his, hds, hss};
+        for (int nargs = 1, useResultsStart = 0; nargs <= argTypes.length; useResultsStart += nargs++) {
+            Class<?>[] useArgTypes = Arrays.copyOf(argTypes, nargs, Class[].class);
+            MethodHandle[] usePreds = Arrays.copyOf(preds, nargs, MethodHandle[].class);
+            MethodHandle[] useSteps = Arrays.copyOf(steps, nargs, MethodHandle[].class);
+            Object[] useArgs = args[nargs - 1];
+            Object[][] usePreCheckedResults = new Object[nargs][];
+            Object[][] usePostCheckedResults = new Object[nargs][];
+            System.arraycopy(preCheckedResults, useResultsStart, usePreCheckedResults, 0, nargs);
+            System.arraycopy(postCheckedResults, useResultsStart, usePostCheckedResults, 0, nargs);
+            testGenericLoopCombinator(nargs, useArgTypes, usePreds, useSteps, useArgs, usePreCheckedResults,
+                    usePostCheckedResults);
+        }
+    }
+    void testGenericLoopCombinator(int nargs, Class<?>[] argTypes, MethodHandle[] preds, MethodHandle[] steps,
+                                   Object[] args, Object[][] preCheckedResults, Object[][] postCheckedResults)
+            throws Throwable {
+        List<Class<?>> lArgTypes = Arrays.asList(argTypes);
+        // Predicate and step handles are passed in as arguments, initializer and finalizer handles are constructed here
+        // from the available information.
+        MethodHandle[] inits = new MethodHandle[nargs];
+        for (int i = 0; i < nargs; ++i) {
+            MethodHandle h;
+            // Initializers are meant to return whatever they are passed at a given argument position. This means that
+            // additional arguments may have to be appended and prepended.
+            h = MethodHandles.identity(argTypes[i]);
+            if (i < nargs - 1) {
+                h = MethodHandles.dropArguments(h, 1, lArgTypes.subList(i + 1, nargs));
+            }
+            if (i > 0) {
+                h = MethodHandles.dropArguments(h, 0, lArgTypes.subList(0, i));
+            }
+            inits[i] = h;
+        }
+        // Finalizers are all meant to collect all of the loop-local state in a single array and return that. Local
+        // state is passed before the loop args. Construct such a finalizer by first taking a varargsArray collector for
+        // the number of local state arguments, and then appending the loop args as to-be-dropped arguments.
+        MethodHandle[] finis = new MethodHandle[nargs];
+        MethodHandle genericFini = MethodHandles.dropArguments(
+                varargsArray(nargs).asType(methodType(Object[].class, lArgTypes)), nargs, lArgTypes);
+        Arrays.fill(finis, genericFini);
+        // The predicate and step handles' signatures need to be extended. They currently just accept local state args;
+        // append possibly missing local state args and loop args using dropArguments.
+        for (int i = 0; i < nargs; ++i) {
+            List<Class<?>> additionalLocalStateArgTypes = lArgTypes.subList(i + 1, nargs);
+            preds[i] = MethodHandles.dropArguments(
+                    MethodHandles.dropArguments(preds[i], i + 1, additionalLocalStateArgTypes), nargs, lArgTypes);
+            steps[i] = MethodHandles.dropArguments(
+                    MethodHandles.dropArguments(steps[i], i + 1, additionalLocalStateArgTypes), nargs, lArgTypes);
+        }
+        // Iterate over all of the predicates, using only one of them at a time.
+        for (int i = 0; i < nargs; ++i) {
+            MethodHandle[] usePreds;
+            if (nargs == 1) {
+                usePreds = preds;
+            } else {
+                // Create an all-null preds array, and only use one predicate in this iteration. The null entries will
+                // be substituted with true predicates by the loop combinator.
+                usePreds = new MethodHandle[nargs];
+                usePreds[i] = preds[i];
+            }
+            // Go for it.
+            if (verbosity >= 3) {
+                System.out.println("calling loop for argument types " + lArgTypes + " with predicate at index " + i);
+                if (verbosity >= 5) {
+                    System.out.println("predicates: " + Arrays.asList(usePreds));
+                }
+            }
+            MethodHandle[] preInits = new MethodHandle[nargs + 1];
+            MethodHandle[] prePreds = new MethodHandle[nargs + 1];
+            MethodHandle[] preSteps = new MethodHandle[nargs + 1];
+            MethodHandle[] preFinis = new MethodHandle[nargs + 1];
+            System.arraycopy(inits, 0, preInits, 1, nargs);
+            System.arraycopy(usePreds, 0, prePreds, 0, nargs); // preds are offset by 1 for pre-checked loops
+            System.arraycopy(steps, 0, preSteps, 1, nargs);
+            System.arraycopy(finis, 0, preFinis, 0, nargs); // finis are also offset by 1 for pre-checked loops
+            // Convert to clause-major form.
+            MethodHandle[][] preClauses = new MethodHandle[nargs+1][4];
+            MethodHandle[][] postClauses = new MethodHandle[nargs][4];
+            toClauseMajor(preClauses, preInits, preSteps, prePreds, preFinis);
+            toClauseMajor(postClauses, inits, steps, usePreds, finis);
+            MethodHandle pre = MethodHandles.loop(preClauses);
+            MethodHandle post = MethodHandles.loop(postClauses);
+            Object[] preResults = (Object[]) pre.invokeWithArguments(args);
+            if (verbosity >= 4) {
+                System.out.println("pre-checked: expected " + Arrays.asList(preCheckedResults[i]) + ", actual " +
+                        Arrays.asList(preResults));
+            }
+            Object[] postResults = (Object[]) post.invokeWithArguments(args);
+            if (verbosity >= 4) {
+                System.out.println("post-checked: expected " + Arrays.asList(postCheckedResults[i]) + ", actual " +
+                        Arrays.asList(postResults));
+            }
+            assertArrayEquals(preCheckedResults[i], preResults);
+            assertArrayEquals(postCheckedResults[i], postResults);
+        }
+    }
+    static void toClauseMajor(MethodHandle[][] clauses, MethodHandle[] init, MethodHandle[] step, MethodHandle[] pred, MethodHandle[] fini) {
+        for (int i = 0; i < clauses.length; ++i) {
+            clauses[i][0] = init[i];
+            clauses[i][1] = step[i];
+            clauses[i][2] = pred[i];
+            clauses[i][3] = fini[i];
+        }
+    }
+    static boolean loopIntPred(int a) {
+        if (verbosity >= 5) {
+            System.out.println("int pred " + a + " -> " + (a < 7));
+        }
+        return a < 7;
+    }
+    static boolean loopDoublePred(int a, double b) {
+        if (verbosity >= 5) {
+            System.out.println("double pred (a=" + a + ") " + b + " -> " + (b > 0.5));
+        }
+        return b > 0.5;
+    }
+    static boolean loopStringPred(int a, double b, String c) {
+        if (verbosity >= 5) {
+            System.out.println("String pred (a=" + a + ",b=" + b + ") " + c + " -> " + (c.length() <= 9));
+        }
+        return c.length() <= 9;
+    }
+    static int loopIntStep(int a) {
+        if (verbosity >= 5) {
+            System.out.println("int step " + a + " -> " + (a + 1));
+        }
+        return a + 1;
+    }
+    static double loopDoubleStep(int a, double b) {
+        if (verbosity >= 5) {
+            System.out.println("double step (a=" + a + ") " + b + " -> " + (b / 2.0));
+        }
+        return b / 2.0;
+    }
+    static String loopStringStep(int a, double b, String c) {
+        if (verbosity >= 5) {
+            System.out.println("String step (a=" + a + ",b=" + b + ") " + c + " -> " + (c + a));
+        }
+        return c + a;
+    }
+
+    @Test
     public void testThrowException() throws Throwable {
         CodeCacheOverflowProcessor.runMHTest(this::testThrowException0);
     }
@@ -2576,12 +2888,107 @@
     }
 
     @Test
+    public void testTryFinally() throws Throwable {
+        CodeCacheOverflowProcessor.runMHTest(this::testTryFinally0);
+    }
+    public void testTryFinally0() throws Throwable {
+        if (CAN_SKIP_WORKING) return;
+        startTest("tryFinally");
+        String inputMessage = "returned";
+        String augmentedMessage = "augmented";
+        String thrownMessage = "thrown";
+        String rethrownMessage = "rethrown";
+        // Test these cases:
+        // * target returns, cleanup passes through
+        // * target returns, cleanup augments
+        // * target throws, cleanup augments and returns
+        // * target throws, cleanup augments and rethrows
+        MethodHandle target = MethodHandles.identity(String.class);
+        MethodHandle targetThrow = MethodHandles.dropArguments(
+                MethodHandles.throwException(String.class, Exception.class).bindTo(new Exception(thrownMessage)), 0, String.class);
+        MethodHandle cleanupPassThrough = MethodHandles.dropArguments(MethodHandles.identity(String.class), 0,
+                Throwable.class, String.class);
+        MethodHandle cleanupAugment = MethodHandles.dropArguments(MethodHandles.constant(String.class, augmentedMessage),
+                0, Throwable.class, String.class, String.class);
+        MethodHandle cleanupCatch = MethodHandles.dropArguments(MethodHandles.constant(String.class, thrownMessage), 0,
+                Throwable.class, String.class, String.class);
+        MethodHandle cleanupThrow = MethodHandles.dropArguments(MethodHandles.throwException(String.class, Exception.class).
+                bindTo(new Exception(rethrownMessage)), 0, Throwable.class, String.class, String.class);
+        testTryFinally(target, cleanupPassThrough, inputMessage, inputMessage, false);
+        testTryFinally(target, cleanupAugment, inputMessage, augmentedMessage, false);
+        testTryFinally(targetThrow, cleanupCatch, inputMessage, thrownMessage, true);
+        testTryFinally(targetThrow, cleanupThrow, inputMessage, rethrownMessage, true);
+        // Test the same cases as above for void targets and cleanups.
+        MethodHandles.Lookup lookup = MethodHandles.lookup();
+        Class<?> C = this.getClass();
+        MethodType targetType = methodType(void.class, String[].class);
+        MethodType cleanupType = methodType(void.class, Throwable.class, String[].class);
+        MethodHandle vtarget = lookup.findStatic(C, "vtarget", targetType);
+        MethodHandle vtargetThrow = lookup.findStatic(C, "vtargetThrow", targetType);
+        MethodHandle vcleanupPassThrough = lookup.findStatic(C, "vcleanupPassThrough", cleanupType);
+        MethodHandle vcleanupAugment = lookup.findStatic(C, "vcleanupAugment", cleanupType);
+        MethodHandle vcleanupCatch = lookup.findStatic(C, "vcleanupCatch", cleanupType);
+        MethodHandle vcleanupThrow = lookup.findStatic(C, "vcleanupThrow", cleanupType);
+        testTryFinally(vtarget, vcleanupPassThrough, inputMessage, inputMessage, false);
+        testTryFinally(vtarget, vcleanupAugment, inputMessage, augmentedMessage, false);
+        testTryFinally(vtargetThrow, vcleanupCatch, inputMessage, thrownMessage, true);
+        testTryFinally(vtargetThrow, vcleanupThrow, inputMessage, rethrownMessage, true);
+    }
+    void testTryFinally(MethodHandle target, MethodHandle cleanup, String input, String msg, boolean mustCatch)
+            throws Throwable {
+        countTest();
+        MethodHandle tf = MethodHandles.tryFinally(target, cleanup);
+        String result = null;
+        boolean isVoid = target.type().returnType() == void.class;
+        String[] argArray = new String[]{input};
+        try {
+            if (isVoid) {
+                tf.invoke(argArray);
+            } else {
+                result = (String) tf.invoke(input);
+            }
+        } catch (Throwable t) {
+            assertTrue(mustCatch);
+            assertEquals(msg, t.getMessage());
+            return;
+        }
+        assertFalse(mustCatch);
+        if (isVoid) {
+            assertEquals(msg, argArray[0]);
+        } else {
+            assertEquals(msg, result);
+        }
+    }
+    static void vtarget(String[] a) {
+        // naught, akin to identity
+    }
+    static void vtargetThrow(String[] a) throws Exception {
+        throw new Exception("thrown");
+    }
+    static void vcleanupPassThrough(Throwable t, String[] a) {
+        assertNull(t);
+        // naught, akin to identity
+    }
+    static void vcleanupAugment(Throwable t, String[] a) {
+        assertNull(t);
+        a[0] = "augmented";
+    }
+    static void vcleanupCatch(Throwable t, String[] a) {
+        assertNotNull(t);
+        a[0] = "caught";
+    }
+    static void vcleanupThrow(Throwable t, String[] a) throws Exception {
+        assertNotNull(t);
+        throw new Exception("rethrown");
+    }
+
+    @Test
     public void testInterfaceCast() throws Throwable {
         CodeCacheOverflowProcessor.runMHTest(this::testInterfaceCast0);
     }
 
     public void testInterfaceCast0() throws Throwable {
-        //if (CAN_SKIP_WORKING)  return;
+        if (CAN_SKIP_WORKING)  return;
         startTest("interfaceCast");
         assert( (((Object)"foo") instanceof CharSequence));
         assert(!(((Object)"foo") instanceof Iterable));
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/invoke/T8139885.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,1082 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test
+ * @run testng/othervm -ea -esa test.java.lang.invoke.T8139885
+ */
+
+package test.java.lang.invoke;
+
+import java.io.StringWriter;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodHandles.Lookup;
+import java.lang.invoke.MethodType;
+import java.util.*;
+
+import static java.lang.invoke.MethodType.methodType;
+
+import static org.testng.AssertJUnit.*;
+
+import org.testng.annotations.*;
+
+/**
+ * Example-scale and negative tests for JEP 274 extensions.
+ */
+public class T8139885 {
+
+    static final Lookup LOOKUP = MethodHandles.lookup();
+
+    //
+    // Tests.
+    //
+
+    @Test
+    public static void testLoopFac() throws Throwable {
+        MethodHandle[] counterClause = new MethodHandle[]{Fac.MH_zero, Fac.MH_inc};
+        MethodHandle[] accumulatorClause = new MethodHandle[]{Fac.MH_one, Fac.MH_mult, Fac.MH_pred, Fac.MH_fin};
+        MethodHandle loop = MethodHandles.loop(counterClause, accumulatorClause);
+        assertEquals(Fac.MT_fac, loop.type());
+        assertEquals(120, loop.invoke(5));
+    }
+
+    @Test
+    public static void testLoopFacNullInit() throws Throwable {
+        // null initializer for counter, should initialize to 0
+        MethodHandle[] counterClause = new MethodHandle[]{null, Fac.MH_inc};
+        MethodHandle[] accumulatorClause = new MethodHandle[]{Fac.MH_one, Fac.MH_mult, Fac.MH_pred, Fac.MH_fin};
+        MethodHandle loop = MethodHandles.loop(counterClause, accumulatorClause);
+        assertEquals(Fac.MT_fac, loop.type());
+        assertEquals(120, loop.invoke(5));
+    }
+
+    @Test
+    public static void testLoopVoid1() throws Throwable {
+        // construct a post-checked loop that only does one iteration and has a void body and void local state
+        MethodHandle loop = MethodHandles.loop(new MethodHandle[]{Empty.MH_f, Empty.MH_f, Empty.MH_pred, null});
+        assertEquals(MethodType.methodType(void.class), loop.type());
+        loop.invoke();
+    }
+
+    @Test
+    public static void testLoopVoid2() throws Throwable {
+        // construct a post-checked loop that only does one iteration and has a void body and void local state,
+        // initialized implicitly from the step type
+        MethodHandle loop = MethodHandles.loop(new MethodHandle[]{null, Empty.MH_f, Empty.MH_pred, null});
+        assertEquals(MethodType.methodType(void.class), loop.type());
+        loop.invoke();
+    }
+
+    @Test
+    public static void testLoopFacWithVoidState() throws Throwable {
+        // like testLoopFac, but with additional void state that outputs a dot
+        MethodHandle[] counterClause = new MethodHandle[]{Fac.MH_zero, Fac.MH_inc};
+        MethodHandle[] accumulatorClause = new MethodHandle[]{Fac.MH_one, Fac.MH_mult, Fac.MH_pred, Fac.MH_fin};
+        MethodHandle[] dotClause = new MethodHandle[]{null, Fac.MH_dot};
+        MethodHandle loop = MethodHandles.loop(counterClause, accumulatorClause, dotClause);
+        assertEquals(Fac.MT_fac, loop.type());
+        assertEquals(120, loop.invoke(5));
+    }
+
+    @Test
+    public static void testLoopNegative() throws Throwable {
+        MethodHandle mh_loop =
+                LOOKUP.findStatic(MethodHandles.class, "loop", methodType(MethodHandle.class, MethodHandle[][].class));
+        MethodHandle i0 = MethodHandles.constant(int.class, 0);
+        MethodHandle ii = MethodHandles.dropArguments(i0, 0, int.class, int.class);
+        MethodHandle id = MethodHandles.dropArguments(i0, 0, int.class, double.class);
+        MethodHandle i3 = MethodHandles.dropArguments(i0, 0, int.class, int.class, int.class);
+        List<MethodHandle> inits = Arrays.asList(ii, id, i3);
+        List<Class<?>> ints = Arrays.asList(int.class, int.class, int.class);
+        List<MethodHandle> finis = Arrays.asList(Fac.MH_fin, Fac.MH_inc, Counted.MH_step);
+        List<MethodHandle> preds1 = Arrays.asList(null, null, null);
+        List<MethodHandle> preds2 = Arrays.asList(null, Fac.MH_fin, null);
+        MethodHandle eek = MethodHandles.dropArguments(i0, 0, int.class, int.class, double.class);
+        List<MethodHandle> nesteps = Arrays.asList(Fac.MH_inc, eek, Fac.MH_dot);
+        List<MethodHandle> nepreds = Arrays.asList(null, Fac.MH_pred, null);
+        List<MethodHandle> nefinis = Arrays.asList(null, Fac.MH_fin, null);
+        MethodHandle[][][] cases = {
+                null,
+                {},
+                {{null, Fac.MH_inc}, {Fac.MH_one, null, Fac.MH_mult, Fac.MH_pred, Fac.MH_fin}},
+                {{null, Fac.MH_inc}, null},
+                {{Fac.MH_zero, Fac.MH_dot}},
+                {{ii}, {id}, {i3}},
+                {{null, Fac.MH_inc, null, Fac.MH_fin}, {null, Fac.MH_inc, null, Fac.MH_inc},
+                        {null, Counted.MH_start, null, Counted.MH_step}},
+                {{Fac.MH_zero, Fac.MH_inc}, {Fac.MH_one, Fac.MH_mult, null, Fac.MH_fin}, {null, Fac.MH_dot}},
+                {{Fac.MH_zero, Fac.MH_inc}, {Fac.MH_one, Fac.MH_mult, Fac.MH_fin, Fac.MH_fin}, {null, Fac.MH_dot}},
+                {{Fac.MH_zero, Fac.MH_inc}, {Fac.MH_one, eek, Fac.MH_pred, Fac.MH_fin}, {null, Fac.MH_dot}}
+        };
+        String[] messages = {
+                "null or no clauses passed",
+                "null or no clauses passed",
+                "All loop clauses must be represented as MethodHandle arrays with at most 4 elements.",
+                "null clauses are not allowed",
+                "clause 0: init and step return types must match: int != void",
+                "found non-effectively identical init parameter type lists: " + inits + " (common suffix: " + ints + ")",
+                "found non-identical finalizer return types: " + finis + " (return type: int)",
+                "no predicate found: " + preds1,
+                "predicates must have boolean return type: " + preds2,
+                "found non-effectively identical parameter type lists:\nstep: " + nesteps + "\npred: " + nepreds +
+                        "\nfini: " + nefinis + " (common parameter sequence: " + ints + ")"
+        };
+        for (int i = 0; i < cases.length; ++i) {
+            boolean caught = false;
+            try {
+                mh_loop.invokeWithArguments(cases[i]);
+            } catch (IllegalArgumentException iae) {
+                assertEquals(messages[i], iae.getMessage());
+                caught = true;
+            }
+            assertTrue(caught);
+        }
+    }
+
+    @Test
+    public static void testWhileLoop() throws Throwable {
+        // int i = 0; while (i < limit) { ++i; } return i; => limit
+        MethodHandle loop = MethodHandles.whileLoop(While.MH_zero, While.MH_pred, While.MH_step);
+        assertEquals(While.MT_while, loop.type());
+        assertEquals(23, loop.invoke(23));
+    }
+
+    @Test
+    public static void testWhileLoopNoIteration() throws Throwable {
+        // a while loop that never executes its body because the predicate evaluates to false immediately
+        MethodHandle loop = MethodHandles.whileLoop(While.MH_initString, While.MH_predString, While.MH_stepString);
+        assertEquals(While.MT_string, loop.type());
+        assertEquals("a", loop.invoke());
+    }
+
+    @Test
+    public static void testDoWhileLoop() throws Throwable {
+        // int i = 0; do { ++i; } while (i < limit); return i; => limit
+        MethodHandle loop = MethodHandles.doWhileLoop(While.MH_zero, While.MH_step, While.MH_pred);
+        assertEquals(While.MT_while, loop.type());
+        assertEquals(23, loop.invoke(23));
+    }
+
+    @Test
+    public static void testWhileZip() throws Throwable {
+        MethodHandle loop = MethodHandles.doWhileLoop(While.MH_zipInitZip, While.MH_zipStep, While.MH_zipPred);
+        assertEquals(While.MT_zip, loop.type());
+        List<String> a = Arrays.asList("a", "b", "c", "d");
+        List<String> b = Arrays.asList("e", "f", "g", "h");
+        List<String> zipped = Arrays.asList("a", "e", "b", "f", "c", "g", "d", "h");
+        assertEquals(zipped, (List<String>) loop.invoke(a.iterator(), b.iterator()));
+    }
+
+    @Test
+    public static void testCountedLoop() throws Throwable {
+        // String s = "Lambdaman!"; for (int i = 0; i < 13; ++i) { s = "na " + s; } return s; => a variation on a well known theme
+        MethodHandle fit13 = MethodHandles.constant(int.class, 13);
+        MethodHandle loop = MethodHandles.countedLoop(fit13, Counted.MH_start, Counted.MH_step);
+        assertEquals(Counted.MT_counted, loop.type());
+        assertEquals("na na na na na na na na na na na na na Lambdaman!", loop.invoke("Lambdaman!"));
+    }
+
+    @Test
+    public static void testCountedArrayLoop() throws Throwable {
+        // int[] a = new int[]{0}; for (int i = 0; i < 13; ++i) { ++a[0]; } => a[0] == 13
+        MethodHandle fit13 = MethodHandles.dropArguments(MethodHandles.constant(int.class, 13), 0, int[].class);
+        MethodHandle loop = MethodHandles.countedLoop(fit13, null, Counted.MH_stepUpdateArray);
+        assertEquals(Counted.MT_arrayCounted, loop.type());
+        int[] a = new int[]{0};
+        loop.invoke(a);
+        assertEquals(13, a[0]);
+    }
+
+    @Test
+    public static void testCountedPrintingLoop() throws Throwable {
+        MethodHandle fit5 = MethodHandles.constant(int.class, 5);
+        MethodHandle loop = MethodHandles.countedLoop(fit5, null, Counted.MH_printHello);
+        assertEquals(Counted.MT_countedPrinting, loop.type());
+        loop.invoke();
+    }
+
+    @Test
+    public static void testCountedRangeLoop() throws Throwable {
+        // String s = "Lambdaman!"; for (int i = -5; i < 8; ++i) { s = "na " + s; } return s; => a well known theme
+        MethodHandle fitm5 = MethodHandles.dropArguments(Counted.MH_m5, 0, String.class);
+        MethodHandle fit8 = MethodHandles.dropArguments(Counted.MH_8, 0, String.class);
+        MethodHandle loop = MethodHandles.countedLoop(fitm5, fit8, Counted.MH_start, Counted.MH_step);
+        assertEquals(Counted.MT_counted, loop.type());
+        assertEquals("na na na na na na na na na na na na na Lambdaman!", loop.invoke("Lambdaman!"));
+    }
+
+    @Test
+    public static void testIterateSum() throws Throwable {
+        // Integer[] a = new Integer[]{1,2,3,4,5,6}; int sum = 0; for (int e : a) { sum += e; } return sum; => 21
+        MethodHandle loop = MethodHandles.iteratedLoop(Iterate.MH_sumIterator, Iterate.MH_sumInit, Iterate.MH_sumStep);
+        assertEquals(Iterate.MT_sum, loop.type());
+        assertEquals(21, loop.invoke(new Integer[]{1, 2, 3, 4, 5, 6}));
+    }
+
+    @Test
+    public static void testIterateReverse() throws Throwable {
+        MethodHandle loop = MethodHandles.iteratedLoop(null, Iterate.MH_reverseInit, Iterate.MH_reverseStep);
+        assertEquals(Iterate.MT_reverse, loop.type());
+        List<String> list = Arrays.asList("a", "b", "c", "d", "e");
+        List<String> reversedList = Arrays.asList("e", "d", "c", "b", "a");
+        assertEquals(reversedList, (List<String>) loop.invoke(list));
+    }
+
+    @Test
+    public static void testIterateLength() throws Throwable {
+        MethodHandle loop = MethodHandles.iteratedLoop(null, Iterate.MH_lengthInit, Iterate.MH_lengthStep);
+        assertEquals(Iterate.MT_length, loop.type());
+        List<Double> list = Arrays.asList(23.0, 148.0, 42.0);
+        assertEquals(list.size(), (int) loop.invoke(list));
+    }
+
+    @Test
+    public static void testIterateMap() throws Throwable {
+        MethodHandle loop = MethodHandles.iteratedLoop(null, Iterate.MH_mapInit, Iterate.MH_mapStep);
+        assertEquals(Iterate.MT_map, loop.type());
+        List<String> list = Arrays.asList("Hello", "world", "!");
+        List<String> upList = Arrays.asList("HELLO", "WORLD", "!");
+        assertEquals(upList, (List<String>) loop.invoke(list));
+    }
+
+    @Test
+    public static void testIteratePrint() throws Throwable {
+        MethodHandle loop = MethodHandles.iteratedLoop(null, null, Iterate.MH_printStep);
+        assertEquals(Iterate.MT_print, loop.type());
+        loop.invoke(Arrays.asList("hello", "world"));
+    }
+
+    @Test
+    public static void testIterateNullBody() {
+        boolean caught = false;
+        try {
+            MethodHandles.iteratedLoop(MethodHandles.identity(int.class), MethodHandles.identity(int.class), null);
+        } catch (IllegalArgumentException iae) {
+            assertEquals("iterated loop body must not be null", iae.getMessage());
+            caught = true;
+        }
+        assertTrue(caught);
+    }
+
+    @Test
+    public static void testTryFinally() throws Throwable {
+        MethodHandle hello = MethodHandles.tryFinally(TryFinally.MH_greet, TryFinally.MH_exclaim);
+        assertEquals(TryFinally.MT_hello, hello.type());
+        assertEquals("Hello, world!", hello.invoke("world"));
+    }
+
+    @Test
+    public static void testTryFinallyVoid() throws Throwable {
+        MethodHandle tfVoid = MethodHandles.tryFinally(TryFinally.MH_print, TryFinally.MH_printMore);
+        assertEquals(TryFinally.MT_printHello, tfVoid.type());
+        tfVoid.invoke("world");
+    }
+
+    @Test
+    public static void testTryFinallySublist() throws Throwable {
+        MethodHandle helloMore = MethodHandles.tryFinally(TryFinally.MH_greetMore, TryFinally.MH_exclaimMore);
+        assertEquals(TryFinally.MT_moreHello, helloMore.type());
+        assertEquals("Hello, world and universe (but world first)!", helloMore.invoke("world", "universe"));
+    }
+
+    @Test
+    public static void testTryFinallyNegative() {
+        MethodHandle intid = MethodHandles.identity(int.class);
+        MethodHandle intco = MethodHandles.constant(int.class, 0);
+        MethodHandle errTarget = MethodHandles.dropArguments(intco, 0, int.class, double.class, String.class, int.class);
+        MethodHandle errCleanup = MethodHandles.dropArguments(MethodHandles.constant(int.class, 0), 0, Throwable.class,
+                int.class, double.class, Object.class);
+        MethodHandle[][] cases = {
+                {intid, MethodHandles.identity(double.class)},
+                {intid, MethodHandles.dropArguments(intid, 0, String.class)},
+                {intid, MethodHandles.dropArguments(intid, 0, Throwable.class, double.class)},
+                {errTarget, errCleanup}
+        };
+        String[] messages = {
+                "target and return types must match: double != int",
+                "cleanup first argument and Throwable must match: (String,int)int != class java.lang.Throwable",
+                "cleanup second argument and target return type must match: (Throwable,double,int)int != int",
+                "cleanup parameters after (Throwable,result) and target parameter list prefix must match: " +
+                        errCleanup.type() + " != " + errTarget.type()
+        };
+        for (int i = 0; i < cases.length; ++i) {
+            boolean caught = false;
+            try {
+                MethodHandles.tryFinally(cases[i][0], cases[i][1]);
+            } catch (IllegalArgumentException iae) {
+                assertEquals(messages[i], iae.getMessage());
+                caught = true;
+            }
+            assertTrue(caught);
+        }
+    }
+
+    @Test
+    public static void testFold0a() throws Throwable {
+        // equivalence to foldArguments(MethodHandle,MethodHandle)
+        MethodHandle fold = MethodHandles.foldArguments(Fold.MH_multer, 0, Fold.MH_adder);
+        assertEquals(Fold.MT_folded1, fold.type());
+        assertEquals(720, (int) fold.invoke(3, 4, 5));
+    }
+
+    @Test
+    public static void testFold1a() throws Throwable {
+        // test foldArguments for folding position 1
+        MethodHandle fold = MethodHandles.foldArguments(Fold.MH_multer, 1, Fold.MH_adder1);
+        assertEquals(Fold.MT_folded1, fold.type());
+        assertEquals(540, (int) fold.invoke(3, 4, 5));
+    }
+
+    @Test
+    public static void testFold0b() throws Throwable {
+        // test foldArguments equivalence with multiple types
+        MethodHandle fold = MethodHandles.foldArguments(Fold.MH_str, 0, Fold.MH_comb);
+        assertEquals(Fold.MT_folded2, fold.type());
+        assertEquals(23, (int) fold.invoke("true", true, 23));
+    }
+
+    @Test
+    public static void testFold1b() throws Throwable {
+        // test folgArguments for folding position 1, with multiple types
+        MethodHandle fold = MethodHandles.foldArguments(Fold.MH_str, 1, Fold.MH_comb2);
+        assertEquals(Fold.MT_folded3, fold.type());
+        assertEquals(1, (int) fold.invoke(true, true, 1));
+        assertEquals(-1, (int) fold.invoke(true, false, -1));
+    }
+
+    @Test
+    public static void testFoldArgumentsExample() throws Throwable {
+        // test the JavaDoc foldArguments-with-pos example
+        StringWriter swr = new StringWriter();
+        MethodHandle trace = LOOKUP.findVirtual(StringWriter.class, "write", methodType(void.class, String.class)).bindTo(swr);
+        MethodHandle cat = LOOKUP.findVirtual(String.class, "concat", methodType(String.class, String.class));
+        assertEquals("boojum", (String) cat.invokeExact("boo", "jum"));
+        MethodHandle catTrace = MethodHandles.foldArguments(cat, 1, trace);
+        assertEquals("boojum", (String) catTrace.invokeExact("boo", "jum"));
+        assertEquals("jum", swr.toString());
+    }
+
+    @Test
+    public static void testAsSpreader() throws Throwable {
+        MethodHandle spreader = SpreadCollect.MH_forSpreading.asSpreader(1, int[].class, 3);
+        assertEquals(SpreadCollect.MT_spreader, spreader.type());
+        assertEquals("A456B", (String) spreader.invoke("A", new int[]{4, 5, 6}, "B"));
+    }
+
+    @Test
+    public static void testAsSpreaderExample() throws Throwable {
+        // test the JavaDoc asSpreader-with-pos example
+        MethodHandle compare = LOOKUP.findStatic(Objects.class, "compare", methodType(int.class, Object.class, Object.class, Comparator.class));
+        MethodHandle compare2FromArray = compare.asSpreader(0, Object[].class, 2);
+        Object[] ints = new Object[]{3, 9, 7, 7};
+        Comparator<Integer> cmp = (a, b) -> a - b;
+        assertTrue((int) compare2FromArray.invoke(Arrays.copyOfRange(ints, 0, 2), cmp) < 0);
+        assertTrue((int) compare2FromArray.invoke(Arrays.copyOfRange(ints, 1, 3), cmp) > 0);
+        assertTrue((int) compare2FromArray.invoke(Arrays.copyOfRange(ints, 2, 4), cmp) == 0);
+    }
+
+    @Test
+    public static void testAsSpreaderIllegalPos() throws Throwable {
+        int[] illegalPos = {-7, 3, 19};
+        int caught = 0;
+        for (int p : illegalPos) {
+            try {
+                SpreadCollect.MH_forSpreading.asSpreader(p, Object[].class, 3);
+            } catch (IllegalArgumentException iae) {
+                assertEquals("bad spread position", iae.getMessage());
+                ++caught;
+            }
+        }
+        assertEquals(illegalPos.length, caught);
+    }
+
+    @Test
+    public static void testAsCollector() throws Throwable {
+        MethodHandle collector = SpreadCollect.MH_forCollecting.asCollector(1, int[].class, 1);
+        assertEquals(SpreadCollect.MT_collector1, collector.type());
+        assertEquals("A4B", (String) collector.invoke("A", 4, "B"));
+        collector = SpreadCollect.MH_forCollecting.asCollector(1, int[].class, 2);
+        assertEquals(SpreadCollect.MT_collector2, collector.type());
+        assertEquals("A45B", (String) collector.invoke("A", 4, 5, "B"));
+        collector = SpreadCollect.MH_forCollecting.asCollector(1, int[].class, 3);
+        assertEquals(SpreadCollect.MT_collector3, collector.type());
+        assertEquals("A456B", (String) collector.invoke("A", 4, 5, 6, "B"));
+    }
+
+    @Test
+    public static void testAsCollectorInvokeWithArguments() throws Throwable {
+        MethodHandle collector = SpreadCollect.MH_forCollecting.asCollector(1, int[].class, 1);
+        assertEquals(SpreadCollect.MT_collector1, collector.type());
+        assertEquals("A4B", (String) collector.invokeWithArguments("A", 4, "B"));
+        collector = SpreadCollect.MH_forCollecting.asCollector(1, int[].class, 2);
+        assertEquals(SpreadCollect.MT_collector2, collector.type());
+        assertEquals("A45B", (String) collector.invokeWithArguments("A", 4, 5, "B"));
+        collector = SpreadCollect.MH_forCollecting.asCollector(1, int[].class, 3);
+        assertEquals(SpreadCollect.MT_collector3, collector.type());
+        assertEquals("A456B", (String) collector.invokeWithArguments("A", 4, 5, 6, "B"));
+    }
+
+    @Test
+    public static void testAsCollectorLeading() throws Throwable {
+        MethodHandle collector = SpreadCollect.MH_forCollectingLeading.asCollector(0, int[].class, 1);
+        assertEquals(SpreadCollect.MT_collectorLeading1, collector.type());
+        assertEquals("7Q", (String) collector.invoke(7, "Q"));
+        collector = SpreadCollect.MH_forCollectingLeading.asCollector(0, int[].class, 2);
+        assertEquals(SpreadCollect.MT_collectorLeading2, collector.type());
+        assertEquals("78Q", (String) collector.invoke(7, 8, "Q"));
+        collector = SpreadCollect.MH_forCollectingLeading.asCollector(0, int[].class, 3);
+        assertEquals(SpreadCollect.MT_collectorLeading3, collector.type());
+        assertEquals("789Q", (String) collector.invoke(7, 8, 9, "Q"));
+    }
+
+    @Test
+    public static void testAsCollectorLeadingInvokeWithArguments() throws Throwable {
+        MethodHandle collector = SpreadCollect.MH_forCollectingLeading.asCollector(0, int[].class, 1);
+        assertEquals(SpreadCollect.MT_collectorLeading1, collector.type());
+        assertEquals("7Q", (String) collector.invokeWithArguments(7, "Q"));
+        collector = SpreadCollect.MH_forCollectingLeading.asCollector(0, int[].class, 2);
+        assertEquals(SpreadCollect.MT_collectorLeading2, collector.type());
+        assertEquals("78Q", (String) collector.invokeWithArguments(7, 8, "Q"));
+        collector = SpreadCollect.MH_forCollectingLeading.asCollector(0, int[].class, 3);
+        assertEquals(SpreadCollect.MT_collectorLeading3, collector.type());
+        assertEquals("789Q", (String) collector.invokeWithArguments(7, 8, 9, "Q"));
+    }
+
+    @Test
+    public static void testAsCollectorNone() throws Throwable {
+        MethodHandle collector = SpreadCollect.MH_forCollecting.asCollector(1, int[].class, 0);
+        assertEquals(SpreadCollect.MT_collector0, collector.type());
+        assertEquals("AB", (String) collector.invoke("A", "B"));
+    }
+
+    @Test
+    public static void testAsCollectorIllegalPos() throws Throwable {
+        int[] illegalPos = {-1, 17};
+        int caught = 0;
+        for (int p : illegalPos) {
+            try {
+                SpreadCollect.MH_forCollecting.asCollector(p, int[].class, 0);
+            } catch (IllegalArgumentException iae) {
+                assertEquals("bad collect position", iae.getMessage());
+                ++caught;
+            }
+        }
+        assertEquals(illegalPos.length, caught);
+    }
+
+    @Test
+    public static void testAsCollectorExample() throws Throwable {
+        // test the JavaDoc asCollector-with-pos example
+        StringWriter swr = new StringWriter();
+        MethodHandle swWrite = LOOKUP.
+                findVirtual(StringWriter.class, "write", methodType(void.class, char[].class, int.class, int.class)).
+                bindTo(swr);
+        MethodHandle swWrite4 = swWrite.asCollector(0, char[].class, 4);
+        swWrite4.invoke('A', 'B', 'C', 'D', 1, 2);
+        assertEquals("BC", swr.toString());
+        swWrite4.invoke('P', 'Q', 'R', 'S', 0, 4);
+        assertEquals("BCPQRS", swr.toString());
+        swWrite4.invoke('W', 'X', 'Y', 'Z', 3, 1);
+        assertEquals("BCPQRSZ", swr.toString());
+    }
+
+    @Test
+    public static void testFindSpecial() throws Throwable {
+        FindSpecial.C c = new FindSpecial.C();
+        assertEquals("I1.m", c.m());
+        MethodType t = MethodType.methodType(String.class);
+        MethodHandle ci1m = LOOKUP.findSpecial(FindSpecial.I1.class, "m", t, FindSpecial.C.class);
+        assertEquals("I1.m", (String) ci1m.invoke(c));
+    }
+
+    @Test
+    public static void testFindSpecialAbstract() throws Throwable {
+        FindSpecial.C c = new FindSpecial.C();
+        assertEquals("q", c.q());
+        MethodType t = MethodType.methodType(String.class);
+        boolean caught = false;
+        try {
+            MethodHandle ci3q = LOOKUP.findSpecial(FindSpecial.I3.class, "q", t, FindSpecial.C.class);
+        } catch (Throwable thrown) {
+            if (!(thrown instanceof IllegalAccessException) || !FindSpecial.ABSTRACT_ERROR.equals(thrown.getMessage())) {
+                throw new AssertionError(thrown.getMessage(), thrown);
+            }
+            caught = true;
+        }
+        assertTrue(caught);
+    }
+
+    @Test
+    public static void testFindClassCNFE() throws Throwable {
+        boolean caught = false;
+        try {
+            LOOKUP.findClass("does.not.Exist");
+        } catch (ClassNotFoundException cnfe) {
+            caught = true;
+        }
+        assertTrue(caught);
+    }
+
+    //
+    // Methods used to assemble tests.
+    //
+
+    static class Empty {
+
+        static void f() { }
+
+        static boolean pred() {
+            return false;
+        }
+
+        static final Class<Empty> EMPTY = Empty.class;
+
+        static final MethodType MT_f = methodType(void.class);
+        static final MethodType MT_pred = methodType(boolean.class);
+
+        static final MethodHandle MH_f;
+        static final MethodHandle MH_pred;
+
+        static {
+            try {
+                MH_f = LOOKUP.findStatic(EMPTY, "f", MT_f);
+                MH_pred = LOOKUP.findStatic(EMPTY, "pred", MT_pred);
+            } catch (Exception e) {
+                throw new ExceptionInInitializerError(e);
+            }
+        }
+    }
+
+    static class Fac {
+
+        static int zero(int k) {
+            return 0;
+        }
+
+        static int one(int k) {
+            return 1;
+        }
+
+        static boolean pred(int i, int acc, int k) {
+            return i < k;
+        }
+
+        static int inc(int i, int acc, int k) {
+            return i + 1;
+        }
+
+        static int mult(int i, int acc, int k) {
+            return i * acc;
+        }
+
+        static void dot(int i, int acc, int k) {
+            System.out.print('.');
+        }
+
+        static int fin(int i, int acc, int k) {
+            return acc;
+        }
+
+        static final Class<Fac> FAC = Fac.class;
+
+        static final MethodType MT_init = methodType(int.class, int.class);
+        static final MethodType MT_fn = methodType(int.class, int.class, int.class, int.class);
+        static final MethodType MT_dot = methodType(void.class, int.class, int.class, int.class);
+        static final MethodType MT_pred = methodType(boolean.class, int.class, int.class, int.class);
+
+        static final MethodHandle MH_zero;
+        static final MethodHandle MH_one;
+        static final MethodHandle MH_pred;
+        static final MethodHandle MH_inc;
+        static final MethodHandle MH_mult;
+        static final MethodHandle MH_dot;
+        static final MethodHandle MH_fin;
+
+        static final MethodType MT_fac = methodType(int.class, int.class);
+
+        static {
+            try {
+                MH_zero = LOOKUP.findStatic(FAC, "zero", MT_init);
+                MH_one = LOOKUP.findStatic(FAC, "one", MT_init);
+                MH_pred = LOOKUP.findStatic(FAC, "pred", MT_pred);
+                MH_inc = LOOKUP.findStatic(FAC, "inc", MT_fn);
+                MH_mult = LOOKUP.findStatic(FAC, "mult", MT_fn);
+                MH_dot = LOOKUP.findStatic(FAC, "dot", MT_dot);
+                MH_fin = LOOKUP.findStatic(FAC, "fin", MT_fn);
+            } catch (Exception e) {
+                throw new ExceptionInInitializerError(e);
+            }
+        }
+
+    }
+
+    static class While {
+
+        static int zero(int limit) {
+            return 0;
+        }
+
+        static boolean pred(int i, int limit) {
+            return i < limit;
+        }
+
+        static int step(int i, int limit) {
+            return i + 1;
+        }
+
+        static String initString() {
+            return "a";
+        }
+
+        static boolean predString(String s) {
+            return s.length() != 1;
+        }
+
+        static String stepString(String s) {
+            return s + "a";
+        }
+
+        static List<String> zipInitZip(Iterator<String> a, Iterator<String> b) {
+            return new ArrayList<>();
+        }
+
+        static boolean zipPred(List<String> zip, Iterator<String> a, Iterator<String> b) {
+            return a.hasNext() && b.hasNext();
+        }
+
+        static List<String> zipStep(List<String> zip, Iterator<String> a, Iterator<String> b) {
+            zip.add(a.next());
+            zip.add(b.next());
+            return zip;
+        }
+
+        static final Class<While> WHILE = While.class;
+
+        static final MethodType MT_zero = methodType(int.class, int.class);
+        static final MethodType MT_pred = methodType(boolean.class, int.class, int.class);
+        static final MethodType MT_fn = methodType(int.class, int.class, int.class);
+        static final MethodType MT_initString = methodType(String.class);
+        static final MethodType MT_predString = methodType(boolean.class, String.class);
+        static final MethodType MT_stepString = methodType(String.class, String.class);
+        static final MethodType MT_zipInitZip = methodType(List.class, Iterator.class, Iterator.class);
+        static final MethodType MT_zipPred = methodType(boolean.class, List.class, Iterator.class, Iterator.class);
+        static final MethodType MT_zipStep = methodType(List.class, List.class, Iterator.class, Iterator.class);
+
+        static final MethodHandle MH_zero;
+        static final MethodHandle MH_pred;
+        static final MethodHandle MH_step;
+        static final MethodHandle MH_initString;
+        static final MethodHandle MH_predString;
+        static final MethodHandle MH_stepString;
+        static final MethodHandle MH_zipInitZip;
+        static final MethodHandle MH_zipPred;
+        static final MethodHandle MH_zipStep;
+
+        static final MethodType MT_while = methodType(int.class, int.class);
+        static final MethodType MT_string = methodType(String.class);
+        static final MethodType MT_zip = methodType(List.class, Iterator.class, Iterator.class);
+
+        static {
+            try {
+                MH_zero = LOOKUP.findStatic(WHILE, "zero", MT_zero);
+                MH_pred = LOOKUP.findStatic(WHILE, "pred", MT_pred);
+                MH_step = LOOKUP.findStatic(WHILE, "step", MT_fn);
+                MH_initString = LOOKUP.findStatic(WHILE, "initString", MT_initString);
+                MH_predString = LOOKUP.findStatic(WHILE, "predString", MT_predString);
+                MH_stepString = LOOKUP.findStatic(WHILE, "stepString", MT_stepString);
+                MH_zipInitZip = LOOKUP.findStatic(WHILE, "zipInitZip", MT_zipInitZip);
+                MH_zipPred = LOOKUP.findStatic(WHILE, "zipPred", MT_zipPred);
+                MH_zipStep = LOOKUP.findStatic(WHILE, "zipStep", MT_zipStep);
+            } catch (Exception e) {
+                throw new ExceptionInInitializerError(e);
+            }
+        }
+
+    }
+
+    static class Counted {
+
+        static String start(String arg) {
+            return arg;
+        }
+
+        static String step(int counter, String v, String arg) {
+            return "na " + v;
+        }
+
+        static void stepUpdateArray(int counter, int[] a) {
+            ++a[0];
+        }
+
+        static void printHello(int counter) {
+            System.out.print("hello");
+        }
+
+        static final Class<Counted> COUNTED = Counted.class;
+
+        static final MethodType MT_start = methodType(String.class, String.class);
+        static final MethodType MT_step = methodType(String.class, int.class, String.class, String.class);
+        static final MethodType MT_stepUpdateArray = methodType(void.class, int.class, int[].class);
+        static final MethodType MT_printHello = methodType(void.class, int.class);
+
+        static final MethodHandle MH_13;
+        static final MethodHandle MH_m5;
+        static final MethodHandle MH_8;
+        static final MethodHandle MH_start;
+        static final MethodHandle MH_step;
+        static final MethodHandle MH_stepUpdateArray;
+        static final MethodHandle MH_printHello;
+
+        static final MethodType MT_counted = methodType(String.class, String.class);
+        static final MethodType MT_arrayCounted = methodType(void.class, int[].class);
+        static final MethodType MT_countedPrinting = methodType(void.class);
+
+        static {
+            try {
+                MH_13 = MethodHandles.constant(int.class, 13);
+                MH_m5 = MethodHandles.constant(int.class, -5);
+                MH_8 = MethodHandles.constant(int.class, 8);
+                MH_start = LOOKUP.findStatic(COUNTED, "start", MT_start);
+                MH_step = LOOKUP.findStatic(COUNTED, "step", MT_step);
+                MH_stepUpdateArray = LOOKUP.findStatic(COUNTED, "stepUpdateArray", MT_stepUpdateArray);
+                MH_printHello = LOOKUP.findStatic(COUNTED, "printHello", MT_printHello);
+            } catch (Exception e) {
+                throw new ExceptionInInitializerError(e);
+            }
+        }
+
+    }
+
+    static class Iterate {
+
+        static Iterator<Integer> sumIterator(Integer[] a) {
+            return Arrays.asList(a).iterator();
+        }
+
+        static int sumInit(Integer[] a) {
+            return 0;
+        }
+
+        static int sumStep(int s, int e, Integer[] a) {
+            return s + e;
+        }
+
+        static List<String> reverseInit(List<String> l) {
+            return new ArrayList<>();
+        }
+
+        static List<String> reverseStep(String e, List<String> r, List<String> l) {
+            r.add(0, e);
+            return r;
+        }
+
+        static int lengthInit(List<Double> l) {
+            return 0;
+        }
+
+        static int lengthStep(Object o, int len, List<Double> l) {
+            return len + 1;
+        }
+
+        static List<String> mapInit(List<String> l) {
+            return new ArrayList<>();
+        }
+
+        static List<String> mapStep(String e, List<String> r, List<String> l) {
+            r.add(e.toUpperCase());
+            return r;
+        }
+
+        static void printStep(String s, List<String> l) {
+            System.out.print(s);
+        }
+
+        static final Class<Iterate> ITERATE = Iterate.class;
+
+        static final MethodType MT_sumIterator = methodType(Iterator.class, Integer[].class);
+
+        static final MethodType MT_sumInit = methodType(int.class, Integer[].class);
+        static final MethodType MT_reverseInit = methodType(List.class, List.class);
+        static final MethodType MT_lenghInit = methodType(int.class, List.class);
+        static final MethodType MT_mapInit = methodType(List.class, List.class);
+
+        static final MethodType MT_sumStep = methodType(int.class, int.class, int.class, Integer[].class);
+        static final MethodType MT_reverseStep = methodType(List.class, String.class, List.class, List.class);
+        static final MethodType MT_lengthStep = methodType(int.class, Object.class, int.class, List.class);
+        static final MethodType MT_mapStep = methodType(List.class, String.class, List.class, List.class);
+        static final MethodType MT_printStep = methodType(void.class, String.class, List.class);
+
+        static final MethodHandle MH_sumIterator;
+        static final MethodHandle MH_sumInit;
+        static final MethodHandle MH_sumStep;
+        static final MethodHandle MH_printStep;
+
+        static final MethodHandle MH_reverseInit;
+        static final MethodHandle MH_reverseStep;
+
+        static final MethodHandle MH_lengthInit;
+        static final MethodHandle MH_lengthStep;
+
+        static final MethodHandle MH_mapInit;
+        static final MethodHandle MH_mapStep;
+
+        static final MethodType MT_sum = methodType(int.class, Integer[].class);
+        static final MethodType MT_reverse = methodType(List.class, List.class);
+        static final MethodType MT_length = methodType(int.class, List.class);
+        static final MethodType MT_map = methodType(List.class, List.class);
+        static final MethodType MT_print = methodType(void.class, List.class);
+
+        static {
+            try {
+                MH_sumIterator = LOOKUP.findStatic(ITERATE, "sumIterator", MT_sumIterator);
+                MH_sumInit = LOOKUP.findStatic(ITERATE, "sumInit", MT_sumInit);
+                MH_sumStep = LOOKUP.findStatic(ITERATE, "sumStep", MT_sumStep);
+                MH_reverseInit = LOOKUP.findStatic(ITERATE, "reverseInit", MT_reverseInit);
+                MH_reverseStep = LOOKUP.findStatic(ITERATE, "reverseStep", MT_reverseStep);
+                MH_lengthInit = LOOKUP.findStatic(ITERATE, "lengthInit", MT_lenghInit);
+                MH_lengthStep = LOOKUP.findStatic(ITERATE, "lengthStep", MT_lengthStep);
+                MH_mapInit = LOOKUP.findStatic(ITERATE, "mapInit", MT_mapInit);
+                MH_mapStep = LOOKUP.findStatic(ITERATE, "mapStep", MT_mapStep);
+                MH_printStep = LOOKUP.findStatic(ITERATE, "printStep", MT_printStep);
+            } catch (Exception e) {
+                throw new ExceptionInInitializerError(e);
+            }
+        }
+
+    }
+
+    static class TryFinally {
+
+        static String greet(String whom) {
+            return "Hello, " + whom;
+        }
+
+        static String exclaim(Throwable t, String r, String whom) {
+            return r + "!";
+        }
+
+        static void print(String what) {
+            System.out.print("Hello, " + what);
+        }
+
+        static void printMore(Throwable t, String what) {
+            System.out.println("!");
+        }
+
+        static String greetMore(String first, String second) {
+            return "Hello, " + first + " and " + second;
+        }
+
+        static String exclaimMore(Throwable t, String r, String first) {
+            return r + " (but " + first + " first)!";
+        }
+
+        static final Class<TryFinally> TRY_FINALLY = TryFinally.class;
+
+        static final MethodType MT_greet = methodType(String.class, String.class);
+        static final MethodType MT_exclaim = methodType(String.class, Throwable.class, String.class, String.class);
+        static final MethodType MT_print = methodType(void.class, String.class);
+        static final MethodType MT_printMore = methodType(void.class, Throwable.class, String.class);
+        static final MethodType MT_greetMore = methodType(String.class, String.class, String.class);
+        static final MethodType MT_exclaimMore = methodType(String.class, Throwable.class, String.class, String.class);
+
+        static final MethodHandle MH_greet;
+        static final MethodHandle MH_exclaim;
+        static final MethodHandle MH_print;
+        static final MethodHandle MH_printMore;
+        static final MethodHandle MH_greetMore;
+        static final MethodHandle MH_exclaimMore;
+
+        static final MethodType MT_hello = methodType(String.class, String.class);
+        static final MethodType MT_printHello = methodType(void.class, String.class);
+        static final MethodType MT_moreHello = methodType(String.class, String.class, String.class);
+
+        static {
+            try {
+                MH_greet = LOOKUP.findStatic(TRY_FINALLY, "greet", MT_greet);
+                MH_exclaim = LOOKUP.findStatic(TRY_FINALLY, "exclaim", MT_exclaim);
+                MH_print = LOOKUP.findStatic(TRY_FINALLY, "print", MT_print);
+                MH_printMore = LOOKUP.findStatic(TRY_FINALLY, "printMore", MT_printMore);
+                MH_greetMore = LOOKUP.findStatic(TRY_FINALLY, "greetMore", MT_greetMore);
+                MH_exclaimMore = LOOKUP.findStatic(TRY_FINALLY, "exclaimMore", MT_exclaimMore);
+            } catch (Exception e) {
+                throw new ExceptionInInitializerError(e);
+            }
+        }
+
+    }
+
+    static class Fold {
+
+        static int adder(int a, int b, int c) {
+            return a + b + c;
+        }
+
+        static int adder1(int a, int b) {
+            return a + b;
+        }
+
+        static int multer(int x, int q, int r, int s) {
+            return x * q * r * s;
+        }
+
+        static int str(boolean b1, String s, boolean b2, int x) {
+            return b1 && s.equals(String.valueOf(b2)) ? x : -x;
+        }
+
+        static boolean comb(String s, boolean b2) {
+            return !s.equals(b2);
+        }
+
+        static String comb2(boolean b2, int x) {
+            int ib = b2 ? 1 : 0;
+            return ib == x ? "true" : "false";
+        }
+
+        static final Class<Fold> FOLD = Fold.class;
+
+        static final MethodType MT_adder = methodType(int.class, int.class, int.class, int.class);
+        static final MethodType MT_adder1 = methodType(int.class, int.class, int.class);
+        static final MethodType MT_multer = methodType(int.class, int.class, int.class, int.class, int.class);
+        static final MethodType MT_str = methodType(int.class, boolean.class, String.class, boolean.class, int.class);
+        static final MethodType MT_comb = methodType(boolean.class, String.class, boolean.class);
+        static final MethodType MT_comb2 = methodType(String.class, boolean.class, int.class);
+
+        static final MethodHandle MH_adder;
+        static final MethodHandle MH_adder1;
+        static final MethodHandle MH_multer;
+        static final MethodHandle MH_str;
+        static final MethodHandle MH_comb;
+        static final MethodHandle MH_comb2;
+
+        static final MethodType MT_folded1 = methodType(int.class, int.class, int.class, int.class);
+        static final MethodType MT_folded2 = methodType(int.class, String.class, boolean.class, int.class);
+        static final MethodType MT_folded3 = methodType(int.class, boolean.class, boolean.class, int.class);
+
+        static {
+            try {
+                MH_adder = LOOKUP.findStatic(FOLD, "adder", MT_adder);
+                MH_adder1 = LOOKUP.findStatic(FOLD, "adder1", MT_adder1);
+                MH_multer = LOOKUP.findStatic(FOLD, "multer", MT_multer);
+                MH_str = LOOKUP.findStatic(FOLD, "str", MT_str);
+                MH_comb = LOOKUP.findStatic(FOLD, "comb", MT_comb);
+                MH_comb2 = LOOKUP.findStatic(FOLD, "comb2", MT_comb2);
+            } catch (Exception e) {
+                throw new ExceptionInInitializerError(e);
+            }
+        }
+    }
+
+    static class SpreadCollect {
+
+        static String forSpreading(String s1, int i1, int i2, int i3, String s2) {
+            return s1 + i1 + i2 + i3 + s2;
+        }
+
+        static String forCollecting(String s1, int[] is, String s2) {
+            StringBuilder sb = new StringBuilder(s1);
+            for (int i : is) {
+                sb.append(i);
+            }
+            return sb.append(s2).toString();
+        }
+
+        static String forCollectingLeading(int[] is, String s) {
+            return forCollecting("", is, s);
+        }
+
+        static final Class<SpreadCollect> SPREAD_COLLECT = SpreadCollect.class;
+
+        static final MethodType MT_forSpreading = methodType(String.class, String.class, int.class, int.class, int.class, String.class);
+        static final MethodType MT_forCollecting = methodType(String.class, String.class, int[].class, String.class);
+        static final MethodType MT_forCollectingLeading = methodType(String.class, int[].class, String.class);
+
+        static final MethodHandle MH_forSpreading;
+        static final MethodHandle MH_forCollecting;
+        static final MethodHandle MH_forCollectingLeading;
+
+        static final MethodType MT_spreader = methodType(String.class, String.class, int[].class, String.class);
+        static final MethodType MT_collector0 = methodType(String.class, String.class, String.class);
+        static final MethodType MT_collector1 = methodType(String.class, String.class, int.class, String.class);
+        static final MethodType MT_collector2 = methodType(String.class, String.class, int.class, int.class, String.class);
+        static final MethodType MT_collector3 = methodType(String.class, String.class, int.class, int.class, int.class, String.class);
+        static final MethodType MT_collectorLeading1 = methodType(String.class, int.class, String.class);
+        static final MethodType MT_collectorLeading2 = methodType(String.class, int.class, int.class, String.class);
+        static final MethodType MT_collectorLeading3 = methodType(String.class, int.class, int.class, int.class, String.class);
+
+        static final String NONE_ERROR = "zero array length in MethodHandle.asCollector";
+
+        static {
+            try {
+                MH_forSpreading = LOOKUP.findStatic(SPREAD_COLLECT, "forSpreading", MT_forSpreading);
+                MH_forCollecting = LOOKUP.findStatic(SPREAD_COLLECT, "forCollecting", MT_forCollecting);
+                MH_forCollectingLeading = LOOKUP.findStatic(SPREAD_COLLECT, "forCollectingLeading", MT_forCollectingLeading);
+            } catch (Exception e) {
+                throw new ExceptionInInitializerError(e);
+            }
+        }
+
+    }
+
+    static class FindSpecial {
+
+        interface I1 {
+            default String m() {
+                return "I1.m";
+            }
+        }
+
+        interface I2 {
+            default String m() {
+                return "I2.m";
+            }
+        }
+
+        interface I3 {
+            String q();
+        }
+
+        static class C implements I1, I2, I3 {
+            public String m() {
+                return I1.super.m();
+            }
+            public String q() {
+                return "q";
+            }
+        }
+
+        static final String ABSTRACT_ERROR = "no such method: test.java.lang.invoke.T8139885$FindSpecial$I3.q()String/invokeSpecial";
+
+    }
+
+    //
+    // Auxiliary methods.
+    //
+
+    static MethodHandle[] mha(MethodHandle... mhs) {
+        return mhs;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/invoke/findclass.security.policy	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,9 @@
+/*
+ * Security policy used by the FindClassSecurityManager test.
+ * Must allow file reads so that jtreg itself can run, and getting class loaders.
+ */
+
+grant {
+  permission java.io.FilePermission "*", "read";
+  permission java.lang.RuntimePermission "getClassLoader";
+};
--- a/jdk/test/java/net/NetworkInterface/NetworkInterfaceStreamTest.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/test/java/net/NetworkInterface/NetworkInterfaceStreamTest.java	Mon Nov 23 14:37:04 2015 -0500
@@ -24,7 +24,7 @@
 /* @test
  * @bug 8081678
  * @summary Tests for stream returning methods
- * @library ../../util/stream/bootlib
+ * @library ../../util/stream/bootlib/java.base
  * @build java.util.stream.OpTestCase
  * @run testng/othervm NetworkInterfaceStreamTest
  * @run testng/othervm -Djava.net.preferIPv4Stack=true NetworkInterfaceStreamTest
--- a/jdk/test/java/nio/file/Files/StreamLinesTest.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/test/java/nio/file/Files/StreamLinesTest.java	Mon Nov 23 14:37:04 2015 -0500
@@ -23,7 +23,7 @@
 
 /* @test
  * @bug 8072773
- * @library /lib/testlibrary/ ../../../util/stream/bootlib
+ * @library /lib/testlibrary/ ../../../util/stream/bootlib/java.base
  * @build java.util.stream.OpTestCase
  * @build jdk.testlibrary.RandomFactory
  * @run testng/othervm StreamLinesTest
--- a/jdk/test/java/security/PermissionCollection/PermissionCollectionStreamTest.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/test/java/security/PermissionCollection/PermissionCollectionStreamTest.java	Mon Nov 23 14:37:04 2015 -0500
@@ -24,7 +24,7 @@
 /* @test
  * @bug 8081678
  * @summary Tests for stream returning methods
- * @library ../../util/stream/bootlib
+ * @library ../../util/stream/bootlib/java.base
  * @build java.util.stream.OpTestCase
  * @run testng/othervm PermissionCollectionStreamTest
  */
--- a/jdk/test/java/time/tck/java/time/TCKClock_Tick.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/test/java/time/tck/java/time/TCKClock_Tick.java	Mon Nov 23 14:37:04 2015 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -160,6 +160,17 @@
     }
 
     //-----------------------------------------------------------------------
+    public void test_tickMillis_ZoneId() throws Exception {
+        Clock test = Clock.tickMillis(PARIS);
+        assertEquals(test.getZone(), PARIS);
+        assertEquals(test.instant().getNano() % 1000_000, 0);
+    }
+
+    @Test(expectedExceptions = NullPointerException.class)
+    public void test_tickMillis_ZoneId_nullZoneId() {
+        Clock.tickMillis(null);
+    }
+    //-----------------------------------------------------------------------
     public void test_tickSeconds_ZoneId() throws Exception {
         Clock test = Clock.tickSeconds(PARIS);
         assertEquals(test.getZone(), PARIS);
--- a/jdk/test/java/time/tck/java/time/TCKLocalDate.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/test/java/time/tck/java/time/TCKLocalDate.java	Mon Nov 23 14:37:04 2015 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -86,8 +86,6 @@
 import static org.testng.Assert.assertSame;
 import static org.testng.Assert.assertTrue;
 
-import java.io.ByteArrayOutputStream;
-import java.io.DataOutputStream;
 import java.time.Clock;
 import java.time.DateTimeException;
 import java.time.DayOfWeek;
@@ -104,6 +102,7 @@
 import java.time.ZoneOffset;
 import java.time.ZonedDateTime;
 import java.time.chrono.IsoChronology;
+import java.time.chrono.IsoEra;
 import java.time.format.DateTimeFormatter;
 import java.time.format.DateTimeParseException;
 import java.time.temporal.ChronoField;
@@ -135,6 +134,7 @@
 
     private static final ZoneOffset OFFSET_PONE = ZoneOffset.ofHours(1);
     private static final ZoneOffset OFFSET_PTWO = ZoneOffset.ofHours(2);
+    private static final ZoneOffset OFFSET_MTWO = ZoneOffset.ofHours(-2);
     private static final ZoneId ZONE_PARIS = ZoneId.of("Europe/Paris");
     private static final ZoneId ZONE_GAZA = ZoneId.of("Asia/Gaza");
 
@@ -475,6 +475,48 @@
         return date.withDayOfMonth(date.getMonth().length(isIsoLeap(date.getYear())));
     }
 
+     //-----------------------------------------------------------------------
+     // ofInstant()
+     //-----------------------------------------------------------------------
+     @DataProvider(name="instantFactory")
+     Object[][] data_instantFactory() {
+         return new Object[][] {
+                 {Instant.ofEpochSecond(86400 + 3600 + 120 + 4, 500), ZONE_PARIS, LocalDate.of(1970, 1, 2)},
+                 {Instant.ofEpochSecond(86400 + 3600 + 120 + 4, 500), OFFSET_MTWO, LocalDate.of(1970, 1, 1)},
+                 {Instant.ofEpochSecond(-86400 + 4, 500), OFFSET_PTWO, LocalDate.of(1969, 12, 31)},
+                 {OffsetDateTime.of(LocalDateTime.of(Year.MIN_VALUE, 1, 1, 0, 0), ZoneOffset.UTC).toInstant(),
+                         ZoneOffset.UTC, LocalDate.MIN},
+                 {OffsetDateTime.of(LocalDateTime.of(Year.MAX_VALUE, 12, 31, 23, 59, 59, 999_999_999), ZoneOffset.UTC).toInstant(),
+                         ZoneOffset.UTC, LocalDate.MAX},
+         };
+     }
+
+     @Test(dataProvider="instantFactory")
+     public void factory_ofInstant(Instant instant, ZoneId zone, LocalDate expected) {
+         LocalDate test = LocalDate.ofInstant(instant, zone);
+         assertEquals(test, expected);
+     }
+
+     @Test(expectedExceptions=DateTimeException.class)
+     public void factory_ofInstant_instantTooBig() {
+         LocalDate.ofInstant(Instant.MAX, OFFSET_PONE);
+     }
+
+     @Test(expectedExceptions=DateTimeException.class)
+     public void factory_ofInstant_instantTooSmall() {
+         LocalDate.ofInstant(Instant.MIN, OFFSET_PONE);
+     }
+
+     @Test(expectedExceptions=NullPointerException.class)
+     public void factory_ofInstant_nullInstant() {
+         LocalDate.ofInstant((Instant) null, ZONE_GAZA);
+     }
+
+     @Test(expectedExceptions=NullPointerException.class)
+     public void factory_ofInstant_nullZone() {
+         LocalDate.ofInstant(Instant.EPOCH, (ZoneId) null);
+     }
+
     //-----------------------------------------------------------------------
     // ofEpochDay()
     //-----------------------------------------------------------------------
@@ -2285,4 +2327,13 @@
         return LocalDate.of(year, month, day);
     }
 
+    //-----------------------------------------------------------------
+    // getEra()
+    // ----------------------------------------------------------------
+    @Test
+    public void test_getEra() {
+        IsoEra isoEra = LocalDate.MAX.getEra();
+        assertSame(isoEra,IsoEra.CE);
+        assertSame(LocalDate.MIN.getEra(),IsoEra.BCE);
+    }
 }
--- a/jdk/test/java/time/tck/java/time/TCKLocalTime.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/test/java/time/tck/java/time/TCKLocalTime.java	Mon Nov 23 14:37:04 2015 -0500
@@ -102,6 +102,7 @@
 import java.time.OffsetDateTime;
 import java.time.OffsetTime;
 import java.time.Period;
+import java.time.Year;
 import java.time.ZoneId;
 import java.time.ZoneOffset;
 import java.time.ZonedDateTime;
@@ -137,6 +138,7 @@
 public class TCKLocalTime extends AbstractDateTimeTest {
 
     private static final ZoneOffset OFFSET_PTWO = ZoneOffset.ofHours(2);
+    private static final ZoneOffset OFFSET_MTWO = ZoneOffset.ofHours(-2);
     private static final ZoneId ZONE_PARIS = ZoneId.of("Europe/Paris");
 
     private LocalTime TEST_12_30_40_987654321;
@@ -420,6 +422,38 @@
         LocalTime.of(0, 0, 0, 1000000000);
     }
 
+     //-----------------------------------------------------------------------
+     // ofInstant()
+     //-----------------------------------------------------------------------
+     @DataProvider(name="instantFactory")
+     Object[][] data_instantFactory() {
+         return new Object[][] {
+                 {Instant.ofEpochSecond(86400 + 3600 + 120 + 4, 500), ZONE_PARIS, LocalTime.of(2, 2, 4, 500)},
+                 {Instant.ofEpochSecond(86400 + 3600 + 120 + 4, 500), OFFSET_MTWO, LocalTime.of(23, 2, 4, 500)},
+                 {Instant.ofEpochSecond(-86400 + 4, 500), OFFSET_PTWO, LocalTime.of(2, 0, 4, 500)},
+                 {OffsetDateTime.of(LocalDateTime.of(Year.MIN_VALUE, 1, 1, 0, 0), ZoneOffset.UTC).toInstant(),
+                         ZoneOffset.UTC, LocalTime.MIN},
+                 {OffsetDateTime.of(LocalDateTime.of(Year.MAX_VALUE, 12, 31, 23, 59, 59, 999_999_999), ZoneOffset.UTC).toInstant(),
+                         ZoneOffset.UTC, LocalTime.MAX},
+         };
+     }
+
+     @Test(dataProvider="instantFactory")
+     public void factory_ofInstant(Instant instant, ZoneId zone, LocalTime expected) {
+         LocalTime test = LocalTime.ofInstant(instant, zone);
+         assertEquals(test, expected);
+     }
+
+     @Test(expectedExceptions=NullPointerException.class)
+     public void factory_ofInstant_nullInstant() {
+         LocalTime.ofInstant((Instant) null, ZONE_PARIS);
+     }
+
+     @Test(expectedExceptions=NullPointerException.class)
+     public void factory_ofInstant_nullZone() {
+         LocalTime.ofInstant(Instant.EPOCH, (ZoneId) null);
+     }
+
     //-----------------------------------------------------------------------
     // ofSecondOfDay(long)
     //-----------------------------------------------------------------------
--- a/jdk/test/java/util/Arrays/ArraysEqCmpTest.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/test/java/util/Arrays/ArraysEqCmpTest.java	Mon Nov 23 14:37:04 2015 -0500
@@ -23,7 +23,7 @@
 
 /*
  * @test
- * @bug 8033148
+ * @bug 8033148 8141409
  * @summary tests for array equals and compare
  * @run testng ArraysEqCmpTest
 */
@@ -312,6 +312,8 @@
                 return Integer.compare(b, a);
             };
 
+            final MethodHandle eqc;
+            final MethodHandle eqcr;
             final MethodHandle cmpc;
             final MethodHandle cmpcr;
             final MethodHandle mismatchc;
@@ -327,6 +329,8 @@
                             int.class, Object[].class, int.class, int.class,
                             Object[].class, int.class, int.class, Comparator.class);
 
+                    eqc = l.findStatic(Arrays.class, "equals", cmpt.changeReturnType(boolean.class));
+                    eqcr = l.findStatic(Arrays.class, "equals", cmprt.changeReturnType(boolean.class));
                     cmpc = l.findStatic(Arrays.class, "compare", cmpt);
                     cmpcr = l.findStatic(Arrays.class, "compare", cmprt);
                     mismatchc = l.findStatic(Arrays.class, "mismatch", cmpt);
@@ -338,6 +342,33 @@
             }
 
             @Override
+            boolean equals(Object a, Object b) {
+                try {
+                    return (boolean) eqc.invoke(a, b, c);
+                }
+                catch (RuntimeException | Error e) {
+                    throw e;
+                }
+                catch (Throwable t) {
+                    throw new Error(t);
+                }
+            }
+
+            @Override
+            boolean equals(Object a, int aFromIndex, int aToIndex,
+                           Object b, int bFromIndex, int bToIndex) {
+                try {
+                    return (boolean) eqcr.invoke(a, aFromIndex, aToIndex, b, bFromIndex, bToIndex, c);
+                }
+                catch (RuntimeException | Error e) {
+                    throw e;
+                }
+                catch (Throwable t) {
+                    throw new Error(t);
+                }
+            }
+
+            @Override
             int compare(Object a, Object b) {
                 try {
                     return (int) cmpc.invoke(a, b, c);
@@ -1002,10 +1033,12 @@
                         continue;
 
                     if (o3 == null) {
+                        testNPE(() -> Arrays.equals(o1, o2, o3));
                         testNPE(() -> Arrays.compare(o1, o2, o3));
                         testNPE(() -> Arrays.mismatch(o1, o2, o3));
                     }
 
+                    testNPE(() -> Arrays.equals(o1, 0, 0, o2, 0, 0, o3));
                     testNPE(() -> Arrays.compare(o1, 0, 0, o2, 0, 0, o3));
                     testNPE(() -> Arrays.mismatch(o1, 0, 0, o2, 0, 0, o3));
                 }
--- a/jdk/test/java/util/Objects/CheckIndex.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/test/java/util/Objects/CheckIndex.java	Mon Nov 23 14:37:04 2015 -0500
@@ -25,7 +25,7 @@
  * @test
  * @summary IndexOutOfBoundsException check index tests
  * @run testng CheckIndex
- * @bug 8135248
+ * @bug 8135248 8142493
  */
 
 import org.testng.annotations.DataProvider;
@@ -54,6 +54,15 @@
         };
     }
 
+    static BiFunction<Integer, Integer, AssertingOutOfBoundsException> assertingOutOfBoundsReturnNull(
+            int expFromIndex, int expToIndexOrSizeOrLength) {
+        return (fromIndex, toIndexOrSizeorLength) -> {
+            assertEquals(fromIndex, Integer.valueOf(expFromIndex));
+            assertEquals(toIndexOrSizeorLength, Integer.valueOf(expToIndexOrSizeOrLength));
+            return null;
+        };
+    }
+
     static final int[] VALUES = {0, 1, Integer.MAX_VALUE - 1, Integer.MAX_VALUE, -1, Integer.MIN_VALUE + 1, Integer.MIN_VALUE};
 
     @DataProvider
@@ -95,6 +104,8 @@
         check.accept(AssertingOutOfBoundsException.class,
                      () -> Objects.checkIndex(index, length, assertingOutOfBounds(index, length)));
         check.accept(IndexOutOfBoundsException.class,
+                     () -> Objects.checkIndex(index, length, assertingOutOfBoundsReturnNull(index, length)));
+        check.accept(IndexOutOfBoundsException.class,
                      () -> Objects.checkIndex(index, length, null));
         check.accept(IndexOutOfBoundsException.class,
                      () -> Objects.checkIndex(index, length));
@@ -140,6 +151,8 @@
         check.accept(AssertingOutOfBoundsException.class,
                      () -> Objects.checkFromToIndex(fromIndex, toIndex, length, assertingOutOfBounds(fromIndex, toIndex)));
         check.accept(IndexOutOfBoundsException.class,
+                     () -> Objects.checkFromToIndex(fromIndex, toIndex, length, assertingOutOfBoundsReturnNull(fromIndex, toIndex)));
+        check.accept(IndexOutOfBoundsException.class,
                      () -> Objects.checkFromToIndex(fromIndex, toIndex, length, null));
         check.accept(IndexOutOfBoundsException.class,
                      () -> Objects.checkFromToIndex(fromIndex, toIndex, length));
@@ -192,6 +205,8 @@
         check.accept(AssertingOutOfBoundsException.class,
                      () -> Objects.checkFromIndexSize(fromIndex, size, length, assertingOutOfBounds(fromIndex, size)));
         check.accept(IndexOutOfBoundsException.class,
+                     () -> Objects.checkFromIndexSize(fromIndex, size, length, assertingOutOfBoundsReturnNull(fromIndex, size)));
+        check.accept(IndexOutOfBoundsException.class,
                      () -> Objects.checkFromIndexSize(fromIndex, size, length, null));
         check.accept(IndexOutOfBoundsException.class,
                      () -> Objects.checkFromIndexSize(fromIndex, size, length));
--- a/jdk/test/java/util/Scanner/ScannerStreamTest.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/test/java/util/Scanner/ScannerStreamTest.java	Mon Nov 23 14:37:04 2015 -0500
@@ -46,7 +46,7 @@
  * @test
  * @bug 8072722
  * @summary Tests of stream support in java.util.Scanner
- * @library ../stream/bootlib
+ * @library ../stream/bootlib/java.base
  * @build java.util.stream.OpTestCase
  * @run testng/othervm ScannerStreamTest
  */
--- a/jdk/test/java/util/logging/LoggerSubclass.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/test/java/util/logging/LoggerSubclass.java	Mon Nov 23 14:37:04 2015 -0500
@@ -92,7 +92,7 @@
         else fail(x + " not equal to " + y);}
     public static void main(String[] args) throws Throwable {
         try {new LoggerSubclass().instanceMain(args);}
-        catch (Throwable e) {throw e.getCause();}}
+        catch (Throwable e) {throw e.getCause() == null ? e : e.getCause();}}
     public void instanceMain(String[] args) throws Throwable {
         try {test(args);} catch (Throwable t) {unexpected(t);}
         System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
--- a/jdk/test/java/util/regex/PatternStreamTest.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/test/java/util/regex/PatternStreamTest.java	Mon Nov 23 14:37:04 2015 -0500
@@ -25,7 +25,7 @@
  * @test
  * @bug 8016846 8024341 8071479
  * @summary Unit tests stream and lambda-based methods on Pattern and Matcher
- * @library ../stream/bootlib
+ * @library ../stream/bootlib/java.base
  * @build java.util.stream.OpTestCase
  * @run testng/othervm PatternStreamTest
  */
--- a/jdk/test/java/util/stream/bootlib/TEST.properties	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/test/java/util/stream/bootlib/TEST.properties	Mon Nov 23 14:37:04 2015 -0500
@@ -1,3 +1,3 @@
 # This file identifies root(s) of the test-ng hierarchy.
 
-bootclasspath.dirs = .
+bootclasspath.dirs = java.base
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/CollectorOps.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,113 @@
+/*
+ * 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 java.util.stream;
+
+import org.testng.Assert;
+
+import java.util.Spliterator;
+import java.util.function.IntFunction;
+
+/** Test helper class for java.util.stream test framework */
+public final class CollectorOps {
+    private CollectorOps() { }
+
+    public static <E_IN> StatefulTestOp<E_IN> collector() {
+        return new StatefulCollector<>(0, StreamShape.REFERENCE);
+    }
+
+    /* Utility classes for collecting output of intermediate pipeline stages */
+    public static class StatefulCollector<E_IN> implements StatefulTestOp<E_IN> {
+        private final int opFlags;
+        private final StreamShape inputShape;
+
+        public StatefulCollector(int opFlags, StreamShape inputShape) {
+            this.opFlags = opFlags;
+            this.inputShape = inputShape;
+        }
+
+        @Override
+        public StreamShape inputShape() {
+            return inputShape;
+        }
+
+        @Override
+        public StreamShape outputShape() {
+            return inputShape;
+        }
+
+        @Override
+        public int opGetFlags() {
+            return opFlags;
+        }
+
+        @Override
+        public Sink<E_IN> opWrapSink(int flags, boolean parallel, Sink<E_IN> sink) {
+            return sink;
+        }
+
+        @Override
+        public <P_IN> Node<E_IN> opEvaluateParallel(PipelineHelper<E_IN> helper,
+                                                    Spliterator<P_IN> spliterator,
+                                                    IntFunction<E_IN[]> generator) {
+            return helper.evaluate(spliterator, false, generator);
+        }
+    }
+
+    public static class TestParallelSizedOp<T> extends StatefulCollector<T> {
+        public TestParallelSizedOp() {
+            this(StreamShape.REFERENCE);
+        }
+
+        protected TestParallelSizedOp(StreamShape shape) {
+            super(0, shape);
+        }
+
+        @Override
+        public <P_IN> Node<T> opEvaluateParallel(PipelineHelper<T> helper,
+                                                 Spliterator<P_IN> spliterator,
+                                                 IntFunction<T[]> generator) {
+            int flags = helper.getStreamAndOpFlags();
+
+            Assert.assertTrue(StreamOpFlag.SIZED.isKnown(flags));
+            return super.opEvaluateParallel(helper, spliterator, generator);
+        }
+
+        public static class OfInt extends TestParallelSizedOp<Integer> {
+            public OfInt() {
+                super(StreamShape.INT_VALUE);
+            }
+        }
+
+        public static class OfLong extends TestParallelSizedOp<Long> {
+            public OfLong() {
+                super(StreamShape.LONG_VALUE);
+            }
+        }
+
+        public static class OfDouble extends TestParallelSizedOp<Double> {
+            public OfDouble() {
+                super(StreamShape.DOUBLE_VALUE);
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/DefaultMethodStreams.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,984 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package java.util.stream;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Comparator;
+import java.util.DoubleSummaryStatistics;
+import java.util.IntSummaryStatistics;
+import java.util.Iterator;
+import java.util.LongSummaryStatistics;
+import java.util.Optional;
+import java.util.OptionalDouble;
+import java.util.OptionalInt;
+import java.util.OptionalLong;
+import java.util.PrimitiveIterator;
+import java.util.Set;
+import java.util.Spliterator;
+import java.util.function.BiConsumer;
+import java.util.function.BiFunction;
+import java.util.function.BinaryOperator;
+import java.util.function.Consumer;
+import java.util.function.DoubleBinaryOperator;
+import java.util.function.DoubleConsumer;
+import java.util.function.DoubleFunction;
+import java.util.function.DoublePredicate;
+import java.util.function.DoubleToIntFunction;
+import java.util.function.DoubleToLongFunction;
+import java.util.function.DoubleUnaryOperator;
+import java.util.function.Function;
+import java.util.function.IntBinaryOperator;
+import java.util.function.IntConsumer;
+import java.util.function.IntFunction;
+import java.util.function.IntPredicate;
+import java.util.function.IntToDoubleFunction;
+import java.util.function.IntToLongFunction;
+import java.util.function.IntUnaryOperator;
+import java.util.function.LongBinaryOperator;
+import java.util.function.LongConsumer;
+import java.util.function.LongFunction;
+import java.util.function.LongPredicate;
+import java.util.function.LongToDoubleFunction;
+import java.util.function.LongToIntFunction;
+import java.util.function.LongUnaryOperator;
+import java.util.function.ObjDoubleConsumer;
+import java.util.function.ObjIntConsumer;
+import java.util.function.ObjLongConsumer;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+import java.util.function.ToDoubleFunction;
+
+import java.util.function.ToIntFunction;
+import java.util.function.ToLongFunction;
+
+import static java.util.stream.Collectors.*;
+
+public final class DefaultMethodStreams {
+
+    static {
+        // Verify that default methods are not overridden
+        verify(DefaultMethodRefStream.class);
+        verify(DefaultMethodIntStream.class);
+        verify(DefaultMethodLongStream.class);
+        verify(DefaultMethodDoubleStream.class);
+    }
+
+    static void verify(Class<?> del) {
+        // Find the stream interface
+        Class<?> s = Stream.of(del.getInterfaces())
+                .filter(c -> BaseStream.class.isAssignableFrom(c))
+                .findFirst().get();
+
+        // Get all default methods on the stream class
+        Set<String> dms = Stream.of(s.getMethods())
+                .filter(m -> !Modifier.isStatic(m.getModifiers()))
+                .filter(m -> !m.isBridge())
+                .filter(Method::isDefault)
+                .map(Method::getName)
+                .collect(toSet());
+
+        // Get all methods on the delegating class
+        Set<String> ims = Stream.of(del.getMethods())
+                .filter(m -> !Modifier.isStatic(m.getModifiers()))
+                .filter(m -> m.getDeclaringClass() == del)
+                .map(Method::getName)
+                .collect(toSet());
+
+        if (ims.stream().anyMatch(dms::contains)) {
+            throw new AssertionError(String.format("%s overrides default methods of %s\n", del, s));
+        }
+    }
+
+    /**
+     * Creates a stream that for the next operation either delegates to
+     * a default method on {@link Stream}, if present for that operation,
+     * otherwise delegates to an underlying stream.
+     *
+     * @param s the underlying stream to be delegated to for non-default
+     * methods.
+     * @param <T> the type of the stream elements
+     * @return the delegating stream
+     */
+    public static <T> Stream<T> delegateTo(Stream<T> s) {
+        return new DefaultMethodRefStream<>(s);
+    }
+
+    /**
+     * Creates a stream that for the next operation either delegates to
+     * a default method on {@link IntStream}, if present for that operation,
+     * otherwise delegates to an underlying stream.
+     *
+     * @param s the underlying stream to be delegated to for non-default
+     * methods.
+     * @return the delegating stream
+     */
+    public static IntStream delegateTo(IntStream s) {
+        return new DefaultMethodIntStream(s);
+    }
+
+    /**
+     * Creates a stream that for the next operation either delegates to
+     * a default method on {@link LongStream}, if present for that operation,
+     * otherwise delegates to an underlying stream.
+     *
+     * @param s the underlying stream to be delegated to for non-default
+     * methods.
+     * @return the delegating stream
+     */
+    public static LongStream delegateTo(LongStream s) {
+        return new DefaultMethodLongStream(s);
+    }
+
+    /**
+     * Creates a stream that for the next operation either delegates to
+     * a default method on {@link DoubleStream}, if present for that operation,
+     * otherwise delegates to an underlying stream.
+     *
+     * @param s the underlying stream to be delegated to for non-default
+     * methods.
+     * @return the delegating stream
+     */
+    public static DoubleStream delegateTo(DoubleStream s) {
+        return new DefaultMethodDoubleStream(s);
+    }
+
+    /**
+     * A stream that delegates the next operation to a default method, if
+     * present, or to the same operation of an underlying stream.
+     *
+     * @param <T> the type of the stream elements
+     */
+    static final class DefaultMethodRefStream<T> implements Stream<T> {
+        final Stream<T> s;
+
+        DefaultMethodRefStream(Stream<T> s) {
+            this.s = s;
+        }
+
+
+        // Delegating non-default methods
+
+        @Override
+        public Stream<T> filter(Predicate<? super T> predicate) {
+            return s.filter(predicate);
+        }
+
+        @Override
+        public <R> Stream<R> map(Function<? super T, ? extends R> mapper) {
+            return s.map(mapper);
+        }
+
+        @Override
+        public IntStream mapToInt(ToIntFunction<? super T> mapper) {
+            return s.mapToInt(mapper);
+        }
+
+        @Override
+        public LongStream mapToLong(ToLongFunction<? super T> mapper) {
+            return s.mapToLong(mapper);
+        }
+
+        @Override
+        public DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper) {
+            return s.mapToDouble(mapper);
+        }
+
+        @Override
+        public <R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper) {
+            return s.flatMap(mapper);
+        }
+
+        @Override
+        public IntStream flatMapToInt(Function<? super T, ? extends IntStream> mapper) {
+            return s.flatMapToInt(mapper);
+        }
+
+        @Override
+        public LongStream flatMapToLong(Function<? super T, ? extends LongStream> mapper) {
+            return s.flatMapToLong(mapper);
+        }
+
+        @Override
+        public DoubleStream flatMapToDouble(Function<? super T, ? extends DoubleStream> mapper) {
+            return s.flatMapToDouble(mapper);
+        }
+
+        @Override
+        public Stream<T> distinct() {
+            return s.distinct();
+        }
+
+        @Override
+        public Stream<T> sorted() {
+            return s.sorted();
+        }
+
+        @Override
+        public Stream<T> sorted(Comparator<? super T> comparator) {
+            return s.sorted(comparator);
+        }
+
+        @Override
+        public Stream<T> peek(Consumer<? super T> action) {
+            return s.peek(action);
+        }
+
+        @Override
+        public Stream<T> limit(long maxSize) {
+            return s.limit(maxSize);
+        }
+
+        @Override
+        public Stream<T> skip(long n) {
+            return s.skip(n);
+        }
+
+        @Override
+        public void forEach(Consumer<? super T> action) {
+            s.forEach(action);
+        }
+
+        @Override
+        public void forEachOrdered(Consumer<? super T> action) {
+            s.forEachOrdered(action);
+        }
+
+        @Override
+        public Object[] toArray() {
+            return s.toArray();
+        }
+
+        @Override
+        public <A> A[] toArray(IntFunction<A[]> generator) {
+            return s.toArray(generator);
+        }
+
+        @Override
+        public T reduce(T identity, BinaryOperator<T> accumulator) {
+            return s.reduce(identity, accumulator);
+        }
+
+        @Override
+        public Optional<T> reduce(BinaryOperator<T> accumulator) {
+            return s.reduce(accumulator);
+        }
+
+        @Override
+        public <U> U reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner) {
+            return s.reduce(identity, accumulator, combiner);
+        }
+
+        @Override
+        public <R> R collect(Supplier<R> supplier, BiConsumer<R, ? super T> accumulator, BiConsumer<R, R> combiner) {
+            return s.collect(supplier, accumulator, combiner);
+        }
+
+        @Override
+        public <R, A> R collect(Collector<? super T, A, R> collector) {
+            return s.collect(collector);
+        }
+
+        @Override
+        public Optional<T> min(Comparator<? super T> comparator) {
+            return s.min(comparator);
+        }
+
+        @Override
+        public Optional<T> max(Comparator<? super T> comparator) {
+            return s.max(comparator);
+        }
+
+        @Override
+        public long count() {
+            return s.count();
+        }
+
+        @Override
+        public boolean anyMatch(Predicate<? super T> predicate) {
+            return s.anyMatch(predicate);
+        }
+
+        @Override
+        public boolean allMatch(Predicate<? super T> predicate) {
+            return s.allMatch(predicate);
+        }
+
+        @Override
+        public boolean noneMatch(Predicate<? super T> predicate) {
+            return s.noneMatch(predicate);
+        }
+
+        @Override
+        public Optional<T> findFirst() {
+            return s.findFirst();
+        }
+
+        @Override
+        public Optional<T> findAny() {
+            return s.findAny();
+        }
+
+        @Override
+        public Iterator<T> iterator() {
+            return s.iterator();
+        }
+
+        @Override
+        public Spliterator<T> spliterator() {
+            return s.spliterator();
+        }
+
+        @Override
+        public boolean isParallel() {
+            return s.isParallel();
+        }
+
+        @Override
+        public Stream<T> sequential() {
+            return s.sequential();
+        }
+
+        @Override
+        public Stream<T> parallel() {
+            return s.parallel();
+        }
+
+        @Override
+        public Stream<T> unordered() {
+            return s.unordered();
+        }
+
+        @Override
+        public Stream<T> onClose(Runnable closeHandler) {
+            return s.onClose(closeHandler);
+        }
+
+        @Override
+        public void close() {
+            s.close();
+        }
+    }
+
+    static final class DefaultMethodIntStream implements IntStream {
+        final IntStream s;
+
+        public DefaultMethodIntStream(IntStream s) {
+            this.s = s;
+        }
+
+
+        // Delegating non-default methods
+
+        @Override
+        public IntStream filter(IntPredicate predicate) {
+            return s.filter(predicate);
+        }
+
+        @Override
+        public IntStream map(IntUnaryOperator mapper) {
+            return s.map(mapper);
+        }
+
+        @Override
+        public <U> Stream<U> mapToObj(IntFunction<? extends U> mapper) {
+            return s.mapToObj(mapper);
+        }
+
+        @Override
+        public LongStream mapToLong(IntToLongFunction mapper) {
+            return s.mapToLong(mapper);
+        }
+
+        @Override
+        public DoubleStream mapToDouble(IntToDoubleFunction mapper) {
+            return s.mapToDouble(mapper);
+        }
+
+        @Override
+        public IntStream flatMap(IntFunction<? extends IntStream> mapper) {
+            return s.flatMap(mapper);
+        }
+
+        @Override
+        public IntStream distinct() {
+            return s.distinct();
+        }
+
+        @Override
+        public IntStream sorted() {
+            return s.sorted();
+        }
+
+        @Override
+        public IntStream peek(IntConsumer action) {
+            return s.peek(action);
+        }
+
+        @Override
+        public IntStream limit(long maxSize) {
+            return s.limit(maxSize);
+        }
+
+        @Override
+        public IntStream skip(long n) {
+            return s.skip(n);
+        }
+
+        @Override
+        public void forEach(IntConsumer action) {
+            s.forEach(action);
+        }
+
+        @Override
+        public void forEachOrdered(IntConsumer action) {
+            s.forEachOrdered(action);
+        }
+
+        @Override
+        public int[] toArray() {
+            return s.toArray();
+        }
+
+        @Override
+        public int reduce(int identity, IntBinaryOperator op) {
+            return s.reduce(identity, op);
+        }
+
+        @Override
+        public OptionalInt reduce(IntBinaryOperator op) {
+            return s.reduce(op);
+        }
+
+        @Override
+        public <R> R collect(Supplier<R> supplier, ObjIntConsumer<R> accumulator, BiConsumer<R, R> combiner) {
+            return s.collect(supplier, accumulator, combiner);
+        }
+
+        @Override
+        public int sum() {
+            return s.sum();
+        }
+
+        @Override
+        public OptionalInt min() {
+            return s.min();
+        }
+
+        @Override
+        public OptionalInt max() {
+            return s.max();
+        }
+
+        @Override
+        public long count() {
+            return s.count();
+        }
+
+        @Override
+        public OptionalDouble average() {
+            return s.average();
+        }
+
+        @Override
+        public IntSummaryStatistics summaryStatistics() {
+            return s.summaryStatistics();
+        }
+
+        @Override
+        public boolean anyMatch(IntPredicate predicate) {
+            return s.anyMatch(predicate);
+        }
+
+        @Override
+        public boolean allMatch(IntPredicate predicate) {
+            return s.allMatch(predicate);
+        }
+
+        @Override
+        public boolean noneMatch(IntPredicate predicate) {
+            return s.noneMatch(predicate);
+        }
+
+        @Override
+        public OptionalInt findFirst() {
+            return s.findFirst();
+        }
+
+        @Override
+        public OptionalInt findAny() {
+            return s.findAny();
+        }
+
+        @Override
+        public LongStream asLongStream() {
+            return s.asLongStream();
+        }
+
+        @Override
+        public DoubleStream asDoubleStream() {
+            return s.asDoubleStream();
+        }
+
+        @Override
+        public Stream<Integer> boxed() {
+            return s.boxed();
+        }
+
+        @Override
+        public IntStream sequential() {
+            return s.sequential();
+        }
+
+        @Override
+        public IntStream parallel() {
+            return s.parallel();
+        }
+
+        @Override
+        public PrimitiveIterator.OfInt iterator() {
+            return s.iterator();
+        }
+
+        @Override
+        public Spliterator.OfInt spliterator() {
+            return s.spliterator();
+        }
+
+        @Override
+        public boolean isParallel() {
+            return s.isParallel();
+        }
+
+        @Override
+        public IntStream unordered() {
+            return s.unordered();
+        }
+
+        @Override
+        public IntStream onClose(Runnable closeHandler) {
+            return s.onClose(closeHandler);
+        }
+
+        @Override
+        public void close() {
+            s.close();
+        }
+    }
+
+    static final class DefaultMethodLongStream implements LongStream {
+        final LongStream s;
+
+        public DefaultMethodLongStream(LongStream s) {
+            this.s = s;
+        }
+
+
+        // Delegating non-default methods
+
+        @Override
+        public void forEach(LongConsumer action) {
+            s.forEach(action);
+        }
+
+        @Override
+        public LongStream filter(LongPredicate predicate) {
+            return s.filter(predicate);
+        }
+
+        @Override
+        public LongStream map(LongUnaryOperator mapper) {
+            return s.map(mapper);
+        }
+
+        @Override
+        public <U> Stream<U> mapToObj(LongFunction<? extends U> mapper) {
+            return s.mapToObj(mapper);
+        }
+
+        @Override
+        public IntStream mapToInt(LongToIntFunction mapper) {
+            return s.mapToInt(mapper);
+        }
+
+        @Override
+        public DoubleStream mapToDouble(LongToDoubleFunction mapper) {
+            return s.mapToDouble(mapper);
+        }
+
+        @Override
+        public LongStream flatMap(LongFunction<? extends LongStream> mapper) {
+            return s.flatMap(mapper);
+        }
+
+        @Override
+        public LongStream distinct() {
+            return s.distinct();
+        }
+
+        @Override
+        public LongStream sorted() {
+            return s.sorted();
+        }
+
+        @Override
+        public LongStream peek(LongConsumer action) {
+            return s.peek(action);
+        }
+
+        @Override
+        public LongStream limit(long maxSize) {
+            return s.limit(maxSize);
+        }
+
+        @Override
+        public LongStream skip(long n) {
+            return s.skip(n);
+        }
+
+        @Override
+        public void forEachOrdered(LongConsumer action) {
+            s.forEachOrdered(action);
+        }
+
+        @Override
+        public long[] toArray() {
+            return s.toArray();
+        }
+
+        @Override
+        public long reduce(long identity, LongBinaryOperator op) {
+            return s.reduce(identity, op);
+        }
+
+        @Override
+        public OptionalLong reduce(LongBinaryOperator op) {
+            return s.reduce(op);
+        }
+
+        @Override
+        public <R> R collect(Supplier<R> supplier, ObjLongConsumer<R> accumulator, BiConsumer<R, R> combiner) {
+            return s.collect(supplier, accumulator, combiner);
+        }
+
+        @Override
+        public long sum() {
+            return s.sum();
+        }
+
+        @Override
+        public OptionalLong min() {
+            return s.min();
+        }
+
+        @Override
+        public OptionalLong max() {
+            return s.max();
+        }
+
+        @Override
+        public long count() {
+            return s.count();
+        }
+
+        @Override
+        public OptionalDouble average() {
+            return s.average();
+        }
+
+        @Override
+        public LongSummaryStatistics summaryStatistics() {
+            return s.summaryStatistics();
+        }
+
+        @Override
+        public boolean anyMatch(LongPredicate predicate) {
+            return s.anyMatch(predicate);
+        }
+
+        @Override
+        public boolean allMatch(LongPredicate predicate) {
+            return s.allMatch(predicate);
+        }
+
+        @Override
+        public boolean noneMatch(LongPredicate predicate) {
+            return s.noneMatch(predicate);
+        }
+
+        @Override
+        public OptionalLong findFirst() {
+            return s.findFirst();
+        }
+
+        @Override
+        public OptionalLong findAny() {
+            return s.findAny();
+        }
+
+        @Override
+        public DoubleStream asDoubleStream() {
+            return s.asDoubleStream();
+        }
+
+        @Override
+        public Stream<Long> boxed() {
+            return s.boxed();
+        }
+
+        @Override
+        public LongStream sequential() {
+            return s.sequential();
+        }
+
+        @Override
+        public LongStream parallel() {
+            return s.parallel();
+        }
+
+        @Override
+        public PrimitiveIterator.OfLong iterator() {
+            return s.iterator();
+        }
+
+        @Override
+        public Spliterator.OfLong spliterator() {
+            return s.spliterator();
+        }
+
+        @Override
+        public boolean isParallel() {
+            return s.isParallel();
+        }
+
+        @Override
+        public LongStream unordered() {
+            return s.unordered();
+        }
+
+        @Override
+        public LongStream onClose(Runnable closeHandler) {
+            return s.onClose(closeHandler);
+        }
+
+        @Override
+        public void close() {
+            s.close();
+        }
+    }
+
+    static final class DefaultMethodDoubleStream implements DoubleStream {
+        final DoubleStream s;
+
+        public DefaultMethodDoubleStream(DoubleStream s) {
+            this.s = s;
+        }
+
+        @Override
+        public DoubleStream filter(DoublePredicate predicate) {
+            return s.filter(predicate);
+        }
+
+        @Override
+        public DoubleStream map(DoubleUnaryOperator mapper) {
+            return s.map(mapper);
+        }
+
+        @Override
+        public <U> Stream<U> mapToObj(DoubleFunction<? extends U> mapper) {
+            return s.mapToObj(mapper);
+        }
+
+        @Override
+        public IntStream mapToInt(DoubleToIntFunction mapper) {
+            return s.mapToInt(mapper);
+        }
+
+        @Override
+        public LongStream mapToLong(DoubleToLongFunction mapper) {
+            return s.mapToLong(mapper);
+        }
+
+        @Override
+        public DoubleStream flatMap(DoubleFunction<? extends DoubleStream> mapper) {
+            return s.flatMap(mapper);
+        }
+
+        @Override
+        public DoubleStream distinct() {
+            return s.distinct();
+        }
+
+        @Override
+        public DoubleStream sorted() {
+            return s.sorted();
+        }
+
+        @Override
+        public DoubleStream peek(DoubleConsumer action) {
+            return s.peek(action);
+        }
+
+        @Override
+        public DoubleStream limit(long maxSize) {
+            return s.limit(maxSize);
+        }
+
+        @Override
+        public DoubleStream skip(long n) {
+            return s.skip(n);
+        }
+
+        @Override
+        public void forEach(DoubleConsumer action) {
+            s.forEach(action);
+        }
+
+        @Override
+        public void forEachOrdered(DoubleConsumer action) {
+            s.forEachOrdered(action);
+        }
+
+        @Override
+        public double[] toArray() {
+            return s.toArray();
+        }
+
+        @Override
+        public double reduce(double identity, DoubleBinaryOperator op) {
+            return s.reduce(identity, op);
+        }
+
+        @Override
+        public OptionalDouble reduce(DoubleBinaryOperator op) {
+            return s.reduce(op);
+        }
+
+        @Override
+        public <R> R collect(Supplier<R> supplier, ObjDoubleConsumer<R> accumulator, BiConsumer<R, R> combiner) {
+            return s.collect(supplier, accumulator, combiner);
+        }
+
+        @Override
+        public double sum() {
+            return s.sum();
+        }
+
+        @Override
+        public OptionalDouble min() {
+            return s.min();
+        }
+
+        @Override
+        public OptionalDouble max() {
+            return s.max();
+        }
+
+        @Override
+        public long count() {
+            return s.count();
+        }
+
+        @Override
+        public OptionalDouble average() {
+            return s.average();
+        }
+
+        @Override
+        public DoubleSummaryStatistics summaryStatistics() {
+            return s.summaryStatistics();
+        }
+
+        @Override
+        public boolean anyMatch(DoublePredicate predicate) {
+            return s.anyMatch(predicate);
+        }
+
+        @Override
+        public boolean allMatch(DoublePredicate predicate) {
+            return s.allMatch(predicate);
+        }
+
+        @Override
+        public boolean noneMatch(DoublePredicate predicate) {
+            return s.noneMatch(predicate);
+        }
+
+        @Override
+        public OptionalDouble findFirst() {
+            return s.findFirst();
+        }
+
+        @Override
+        public OptionalDouble findAny() {
+            return s.findAny();
+        }
+
+        @Override
+        public Stream<Double> boxed() {
+            return s.boxed();
+        }
+
+        @Override
+        public DoubleStream sequential() {
+            return s.sequential();
+        }
+
+        @Override
+        public DoubleStream parallel() {
+            return s.parallel();
+        }
+
+        @Override
+        public PrimitiveIterator.OfDouble iterator() {
+            return s.iterator();
+        }
+
+        @Override
+        public Spliterator.OfDouble spliterator() {
+            return s.spliterator();
+        }
+
+        @Override
+        public boolean isParallel() {
+            return s.isParallel();
+        }
+
+        @Override
+        public DoubleStream unordered() {
+            return s.unordered();
+        }
+
+        @Override
+        public DoubleStream onClose(Runnable closeHandler) {
+            return s.onClose(closeHandler);
+        }
+
+        @Override
+        public void close() {
+            s.close();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/DoubleStreamTestDataProvider.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 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 java.util.stream;
+
+import org.testng.annotations.DataProvider;
+
+import java.util.*;
+import java.util.Spliterators;
+import java.util.function.Supplier;
+
+/** TestNG DataProvider for double-valued streams */
+public class DoubleStreamTestDataProvider {
+    private static final double[] to0 = new double[0];
+    private static final double[] to1 = new double[1];
+    private static final double[] to10 = new double[10];
+    private static final double[] to100 = new double[100];
+    private static final double[] to1000 = new double[1000];
+    private static final double[] reversed = new double[100];
+    private static final double[] ones = new double[100];
+    private static final double[] twice = new double[200];
+    private static final double[] pseudoRandom;
+
+    private static final Object[][] testData;
+    private static final Object[][] spliteratorTestData;
+
+    static {
+        double[][] arrays = {to0, to1, to10, to100, to1000};
+        for (double[] arr : arrays) {
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = i;
+            }
+        }
+        for (int i = 0; i < reversed.length; i++) {
+            reversed[i] = reversed.length - i;
+        }
+        for (int i = 0; i < ones.length; i++) {
+            ones[i] = 1;
+        }
+        System.arraycopy(to100, 0, twice, 0, to100.length);
+        System.arraycopy(to100, 0, twice, to100.length, to100.length);
+        pseudoRandom = new double[LambdaTestHelpers.LONG_STRING.length()];
+        for (int i = 0; i < LambdaTestHelpers.LONG_STRING.length(); i++) {
+            pseudoRandom[i] = (double) LambdaTestHelpers.LONG_STRING.charAt(i);
+        }
+    }
+
+    static final Object[][] arrays = {
+            {"empty", to0},
+            {"0..1", to1},
+            {"0..10", to10},
+            {"0..100", to100},
+            {"0..1000", to1000},
+            {"100x[1]", ones},
+            {"2x[0..100]", twice},
+            {"reverse 0..100", reversed},
+            {"pseudorandom", pseudoRandom}
+    };
+
+    static {
+        {
+            List<Object[]> list = new ArrayList<>();
+            for (Object[] data : arrays) {
+                final Object name = data[0];
+                final double[] doubles = (double[]) data[1];
+
+                list.add(new Object[]{"array:" + name,
+                        TestData.Factory.ofArray("array:" + name, doubles)});
+
+                SpinedBuffer.OfDouble isl = new SpinedBuffer.OfDouble();
+                for (double i : doubles) {
+                    isl.accept(i);
+                }
+                list.add(new Object[]{"SpinedList:" + name,
+                        TestData.Factory.ofSpinedBuffer("SpinedList:" + name, isl)});
+            }
+            testData = list.toArray(new Object[0][]);
+        }
+
+        {
+            List<Object[]> spliterators = new ArrayList<>();
+            for (Object[] data : arrays) {
+                final Object name = data[0];
+                final double[] doubles = (double[]) data[1];
+
+                SpinedBuffer.OfDouble isl = new SpinedBuffer.OfDouble();
+                for (double i : doubles) {
+                    isl.accept(i);
+                }
+
+                spliterators.add(splitDescr("Arrays.s(array):" + name,
+                                            () -> Arrays.spliterator(doubles)));
+                spliterators.add(splitDescr("Arrays.s(array,o,l):" + name,
+                                            () -> Arrays.spliterator(doubles, 0, doubles.length / 2)));
+
+                spliterators.add(splitDescr("SpinedBuffer.s():" + name,
+                                            () -> isl.spliterator()));
+
+                spliterators.add(splitDescr("Primitives.s(SpinedBuffer.iterator(), size):" + name,
+                                            () -> Spliterators.spliterator(isl.iterator(), doubles.length, 0)));
+                spliterators.add(splitDescr("Primitives.s(SpinedBuffer.iterator()):" + name,
+                                            () -> Spliterators.spliteratorUnknownSize(isl.iterator(), 0)));
+                // Need more!
+            }
+            spliteratorTestData = spliterators.toArray(new Object[0][]);
+        }
+
+    }
+
+    static <T> Object[] splitDescr(String description, Supplier<Spliterator.OfDouble> s) {
+        return new Object[] { description, s };
+    }
+
+    // Return an array of ( String name, DoubleStreamTestData )
+    @DataProvider(name = "DoubleStreamTestData")
+    public static Object[][] makeDoubleStreamTestData() {
+        return testData;
+    }
+
+    // returns an array of (String name, Supplier<PrimitiveSpliterator<Double>>)
+    @DataProvider(name = "DoubleSpliterator")
+    public static Object[][] spliteratorProvider() {
+        return spliteratorTestData;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/DoubleStreamTestScenario.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,232 @@
+/*
+ * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package java.util.stream;
+
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.PrimitiveIterator;
+import java.util.Set;
+import java.util.Spliterator;
+import java.util.function.Consumer;
+import java.util.function.DoubleConsumer;
+import java.util.function.Function;
+
+/**
+ * Test scenarios for double streams.
+ *
+ * Each scenario is provided with a data source, a function that maps a fresh
+ * stream (as provided by the data source) to a new stream, and a sink to
+ * receive results.  Each scenario describes a different way of computing the
+ * stream contents.  The test driver will ensure that all scenarios produce
+ * the same output (modulo allowable differences in ordering).
+ */
+@SuppressWarnings({"rawtypes", "unchecked"})
+public enum DoubleStreamTestScenario implements OpTestCase.BaseStreamTestScenario {
+
+    STREAM_FOR_EACH(false) {
+        <T, S_IN extends BaseStream<T, S_IN>>
+        void run(TestData<T, S_IN> data, S_IN source, DoubleConsumer b, Function<S_IN, DoubleStream> m) {
+            DoubleStream s = m.apply(source);
+            if (s.isParallel()) {
+                s = s.sequential();
+            }
+            s.forEach(b);
+        }
+    },
+
+    STREAM_TO_ARRAY(false) {
+        <T, S_IN extends BaseStream<T, S_IN>>
+        void run(TestData<T, S_IN> data, S_IN source, DoubleConsumer b, Function<S_IN, DoubleStream> m) {
+            for (double t : m.apply(source).toArray()) {
+                b.accept(t);
+            }
+        }
+    },
+
+    STREAM_ITERATOR(false) {
+        <T, S_IN extends BaseStream<T, S_IN>>
+        void run(TestData<T, S_IN> data, S_IN source, DoubleConsumer b, Function<S_IN, DoubleStream> m) {
+            for (PrimitiveIterator.OfDouble seqIter = m.apply(source).iterator(); seqIter.hasNext(); )
+                b.accept(seqIter.nextDouble());
+        }
+    },
+
+    // Wrap as stream, and spliterate then iterate in pull mode
+    STREAM_SPLITERATOR(false) {
+        <T, S_IN extends BaseStream<T, S_IN>>
+        void run(TestData<T, S_IN> data, S_IN source, DoubleConsumer b, Function<S_IN, DoubleStream> m) {
+            for (Spliterator.OfDouble spl = m.apply(source).spliterator(); spl.tryAdvance(b); ) {
+            }
+        }
+    },
+
+    // Wrap as stream, spliterate, then split a few times mixing advances with forEach
+    STREAM_SPLITERATOR_WITH_MIXED_TRAVERSE_AND_SPLIT(false) {
+        <T, S_IN extends BaseStream<T, S_IN>>
+        void run(TestData<T, S_IN> data, S_IN source, DoubleConsumer b, Function<S_IN, DoubleStream> m) {
+            SpliteratorTestHelper.mixedTraverseAndSplit(b, m.apply(source).spliterator());
+        }
+    },
+
+    // Wrap as stream, and spliterate then iterate in pull mode
+    STREAM_SPLITERATOR_FOREACH(false) {
+        <T, S_IN extends BaseStream<T, S_IN>>
+        void run(TestData<T, S_IN> data, S_IN source, DoubleConsumer b, Function<S_IN, DoubleStream> m) {
+            m.apply(source).spliterator().forEachRemaining(b);
+        }
+    },
+
+    PAR_STREAM_SEQUENTIAL_FOR_EACH(true) {
+        <T, S_IN extends BaseStream<T, S_IN>>
+        void run(TestData<T, S_IN> data, S_IN source, DoubleConsumer b, Function<S_IN, DoubleStream> m) {
+            m.apply(source).sequential().forEach(b);
+        }
+    },
+
+    // Wrap as parallel stream + forEachOrdered
+    PAR_STREAM_FOR_EACH_ORDERED(true) {
+        <T, S_IN extends BaseStream<T, S_IN>>
+        void run(TestData<T, S_IN> data, S_IN source, DoubleConsumer b, Function<S_IN, DoubleStream> m) {
+            // @@@ Want to explicitly select ordered equalator
+            m.apply(source).forEachOrdered(b);
+        }
+    },
+
+    // Wrap as stream, and spliterate then iterate sequentially
+    PAR_STREAM_SPLITERATOR(true) {
+        <T, S_IN extends BaseStream<T, S_IN>>
+        void run(TestData<T, S_IN> data, S_IN source, DoubleConsumer b, Function<S_IN, DoubleStream> m) {
+            for (Spliterator.OfDouble spl = m.apply(source).spliterator(); spl.tryAdvance(b); ) {
+            }
+        }
+    },
+
+    // Wrap as stream, and spliterate then iterate sequentially
+    PAR_STREAM_SPLITERATOR_FOREACH(true) {
+        <T, S_IN extends BaseStream<T, S_IN>>
+        void run(TestData<T, S_IN> data, S_IN source, DoubleConsumer b, Function<S_IN, DoubleStream> m) {
+            m.apply(source).spliterator().forEachRemaining(b);
+        }
+    },
+
+    PAR_STREAM_TO_ARRAY(true) {
+        <T, S_IN extends BaseStream<T, S_IN>>
+        void run(TestData<T, S_IN> data, S_IN source, DoubleConsumer b, Function<S_IN, DoubleStream> m) {
+            for (double t : m.apply(source).toArray())
+                b.accept(t);
+        }
+    },
+
+    // Wrap as parallel stream, get the spliterator, wrap as a stream + toArray
+    PAR_STREAM_SPLITERATOR_STREAM_TO_ARRAY(true) {
+        <T, S_IN extends BaseStream<T, S_IN>>
+        void run(TestData<T, S_IN> data, S_IN source, DoubleConsumer b, Function<S_IN, DoubleStream> m) {
+            DoubleStream s = m.apply(source);
+            Spliterator.OfDouble sp = s.spliterator();
+            DoubleStream ss = StreamSupport.doubleStream(() -> sp,
+                                                         StreamOpFlag.toCharacteristics(OpTestCase.getStreamFlags(s))
+                                                         | (sp.getExactSizeIfKnown() < 0 ? 0 : Spliterator.SIZED), true);
+            for (double t : ss.toArray())
+                b.accept(t);
+        }
+    },
+
+    PAR_STREAM_TO_ARRAY_CLEAR_SIZED(true) {
+        <T, S_IN extends BaseStream<T, S_IN>>
+        void run(TestData<T, S_IN> data, S_IN source, DoubleConsumer b, Function<S_IN, DoubleStream> m) {
+            S_IN pipe1 = (S_IN) OpTestCase.chain(source,
+                                                 new FlagDeclaringOp(StreamOpFlag.NOT_SIZED, data.getShape()));
+            DoubleStream pipe2 = m.apply(pipe1);
+
+            for (double t : pipe2.toArray())
+                b.accept(t);
+        }
+    },
+
+    // Wrap as parallel stream + forEach synchronizing
+    PAR_STREAM_FOR_EACH(true, false) {
+        <T, S_IN extends BaseStream<T, S_IN>>
+        void run(TestData<T, S_IN> data, S_IN source, DoubleConsumer b, Function<S_IN, DoubleStream> m) {
+            m.apply(source).forEach(e -> {
+                synchronized (data) {
+                    b.accept(e);
+                }
+            });
+        }
+    },
+
+    // Wrap as parallel stream + forEach synchronizing and clear SIZED flag
+    PAR_STREAM_FOR_EACH_CLEAR_SIZED(true, false) {
+        <T, S_IN extends BaseStream<T, S_IN>>
+        void run(TestData<T, S_IN> data, S_IN source, DoubleConsumer b, Function<S_IN, DoubleStream> m) {
+            S_IN pipe1 = (S_IN) OpTestCase.chain(source,
+                                                 new FlagDeclaringOp(StreamOpFlag.NOT_SIZED, data.getShape()));
+            m.apply(pipe1).forEach(e -> {
+                synchronized (data) {
+                    b.accept(e);
+                }
+            });
+        }
+    },
+    ;
+
+    // The set of scenarios that clean the SIZED flag
+    public static final Set<DoubleStreamTestScenario> CLEAR_SIZED_SCENARIOS = Collections.unmodifiableSet(
+            EnumSet.of(PAR_STREAM_TO_ARRAY_CLEAR_SIZED, PAR_STREAM_FOR_EACH_CLEAR_SIZED));
+
+    private boolean isParallel;
+
+    private final boolean isOrdered;
+
+    DoubleStreamTestScenario(boolean isParallel) {
+        this(isParallel, true);
+    }
+
+    DoubleStreamTestScenario(boolean isParallel, boolean isOrdered) {
+        this.isParallel = isParallel;
+        this.isOrdered = isOrdered;
+    }
+
+    public StreamShape getShape() {
+        return StreamShape.DOUBLE_VALUE;
+    }
+
+    public boolean isParallel() {
+        return isParallel;
+    }
+
+    public boolean isOrdered() {
+        return isOrdered;
+    }
+
+    public <T, U, S_IN extends BaseStream<T, S_IN>, S_OUT extends BaseStream<U, S_OUT>>
+    void run(TestData<T, S_IN> data, Consumer<U> b, Function<S_IN, S_OUT> m) {
+        try (S_IN source = getStream(data)) {
+            run(data, source, (DoubleConsumer) b, (Function<S_IN, DoubleStream>) m);
+        }
+    }
+
+    abstract <T, S_IN extends BaseStream<T, S_IN>>
+    void run(TestData<T, S_IN> data, S_IN source, DoubleConsumer b, Function<S_IN, DoubleStream> m);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/FlagDeclaringOp.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,61 @@
+/*
+ * 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 java.util.stream;
+
+/**
+ * An operation that injects or clears flags but otherwise performs no operation on elements.
+ */
+@SuppressWarnings({"rawtypes", "unchecked"})
+public class FlagDeclaringOp<T> implements StatelessTestOp<T, T> {
+    private final int flags;
+    private final StreamShape shape;
+
+    public FlagDeclaringOp(int flags) {
+        this(flags, StreamShape.REFERENCE);
+    }
+
+    public FlagDeclaringOp(int flags, StreamShape shape) {
+        this.flags = flags;
+        this.shape = shape;
+    }
+
+    @Override
+    public StreamShape outputShape() {
+        return shape;
+    }
+
+    @Override
+    public StreamShape inputShape() {
+        return shape;
+    }
+
+    @Override
+    public int opGetFlags() {
+        return flags;
+    }
+
+    @Override
+    public Sink<T> opWrapSink(int flags, boolean parallel, Sink sink) {
+        return sink;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/IntStreamTestDataProvider.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,158 @@
+/*
+ * 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 java.util.stream;
+
+import org.testng.annotations.DataProvider;
+
+import java.util.*;
+import java.util.Spliterators;
+import java.util.function.Supplier;
+
+/** TestNG DataProvider for int-valued streams */
+public class IntStreamTestDataProvider {
+    private static final int[] to0 = new int[0];
+    private static final int[] to1 = new int[1];
+    private static final int[] to10 = new int[10];
+    private static final int[] to100 = new int[100];
+    private static final int[] to1000 = new int[1000];
+    private static final int[] reversed = new int[100];
+    private static final int[] ones = new int[100];
+    private static final int[] twice = new int[200];
+    private static final int[] pseudoRandom;
+
+    private static final Object[][] testData;
+    private static final Object[][] spliteratorTestData;
+
+    static {
+        int[][] arrays = {to0, to1, to10, to100, to1000};
+        for (int[] arr : arrays) {
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = i;
+            }
+        }
+        for (int i = 0; i < reversed.length; i++) {
+            reversed[i] = reversed.length - i;
+        }
+        for (int i = 0; i < ones.length; i++) {
+            ones[i] = 1;
+        }
+        System.arraycopy(to100, 0, twice, 0, to100.length);
+        System.arraycopy(to100, 0, twice, to100.length, to100.length);
+        pseudoRandom = new int[LambdaTestHelpers.LONG_STRING.length()];
+        for (int i = 0; i < LambdaTestHelpers.LONG_STRING.length(); i++) {
+            pseudoRandom[i] = (int) LambdaTestHelpers.LONG_STRING.charAt(i);
+        }
+    }
+
+    static final Object[][] arrays = {
+            {"empty", to0},
+            {"0..1", to1},
+            {"0..10", to10},
+            {"0..100", to100},
+            {"0..1000", to1000},
+            {"100x[1]", ones},
+            {"2x[0..100]", twice},
+            {"reverse 0..100", reversed},
+            {"pseudorandom", pseudoRandom}
+    };
+
+    static {
+        {
+            List<Object[]> list = new ArrayList<>();
+            for (Object[] data : arrays) {
+                final Object name = data[0];
+                final int[] ints = (int[]) data[1];
+
+                list.add(new Object[]{"array:" +
+                                      name, TestData.Factory.ofArray("array:" + name, ints)});
+
+                SpinedBuffer.OfInt isl = new SpinedBuffer.OfInt();
+                for (int i : ints) {
+                    isl.accept(i);
+                }
+                list.add(new Object[]{"SpinedList:" + name,
+                         TestData.Factory.ofSpinedBuffer("SpinedList:" + name, isl)});
+
+                list.add(streamDataDescr("IntStream.intRange(0,l): " + ints.length,
+                                         () -> IntStream.range(0, ints.length)));
+                list.add(streamDataDescr("IntStream.rangeClosed(0,l): " + ints.length,
+                                         () -> IntStream.rangeClosed(0, ints.length)));
+            }
+            testData = list.toArray(new Object[0][]);
+        }
+
+        {
+            List<Object[]> spliterators = new ArrayList<>();
+            for (Object[] data : arrays) {
+                final Object name = data[0];
+                final int[] ints = (int[]) data[1];
+
+                SpinedBuffer.OfInt isl = new SpinedBuffer.OfInt();
+                for (int i : ints) {
+                    isl.accept(i);
+                }
+
+                spliterators.add(splitDescr("Arrays.s(array):" + name,
+                                            () -> Arrays.spliterator(ints)));
+                spliterators.add(splitDescr("Arrays.s(array,o,l):" + name,
+                                            () -> Arrays.spliterator(ints, 0, ints.length / 2)));
+
+                spliterators.add(splitDescr("SpinedBuffer.s():" + name,
+                                            () -> isl.spliterator()));
+
+                spliterators.add(splitDescr("Primitives.s(SpinedBuffer.iterator(), size):" + name,
+                                            () -> Spliterators.spliterator(isl.iterator(), ints.length, 0)));
+                spliterators.add(splitDescr("Primitives.s(SpinedBuffer.iterator()):" + name,
+                                            () -> Spliterators.spliteratorUnknownSize(isl.iterator(), 0)));
+
+                spliterators.add(splitDescr("IntStream.intRange(0,l):" + name,
+                                            () -> IntStream.range(0, ints.length).spliterator()));
+                spliterators.add(splitDescr("IntStream.intRangeClosed(0,l):" + name,
+                                            () -> IntStream.rangeClosed(0, ints.length).spliterator()));
+                // Need more!
+            }
+            spliteratorTestData = spliterators.toArray(new Object[0][]);
+        }
+
+    }
+
+    static <T> Object[] streamDataDescr(String description, Supplier<IntStream> s) {
+        return new Object[] { description, TestData.Factory.ofIntSupplier(description, s) };
+    }
+
+    static <T> Object[] splitDescr(String description, Supplier<Spliterator.OfInt> s) {
+        return new Object[] { description, s };
+    }
+
+    // Return an array of ( String name, IntStreamTestData )
+    @DataProvider(name = "IntStreamTestData")
+    public static Object[][] makeIntStreamTestData() {
+        return testData;
+    }
+
+    // returns an array of (String name, Supplier<PrimitiveSpliterator<Integer>>)
+    @DataProvider(name = "IntSpliterator")
+    public static Object[][] spliteratorProvider() {
+        return spliteratorTestData;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/IntStreamTestScenario.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,233 @@
+/*
+ * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package java.util.stream;
+
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.PrimitiveIterator;
+import java.util.Set;
+import java.util.Spliterator;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.IntConsumer;
+
+/**
+ * Test scenarios for int streams.
+ *
+ * Each scenario is provided with a data source, a function that maps a fresh
+ * stream (as provided by the data source) to a new stream, and a sink to
+ * receive results.  Each scenario describes a different way of computing the
+ * stream contents.  The test driver will ensure that all scenarios produce
+ * the same output (modulo allowable differences in ordering).
+ */
+@SuppressWarnings({"rawtypes", "unchecked"})
+public enum IntStreamTestScenario implements OpTestCase.BaseStreamTestScenario {
+
+    STREAM_FOR_EACH(false) {
+        <T, S_IN extends BaseStream<T, S_IN>>
+        void run(TestData<T, S_IN> data, S_IN source, IntConsumer b, Function<S_IN, IntStream> m) {
+            IntStream s = m.apply(source);
+            if (s.isParallel()) {
+                s = s.sequential();
+            }
+            s.forEach(b);
+        }
+    },
+
+    STREAM_TO_ARRAY(false) {
+        <T, S_IN extends BaseStream<T, S_IN>>
+        void run(TestData<T, S_IN> data, S_IN source, IntConsumer b, Function<S_IN, IntStream> m) {
+            for (int t : m.apply(source).toArray()) {
+                b.accept(t);
+            }
+        }
+    },
+
+    STREAM_ITERATOR(false) {
+        <T, S_IN extends BaseStream<T, S_IN>>
+        void run(TestData<T, S_IN> data, S_IN source, IntConsumer b, Function<S_IN, IntStream> m) {
+            for (PrimitiveIterator.OfInt seqIter = m.apply(source).iterator(); seqIter.hasNext(); )
+                b.accept(seqIter.nextInt());
+        }
+    },
+
+    // Wrap as stream, and spliterate then iterate in pull mode
+    STREAM_SPLITERATOR(false) {
+        <T, S_IN extends BaseStream<T, S_IN>>
+        void run(TestData<T, S_IN> data, S_IN source, IntConsumer b, Function<S_IN, IntStream> m) {
+            for (Spliterator.OfInt spl = m.apply(source).spliterator(); spl.tryAdvance(b); ) {
+            }
+        }
+    },
+
+    // Wrap as stream, spliterate, then split a few times mixing advances with forEach
+    STREAM_SPLITERATOR_WITH_MIXED_TRAVERSE_AND_SPLIT(false) {
+        <T, S_IN extends BaseStream<T, S_IN>>
+        void run(TestData<T, S_IN> data, S_IN source, IntConsumer b, Function<S_IN, IntStream> m) {
+            SpliteratorTestHelper.mixedTraverseAndSplit(b, m.apply(source).spliterator());
+        }
+    },
+
+    // Wrap as stream, and spliterate then iterate in pull mode
+    STREAM_SPLITERATOR_FOREACH(false) {
+        <T, S_IN extends BaseStream<T, S_IN>>
+        void run(TestData<T, S_IN> data, S_IN source, IntConsumer b, Function<S_IN, IntStream> m) {
+            m.apply(source).spliterator().forEachRemaining(b);
+        }
+    },
+
+    PAR_STREAM_SEQUENTIAL_FOR_EACH(true) {
+        <T, S_IN extends BaseStream<T, S_IN>>
+        void run(TestData<T, S_IN> data, S_IN source, IntConsumer b, Function<S_IN, IntStream> m) {
+            m.apply(source).sequential().forEach(b);
+        }
+    },
+
+    // Wrap as parallel stream + forEachOrdered
+    PAR_STREAM_FOR_EACH_ORDERED(true) {
+        <T, S_IN extends BaseStream<T, S_IN>>
+        void run(TestData<T, S_IN> data, S_IN source, IntConsumer b, Function<S_IN, IntStream> m) {
+            // @@@ Want to explicitly select ordered equalator
+            m.apply(source).forEachOrdered(b);
+        }
+    },
+
+    // Wrap as stream, and spliterate then iterate sequentially
+    PAR_STREAM_SPLITERATOR(true) {
+        <T, S_IN extends BaseStream<T, S_IN>>
+        void run(TestData<T, S_IN> data, S_IN source, IntConsumer b, Function<S_IN, IntStream> m) {
+            for (Spliterator.OfInt spl = m.apply(source).spliterator(); spl.tryAdvance(b); ) {
+            }
+        }
+    },
+
+    // Wrap as stream, and spliterate then iterate sequentially
+    PAR_STREAM_SPLITERATOR_FOREACH(true) {
+        <T, S_IN extends BaseStream<T, S_IN>>
+        void run(TestData<T, S_IN> data, S_IN source, IntConsumer b, Function<S_IN, IntStream> m) {
+            m.apply(source).spliterator().forEachRemaining(b);
+        }
+    },
+
+    PAR_STREAM_TO_ARRAY(true) {
+        <T, S_IN extends BaseStream<T, S_IN>>
+        void run(TestData<T, S_IN> data, S_IN source, IntConsumer b, Function<S_IN, IntStream> m) {
+            for (int t : m.apply(source).toArray())
+                b.accept(t);
+        }
+    },
+
+    // Wrap as parallel stream, get the spliterator, wrap as a stream + toArray
+    PAR_STREAM_SPLITERATOR_STREAM_TO_ARRAY(true) {
+        <T, S_IN extends BaseStream<T, S_IN>>
+        void run(TestData<T, S_IN> data, S_IN source, IntConsumer b, Function<S_IN, IntStream> m) {
+            IntStream s = m.apply(source);
+            Spliterator.OfInt sp = s.spliterator();
+            IntStream ss = StreamSupport.intStream(() -> sp,
+                                                   StreamOpFlag.toCharacteristics(OpTestCase.getStreamFlags(s))
+                                                   | (sp.getExactSizeIfKnown() < 0 ? 0 : Spliterator.SIZED),
+                                                   true);
+            for (int t : ss.toArray())
+                b.accept(t);
+        }
+    },
+
+    PAR_STREAM_TO_ARRAY_CLEAR_SIZED(true) {
+        <T, S_IN extends BaseStream<T, S_IN>>
+        void run(TestData<T, S_IN> data, S_IN source, IntConsumer b, Function<S_IN, IntStream> m) {
+            S_IN pipe1 = (S_IN) OpTestCase.chain(source,
+                                                 new FlagDeclaringOp(StreamOpFlag.NOT_SIZED, data.getShape()));
+            IntStream pipe2 = m.apply(pipe1);
+
+            for (int t : pipe2.toArray())
+                b.accept(t);
+        }
+    },
+
+    // Wrap as parallel stream + forEach synchronizing
+    PAR_STREAM_FOR_EACH(true, false) {
+        <T, S_IN extends BaseStream<T, S_IN>>
+        void run(TestData<T, S_IN> data, S_IN source, IntConsumer b, Function<S_IN, IntStream> m) {
+            m.apply(source).forEach(e -> {
+                synchronized (data) {
+                    b.accept(e);
+                }
+            });
+        }
+    },
+
+    // Wrap as parallel stream + forEach synchronizing and clear SIZED flag
+    PAR_STREAM_FOR_EACH_CLEAR_SIZED(true, false) {
+        <T, S_IN extends BaseStream<T, S_IN>>
+        void run(TestData<T, S_IN> data, S_IN source, IntConsumer b, Function<S_IN, IntStream> m) {
+            S_IN pipe1 = (S_IN) OpTestCase.chain(source,
+                                                 new FlagDeclaringOp(StreamOpFlag.NOT_SIZED, data.getShape()));
+            m.apply(pipe1).forEach(e -> {
+                synchronized (data) {
+                    b.accept(e);
+                }
+            });
+        }
+    },
+    ;
+
+    // The set of scenarios that clean the SIZED flag
+    public static final Set<IntStreamTestScenario> CLEAR_SIZED_SCENARIOS = Collections.unmodifiableSet(
+            EnumSet.of(PAR_STREAM_TO_ARRAY_CLEAR_SIZED, PAR_STREAM_FOR_EACH_CLEAR_SIZED));
+
+    private final boolean isParallel;
+
+    private final boolean isOrdered;
+
+    IntStreamTestScenario(boolean isParallel) {
+        this(isParallel, true);
+    }
+
+    IntStreamTestScenario(boolean isParallel, boolean isOrdered) {
+        this.isParallel = isParallel;
+        this.isOrdered = isOrdered;
+    }
+
+    public StreamShape getShape() {
+        return StreamShape.INT_VALUE;
+    }
+
+    public boolean isParallel() {
+        return isParallel;
+    }
+
+    public boolean isOrdered() {
+        return isOrdered;
+    }
+
+    public <T, U, S_IN extends BaseStream<T, S_IN>, S_OUT extends BaseStream<U, S_OUT>>
+    void run(TestData<T, S_IN> data, Consumer<U> b, Function<S_IN, S_OUT> m) {
+        try (S_IN source = getStream(data)) {
+            run(data, source, (IntConsumer) b, (Function<S_IN, IntStream>) m);
+        }
+    }
+
+    abstract <T, S_IN extends BaseStream<T, S_IN>>
+    void run(TestData<T, S_IN> data, S_IN source, IntConsumer b, Function<S_IN, IntStream> m);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/IntermediateTestOp.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,41 @@
+/*
+ * 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 java.util.stream;
+
+/**
+ * A base type for test operations
+ */
+interface IntermediateTestOp<E_IN, E_OUT> {
+
+    @SuppressWarnings({"rawtypes", "unchecked"})
+    public static<T> AbstractPipeline chain(AbstractPipeline upstream,
+                                            IntermediateTestOp<?, T> op) {
+        if (op instanceof StatelessTestOp)
+            return StatelessTestOp.chain(upstream, (StatelessTestOp) op);
+
+        if (op instanceof StatefulTestOp)
+            return StatefulTestOp.chain(upstream, (StatefulTestOp) op);
+
+        throw new IllegalStateException("Unknown test op type: " + op.getClass().getName());
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/LambdaTestHelpers.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,462 @@
+/*
+ * Copyright (c) 1997, 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 java.util.stream;
+
+import java.util.*;
+import java.util.function.BiConsumer;
+import java.util.function.BiPredicate;
+import java.util.function.BinaryOperator;
+import java.util.function.Consumer;
+import java.util.function.DoubleBinaryOperator;
+import java.util.function.DoubleConsumer;
+import java.util.function.DoublePredicate;
+import java.util.function.Function;
+import java.util.function.IntBinaryOperator;
+import java.util.function.IntConsumer;
+import java.util.function.IntFunction;
+import java.util.function.IntPredicate;
+import java.util.function.IntUnaryOperator;
+import java.util.function.LongBinaryOperator;
+import java.util.function.LongConsumer;
+import java.util.function.LongPredicate;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+import java.util.function.ToDoubleFunction;
+import java.util.function.ToIntFunction;
+import java.util.function.ToLongFunction;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+/**
+ * LambdaTestHelpers -- assertion methods and useful objects for lambda test cases
+ */
+public class LambdaTestHelpers {
+    public static final String LONG_STRING = "When in the Course of human events it becomes necessary for one people to dissolve the political bands which have connected them with another and to assume among the powers of the earth, the separate and equal station to which the Laws of Nature and of Nature's God entitle them, a decent respect to the opinions of mankind requires that they should declare the causes which impel them to the separation.";
+
+    @SuppressWarnings("rawtypes")
+    public static final Consumer bEmpty = x -> {  };
+    @SuppressWarnings("rawtypes")
+    public static final IntConsumer bIntEmpty = x -> {  };
+    @SuppressWarnings("rawtypes")
+    public static final BiConsumer bBiEmpty = (x,y) -> { };
+    @SuppressWarnings("rawtypes")
+    public static final Consumer bHashCode = x -> { Objects.hashCode(x); };
+    @SuppressWarnings("rawtypes")
+    public static final BiConsumer bBiHashCode = (x,y) -> { Objects.hash(x, y); };
+    public static final Function<Integer, Integer> mZero = x -> 0;
+    public static final Function<Integer, Integer> mId = x -> x;
+    public static final Function<Integer, Integer> mDoubler = x -> x * 2;
+    public static final Function<Integer, Stream<Integer>> mfId = e -> Collections.singletonList(e).stream();
+    public static final Function<Integer, Stream<Integer>> mfNull = e -> Collections.<Integer>emptyList().stream();
+    public static final Function<Integer, Stream<Integer>> mfLt = e -> {
+        List<Integer> l = new ArrayList<>();
+        for (int i=0; i<e; i++)
+            l.add(i);
+        return l.stream();
+    };
+    public static final ToIntFunction<Integer> imDoubler = x -> x * 2;
+    public static final ToLongFunction<Long> lmDoubler = x -> x * 2;
+    public static final ToDoubleFunction<Double> dmDoubler = x -> x * 2;
+    public static final Predicate<Integer> pFalse = x -> false;
+    public static final Predicate<Integer> pTrue = x -> true;
+    public static final Predicate<Integer> pEven = x -> 0 == x % 2;
+    public static final Predicate<Integer> pOdd = x -> 1 == x % 2;
+    public static final IntPredicate ipFalse = x -> false;
+    public static final IntPredicate ipTrue = x -> true;
+    public static final IntPredicate ipEven = x -> 0 == x % 2;
+    public static final IntPredicate ipOdd = x -> 1 == x % 2;
+    public static final LongPredicate lpFalse = x -> false;
+    public static final LongPredicate lpTrue = x -> true;
+    public static final LongPredicate lpEven = x -> 0 == x % 2;
+    public static final LongPredicate lpOdd = x -> 1 == x % 2;
+    public static final DoublePredicate dpFalse = x -> false;
+    public static final DoublePredicate dpTrue = x -> true;
+    public static final DoublePredicate dpEven = x -> 0 == ((long) x) % 2;
+    public static final DoublePredicate dpOdd = x -> 1 == ((long) x) % 2;
+    public static final BinaryOperator<Integer> rPlus = (x, y) -> x+y;
+    public static final BinaryOperator<Integer> rMax = (x, y) -> Math.max(x, y);
+    public static final BinaryOperator<Integer> rMin = (x, y) -> Math.min(x,y);
+    public static final IntBinaryOperator irPlus = (x, y) -> x+y;
+    public static final IntBinaryOperator irMax = (x, y) -> Math.max(x, y);
+    public static final IntBinaryOperator irMin = (x, y) -> Math.min(x,y);
+    public static final IntUnaryOperator irDoubler = x -> x * 2;
+    public static final LongBinaryOperator lrPlus = (x, y) -> x+y;
+    public static final DoubleBinaryOperator drPlus = (x, y) -> x+y;
+    public static final Comparator<Integer> cInteger = (a, b) -> Integer.compare(a, b);
+    public static final BiPredicate<?, ?> bipFalse = (x, y) -> false;
+    public static final BiPredicate<?, ?> bipTrue = (x, y) -> true;
+    public static final BiPredicate<Integer, Integer> bipBothEven = (x, y) -> 0 == (x % 2 + y % 2);
+    public static final BiPredicate<Integer, Integer> bipBothOdd = (x, y) -> 2 == (x % 2 + y % 2);
+    public static final BiPredicate<?, ?> bipSameString = (x, y) -> String.valueOf(x).equals(String.valueOf(y));
+
+    public static final IntFunction<Integer[]> integerArrayGenerator = s -> new Integer[s];
+
+    public static final IntFunction<Object[]> objectArrayGenerator = s -> new Object[s];
+
+    public static final Function<String, Stream<Character>> flattenChars = string -> {
+        List<Character> l = new ArrayList<>();
+        for (int i=0; i<string.length(); i++)
+            l.add(string.charAt(i));
+        return l.stream();
+    };
+
+    public static final Function<String, IntStream> flattenInt
+            = string -> IntStream.range(0, string.length()).map(string::charAt);
+
+    public static <T, R> Function<T, R> forPredicate(Predicate<? super T> predicate, R forTrue, R forFalse) {
+        Objects.requireNonNull(predicate);
+
+        return t -> predicate.test(t) ? forTrue : forFalse;
+    }
+
+    public static <T> Function<T, T> identity() {
+        return t -> t;
+    }
+
+    public static<V, T, R> Function<V, R> compose(Function<? super T, ? extends R> after, Function<? super V, ? extends T> before) {
+        Objects.requireNonNull(before);
+        return (V v) -> after.apply(before.apply(v));
+    }
+
+    public static List<Integer> empty() {
+        ArrayList<Integer> list = new ArrayList<>();
+        list.add(null);
+        return list;
+    }
+
+    public static List<Integer> countTo(int n) {
+        return range(1, n);
+    }
+
+    public static List<Integer> range(int l, int u) {
+        ArrayList<Integer> list = new ArrayList<>(u - l + 1);
+        for (int i=l; i<=u; i++) {
+            list.add(i);
+        }
+        return list;
+    }
+
+    public static List<Integer> repeat(int value, int n) {
+        ArrayList<Integer> list = new ArrayList<>(n);
+        for (int i=1; i<=n; i++) {
+            list.add(value);
+        }
+        return list;
+    }
+
+    public static List<Double> asDoubles(List<Integer> integers) {
+        ArrayList<Double> list = new ArrayList<>();
+        for (Integer i : integers) {
+            list.add((double) i);
+        }
+        return list;
+    }
+
+    public static List<Long> asLongs(List<Integer> integers) {
+        ArrayList<Long> list = new ArrayList<>();
+        for (Integer i : integers) {
+            list.add((long) i);
+        }
+        return list;
+    }
+
+    public static void assertCountSum(Stream<? super Integer> it, int count, int sum) {
+        assertCountSum(it.iterator(), count, sum);
+    }
+
+    public static void assertCountSum(Iterable<? super Integer> it, int count, int sum) {
+        assertCountSum(it.iterator(), count, sum);
+    }
+
+    public static void assertCountSum(Iterator<? super Integer> it, int count, int sum) {
+        int c = 0;
+        int s = 0;
+        while (it.hasNext()) {
+            int i = (Integer) it.next();
+            c++;
+            s += i;
+        }
+
+        assertEquals(c, count);
+        assertEquals(s, sum);
+    }
+
+    public static void assertConcat(Iterator<Character> it, String result) {
+        StringBuilder sb = new StringBuilder();
+        while (it.hasNext()) {
+            sb.append(it.next());
+        }
+
+        assertEquals(result, sb.toString());
+    }
+
+    public static<T extends Comparable<? super T>> void assertSorted(Iterator<T> i) {
+        i = toBoxedList(i).iterator();
+
+        if (!i.hasNext())
+            return;
+        T last = i.next();
+        while (i.hasNext()) {
+            T t = i.next();
+            assertTrue(last.compareTo(t) <= 0);
+            assertTrue(t.compareTo(last) >= 0);
+            last = t;
+        }
+    }
+
+    public static<T> void assertSorted(Iterator<T> i, Comparator<? super T> comp) {
+        if (i instanceof PrimitiveIterator.OfInt
+                || i instanceof PrimitiveIterator.OfDouble
+                || i instanceof PrimitiveIterator.OfLong) {
+            i = toBoxedList(i).iterator();
+        }
+
+        if (!i.hasNext())
+            return;
+        T last = i.next();
+        while (i.hasNext()) {
+            T t = i.next();
+            assertTrue(comp.compare(last, t) <= 0);
+            assertTrue(comp.compare(t, last) >= 0);
+            last = t;
+        }
+    }
+
+    public static<T extends Comparable<? super T>> void assertSorted(Iterable<T> iter) {
+        assertSorted(iter.iterator());
+    }
+
+    public static<T> void assertSorted(Iterable<T> iter, Comparator<? super T> comp) {
+        assertSorted(iter.iterator(), comp);
+    }
+
+    public static <T> void assertUnique(Iterable<T> iter) {
+        assertUnique(iter.iterator());
+    }
+
+    public static<T> void assertUnique(Iterator<T> iter) {
+        if (!iter.hasNext()) {
+            return;
+        }
+
+        if (iter instanceof PrimitiveIterator.OfInt
+            || iter instanceof PrimitiveIterator.OfDouble
+            || iter instanceof PrimitiveIterator.OfLong) {
+            iter = toBoxedList(iter).iterator();
+        }
+
+        Set<T> uniq = new HashSet<>();
+        while(iter.hasNext()) {
+            T each = iter.next();
+            assertTrue(!uniq.contains(each), "Not unique");
+            uniq.add(each);
+        }
+    }
+
+    public static<T> void assertContents(Iterable<T> actual, Iterable<T> expected) {
+        if (actual instanceof Collection && expected instanceof Collection) {
+            assertEquals(actual, expected);
+        } else {
+            assertContents(actual.iterator(), expected.iterator());
+        }
+    }
+
+    public static<T> void assertContents(Iterator<T> actual, Iterator<T> expected) {
+        assertEquals(toBoxedList(actual), toBoxedList(expected));
+    }
+
+    @SafeVarargs
+    @SuppressWarnings("varargs")
+    public static<T> void assertContents(Iterator<T> actual, T... expected) {
+        assertContents(actual, Arrays.asList(expected).iterator());
+    }
+
+    /**
+     * The all consuming consumer (rampant capitalist) that can accepting a reference or any primitive value.
+     */
+    private static interface OmnivorousConsumer<T>
+            extends Consumer<T>, IntConsumer, LongConsumer, DoubleConsumer { }
+
+    @SuppressWarnings({"rawtypes", "unchecked"})
+    public static<T> Consumer<T> toBoxingConsumer(Consumer<? super T> c) {
+        return (Consumer<T>) new OmnivorousConsumer() {
+            @Override
+            public void accept(Object t) {
+                c.accept((T) t);
+            }
+
+            @Override
+            public void accept(int t) {
+                accept((Object) t);
+            }
+
+            @Override
+            public void accept(long t) {
+                accept((Object) t);
+            }
+
+            @Override
+            public void accept(double t) {
+                accept((Object) t);
+            }
+        };
+    }
+
+    /**
+     * Convert an iterator to a list using forEach with an implementation of
+     * {@link java.util.stream.LambdaTestHelpers.OmnivorousConsumer}.
+     *
+     * This ensures equality comparisons for test results do not trip
+     * the boxing trip-wires.
+     */
+    private static<T> List<T> toBoxedList(Iterator<T> it) {
+        List<T> l = new ArrayList<>();
+        it.forEachRemaining(toBoxingConsumer(l::add));
+        return l;
+    }
+
+    /**
+     * Convert a spliterator to a list using forEach with an implementation of
+     * {@link java.util.stream.LambdaTestHelpers.OmnivorousConsumer}.
+     *
+     * This ensures equality comparisons for test results do not trip
+     * the boxing trip-wires.
+     */
+    public static<T> List<T> toBoxedList(Spliterator<T> sp) {
+        List<T> l = new ArrayList<>();
+        sp.forEachRemaining(toBoxingConsumer(l::add));
+        return l;
+    }
+
+    /**
+     * Convert an iterator to a multi-set, represented as a Map, using forEach with an implementation of
+     * {@link java.util.stream.LambdaTestHelpers.OmnivorousConsumer}.
+     *
+     * This ensures equality comparisons for test results do not trip
+     * the boxing trip-wires.
+     */
+    @SuppressWarnings("unchecked")
+    private static<T> Map<T, Integer> toBoxedMultiset(Iterator<T> it) {
+        Map<Object, Integer> result = new HashMap<>();
+
+        it.forEachRemaining(toBoxingConsumer(o -> {
+                if (result.containsKey(o))
+                    result.put(o, result.get(o) + 1);
+                else
+                    result.put(o, 1);
+            }));
+
+        return (Map<T, Integer>) result;
+    }
+
+    @SuppressWarnings("unchecked")
+    public static<T> Map<T, Integer> toBoxedMultiset(Spliterator<T> it) {
+        Map<Object, Integer> result = new HashMap<>();
+
+        it.forEachRemaining(toBoxingConsumer(o -> {
+                if (result.containsKey(o))
+                    result.put(o, result.get(o) + 1);
+                else
+                    result.put(o, 1);
+            }));
+
+        return (Map<T, Integer>) result;
+    }
+
+    @SuppressWarnings("unchecked")
+    public static void assertContentsEqual(Object a, Object b) {
+        if (a instanceof Iterable && b instanceof Iterable)
+            assertContents((Iterable) a, (Iterable) b);
+        else
+            assertEquals(a, b);
+    }
+
+    public static<T> void assertContentsUnordered(Iterable<T> actual, Iterable<T> expected) {
+        assertContentsUnordered(actual.iterator(), expected.iterator());
+    }
+
+    public static<T> void assertContentsUnordered(Iterator<T> actual, Iterator<T> expected) {
+        assertEquals(toBoxedMultiset(actual), toBoxedMultiset(expected));
+    }
+
+    public static void launderAssertion(Runnable r, Supplier<String> additionalInfo) {
+        try {
+            r.run();
+        }
+        catch (AssertionError ae) {
+            AssertionError cloned = new AssertionError(ae.getMessage() + String.format("%n%s", additionalInfo.get()));
+            cloned.setStackTrace(ae.getStackTrace());
+            if (ae.getCause() != null)
+                cloned.initCause(ae.getCause());
+            throw cloned;
+        }
+    }
+
+    public static <T, S extends BaseStream<T, S>>
+    List<Function<S, S>> permuteStreamFunctions(List<Function<S, S>> opFunctions) {
+        List<List<Function<S, S>>> opFunctionPermutations = perm(opFunctions);
+
+        List<Function<S, S>> appliedFunctions = new ArrayList<>();
+        for (List<Function<S, S>> fs : opFunctionPermutations) {
+            Function<S, S> applied = s -> {
+                for (Function<S, S> f : fs) {
+                    s = f.apply(s);
+                }
+                return s;
+            };
+            appliedFunctions.add(applied);
+        }
+
+        return appliedFunctions;
+    }
+
+    private static <T> List<T> sub(List<T> l, int index) {
+        List<T> subL = new ArrayList<>(l);
+        subL.remove(index);
+        return subL;
+    }
+
+    public static <T> List<List<T>> perm(List<T> l) {
+        List<List<T>> result = new ArrayList<>();
+        for (int i = 0; i < l.size(); i++) {
+            for (List<T> perm : perm(sub(l, i))) {
+                perm.add(0, l.get(i));
+                result.add(perm);
+            }
+        }
+        result.add(new ArrayList<T>());
+
+        return result;
+    }
+
+    public static String flagsToString(int flags) {
+        StringJoiner sj = new StringJoiner(", ", "StreamOpFlag[", "]");
+        if (StreamOpFlag.DISTINCT.isKnown(flags)) sj.add("IS_DISTINCT");
+        if (StreamOpFlag.ORDERED.isKnown(flags)) sj.add("IS_ORDERED");
+        if (StreamOpFlag.SIZED.isKnown(flags)) sj.add("IS_SIZED");
+        if (StreamOpFlag.SORTED.isKnown(flags)) sj.add("IS_SORTED");
+        if (StreamOpFlag.SHORT_CIRCUIT.isKnown(flags)) sj.add("IS_SHORT_CIRCUIT");
+        return sj.toString();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/LambdaTestMode.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 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 java.util.stream;
+
+/**
+ * Runtime modes of test execution.
+ */
+public enum LambdaTestMode {
+    /**
+     * Execution mode with no particular runtime constraints.
+     */
+    NORMAL,
+
+    /**
+     * Execution mode where tests are executed for testing lambda serialization
+     * and deserialization.
+     *
+     * <p>This mode may be queried by tests or data supplied by data
+     * providers, which cannot otherwise be assigned to the test group
+     * <em>serialization-hostile</em>, to not execute or declare
+     * serialization-hostile code or data.
+     *
+     * <p>This mode is enabled if the boolean system property
+     * {@code org.openjdk.java.util.stream.sand.mode} is declared with a
+     * {@code true} value.
+     */
+    SERIALIZATION;
+
+    /**
+     * {@code true} if tests are executed in the mode for testing lambda
+     * Serialization ANd Deserialization (SAND).
+     */
+    private static final boolean IS_LAMBDA_SERIALIZATION_MODE =
+            Boolean.getBoolean("org.openjdk.java.util.stream.sand.mode");
+
+    /**
+     *
+     * @return the mode of test execution.
+     */
+    public static LambdaTestMode getMode() {
+        return IS_LAMBDA_SERIALIZATION_MODE ? SERIALIZATION : NORMAL;
+    }
+
+    /**
+     *
+     * @return {@code true} if normal test mode.
+     */
+    public static boolean isNormalMode() {
+        return getMode() == NORMAL;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/LoggingTestCase.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 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 java.util.stream;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.testng.Assert;
+import org.testng.ITestResult;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+/**
+ * LoggingTestCase
+ *
+ */
+@Test
+public class LoggingTestCase extends Assert {
+    private Map<String, Object> context = new HashMap<>();
+
+    @BeforeMethod
+    public void before() {
+        context.clear();
+    }
+
+    @AfterMethod
+    public void after(ITestResult result) {
+        if (!result.isSuccess()) {
+            List<Object> list = new ArrayList<>();
+            Collections.addAll(list, result.getParameters());
+            list.add(context.toString());
+            result.setParameters(list.toArray(new Object[list.size()]));
+        }
+    }
+
+    protected void setContext(String key, Object value) {
+        context.put(key, value);
+    }
+
+    protected void clearContext(String key) {
+        context.remove(key);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/LongStreamTestDataProvider.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 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 java.util.stream;
+
+import org.testng.annotations.DataProvider;
+
+import java.util.*;
+import java.util.Spliterators;
+import java.util.function.Supplier;
+
+/** TestNG DataProvider for long-valued streams */
+public class LongStreamTestDataProvider {
+    private static final long[] to0 = new long[0];
+    private static final long[] to1 = new long[1];
+    private static final long[] to10 = new long[10];
+    private static final long[] to100 = new long[100];
+    private static final long[] to1000 = new long[1000];
+    private static final long[] reversed = new long[100];
+    private static final long[] ones = new long[100];
+    private static final long[] twice = new long[200];
+    private static final long[] pseudoRandom;
+
+    private static final Object[][] testData;
+    private static final Object[][] spliteratorTestData;
+
+    static {
+        long[][] arrays = {to0, to1, to10, to100, to1000};
+        for (long[] arr : arrays) {
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = i;
+            }
+        }
+        for (int i = 0; i < reversed.length; i++) {
+            reversed[i] = reversed.length - i;
+        }
+        for (int i = 0; i < ones.length; i++) {
+            ones[i] = 1;
+        }
+        System.arraycopy(to100, 0, twice, 0, to100.length);
+        System.arraycopy(to100, 0, twice, to100.length, to100.length);
+        pseudoRandom = new long[LambdaTestHelpers.LONG_STRING.length()];
+        for (int i = 0; i < LambdaTestHelpers.LONG_STRING.length(); i++) {
+            pseudoRandom[i] = (long) LambdaTestHelpers.LONG_STRING.charAt(i);
+        }
+    }
+
+    static final Object[][] arrays = {
+            {"empty", to0},
+            {"0..1", to1},
+            {"0..10", to10},
+            {"0..100", to100},
+            {"0..1000", to1000},
+            {"100x[1]", ones},
+            {"2x[0..100]", twice},
+            {"reverse 0..100", reversed},
+            {"pseudorandom", pseudoRandom}
+    };
+
+    static {
+        {
+            List<Object[]> list = new ArrayList<>();
+            for (Object[] data : arrays) {
+                final Object name = data[0];
+                final long[] longs = (long[]) data[1];
+
+                list.add(new Object[]{"array:" + name,
+                        TestData.Factory.ofArray("array:" + name, longs)});
+
+                SpinedBuffer.OfLong isl = new SpinedBuffer.OfLong();
+                for (long i : longs) {
+                    isl.accept(i);
+                }
+                list.add(new Object[]{"SpinedList:" + name,
+                        TestData.Factory.ofSpinedBuffer("SpinedList:" + name, isl)});
+
+                list.add(streamDataDescr("LongStream.longRange(0,l): " + longs.length,
+                                         () -> LongStream.range(0, longs.length)));
+                list.add(streamDataDescr("LongStream.longRangeClosed(0,l): " + longs.length,
+                                         () -> LongStream.rangeClosed(0, longs.length)));
+            }
+            testData = list.toArray(new Object[0][]);
+        }
+
+        {
+            List<Object[]> spliterators = new ArrayList<>();
+            for (Object[] data : arrays) {
+                final Object name = data[0];
+                final long[] longs = (long[]) data[1];
+
+                SpinedBuffer.OfLong isl = new SpinedBuffer.OfLong();
+                for (long i : longs) {
+                    isl.accept(i);
+                }
+
+                spliterators.add(splitDescr("Arrays.s(array):" + name,
+                                            () -> Arrays.spliterator(longs)));
+                spliterators.add(splitDescr("Arrays.s(array,o,l):" + name,
+                                            () -> Arrays.spliterator(longs, 0, longs.length / 2)));
+
+                spliterators.add(splitDescr("SpinedBuffer.s():" + name,
+                                            () -> isl.spliterator()));
+
+                spliterators.add(splitDescr("Primitives.s(SpinedBuffer.iterator(), size):" + name,
+                                            () -> Spliterators.spliterator(isl.iterator(), longs.length, 0)));
+                spliterators.add(splitDescr("Primitives.s(SpinedBuffer.iterator()):" + name,
+                                            () -> Spliterators.spliteratorUnknownSize(isl.iterator(), 0)));
+
+                spliterators.add(splitDescr("LongStream.longRange(0,l):" + name,
+                                            () -> LongStream.range(0, longs.length).spliterator()));
+                spliterators.add(splitDescr("LongStream.longRangeClosed(0,l):" + name,
+                                            () -> LongStream.rangeClosed(0, longs.length).spliterator()));
+                // Need more!
+            }
+            spliteratorTestData = spliterators.toArray(new Object[0][]);
+        }
+
+    }
+
+    static <T> Object[] streamDataDescr(String description, Supplier<LongStream> s) {
+        return new Object[] { description, TestData.Factory.ofLongSupplier(description, s) };
+    }
+
+    static <T> Object[] splitDescr(String description, Supplier<Spliterator.OfLong> s) {
+        return new Object[] { description, s };
+    }
+
+    // Return an array of ( String name, LongStreamTestData )
+    @DataProvider(name = "LongStreamTestData")
+    public static Object[][] makeLongStreamTestData() {
+        return testData;
+    }
+
+    // returns an array of (String name, Supplier<PrimitiveSpliterator<Long>>)
+    @DataProvider(name = "LongSpliterator")
+    public static Object[][] spliteratorProvider() {
+        return spliteratorTestData;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/LongStreamTestScenario.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,232 @@
+/*
+ * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package java.util.stream;
+
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.PrimitiveIterator;
+import java.util.Set;
+import java.util.Spliterator;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.LongConsumer;
+
+/**
+ * Test scenarios for long streams.
+ *
+ * Each scenario is provided with a data source, a function that maps a fresh
+ * stream (as provided by the data source) to a new stream, and a sink to
+ * receive results.  Each scenario describes a different way of computing the
+ * stream contents.  The test driver will ensure that all scenarios produce
+ * the same output (modulo allowable differences in ordering).
+ */
+@SuppressWarnings({"rawtypes", "unchecked"})
+public enum LongStreamTestScenario implements OpTestCase.BaseStreamTestScenario {
+
+    STREAM_FOR_EACH(false) {
+        <T, S_IN extends BaseStream<T, S_IN>>
+        void run(TestData<T, S_IN> data, S_IN source, LongConsumer b, Function<S_IN, LongStream> m) {
+            LongStream s = m.apply(source);
+            if (s.isParallel()) {
+                s = s.sequential();
+            }
+            s.forEach(b);
+        }
+    },
+
+    STREAM_TO_ARRAY(false) {
+        <T, S_IN extends BaseStream<T, S_IN>>
+        void run(TestData<T, S_IN> data, S_IN source, LongConsumer b, Function<S_IN, LongStream> m) {
+            for (long t : m.apply(source).toArray()) {
+                b.accept(t);
+            }
+        }
+    },
+
+    STREAM_ITERATOR(false) {
+        <T, S_IN extends BaseStream<T, S_IN>>
+        void run(TestData<T, S_IN> data, S_IN source, LongConsumer b, Function<S_IN, LongStream> m) {
+            for (PrimitiveIterator.OfLong seqIter = m.apply(source).iterator(); seqIter.hasNext(); )
+                b.accept(seqIter.nextLong());
+        }
+    },
+
+    // Wrap as stream, and spliterate then iterate in pull mode
+    STREAM_SPLITERATOR(false) {
+        <T, S_IN extends BaseStream<T, S_IN>>
+        void run(TestData<T, S_IN> data, S_IN source, LongConsumer b, Function<S_IN, LongStream> m) {
+            for (Spliterator.OfLong spl = m.apply(source).spliterator(); spl.tryAdvance(b); ) {
+            }
+        }
+    },
+
+    // Wrap as stream, spliterate, then split a few times mixing advances with forEach
+    STREAM_SPLITERATOR_WITH_MIXED_TRAVERSE_AND_SPLIT(false) {
+        <T, S_IN extends BaseStream<T, S_IN>>
+        void run(TestData<T, S_IN> data, S_IN source, LongConsumer b, Function<S_IN, LongStream> m) {
+            SpliteratorTestHelper.mixedTraverseAndSplit(b, m.apply(source).spliterator());
+        }
+    },
+
+    // Wrap as stream, and spliterate then iterate in pull mode
+    STREAM_SPLITERATOR_FOREACH(false) {
+        <T, S_IN extends BaseStream<T, S_IN>>
+        void run(TestData<T, S_IN> data, S_IN source, LongConsumer b, Function<S_IN, LongStream> m) {
+            m.apply(source).spliterator().forEachRemaining(b);
+        }
+    },
+
+    PAR_STREAM_SEQUENTIAL_FOR_EACH(true) {
+        <T, S_IN extends BaseStream<T, S_IN>>
+        void run(TestData<T, S_IN> data, S_IN source, LongConsumer b, Function<S_IN, LongStream> m) {
+            m.apply(source).sequential().forEach(b);
+        }
+    },
+
+    // Wrap as parallel stream + forEachOrdered
+    PAR_STREAM_FOR_EACH_ORDERED(true) {
+        <T, S_IN extends BaseStream<T, S_IN>>
+        void run(TestData<T, S_IN> data, S_IN source, LongConsumer b, Function<S_IN, LongStream> m) {
+            // @@@ Want to explicitly select ordered equalator
+            m.apply(source).forEachOrdered(b);
+        }
+    },
+
+    // Wrap as stream, and spliterate then iterate sequentially
+    PAR_STREAM_SPLITERATOR(true) {
+        <T, S_IN extends BaseStream<T, S_IN>>
+        void run(TestData<T, S_IN> data, S_IN source, LongConsumer b, Function<S_IN, LongStream> m) {
+            for (Spliterator.OfLong spl = m.apply(source).spliterator(); spl.tryAdvance(b); ) {
+            }
+        }
+    },
+
+    // Wrap as stream, and spliterate then iterate sequentially
+    PAR_STREAM_SPLITERATOR_FOREACH(true) {
+        <T, S_IN extends BaseStream<T, S_IN>>
+        void run(TestData<T, S_IN> data, S_IN source, LongConsumer b, Function<S_IN, LongStream> m) {
+            m.apply(source).spliterator().forEachRemaining(b);
+        }
+    },
+
+    PAR_STREAM_TO_ARRAY(true) {
+        <T, S_IN extends BaseStream<T, S_IN>>
+        void run(TestData<T, S_IN> data, S_IN source, LongConsumer b, Function<S_IN, LongStream> m) {
+            for (long t : m.apply(source).toArray())
+                b.accept(t);
+        }
+    },
+
+    // Wrap as parallel stream, get the spliterator, wrap as a stream + toArray
+    PAR_STREAM_SPLITERATOR_STREAM_TO_ARRAY(true) {
+        <T, S_IN extends BaseStream<T, S_IN>>
+        void run(TestData<T, S_IN> data, S_IN source, LongConsumer b, Function<S_IN, LongStream> m) {
+            LongStream s = m.apply(source);
+            Spliterator.OfLong sp = s.spliterator();
+            LongStream ss = StreamSupport.longStream(() -> sp,
+                                                     StreamOpFlag.toCharacteristics(OpTestCase.getStreamFlags(s))
+                                                     | (sp.getExactSizeIfKnown() < 0 ? 0 : Spliterator.SIZED), true);
+            for (long t : ss.toArray())
+                b.accept(t);
+        }
+    },
+
+    PAR_STREAM_TO_ARRAY_CLEAR_SIZED(true) {
+        <T, S_IN extends BaseStream<T, S_IN>>
+        void run(TestData<T, S_IN> data, S_IN source, LongConsumer b, Function<S_IN, LongStream> m) {
+            S_IN pipe1 = (S_IN) OpTestCase.chain(source,
+                                                 new FlagDeclaringOp(StreamOpFlag.NOT_SIZED, data.getShape()));
+            LongStream pipe2 = m.apply(pipe1);
+
+            for (long t : pipe2.toArray())
+                b.accept(t);
+        }
+    },
+
+    // Wrap as parallel stream + forEach synchronizing
+    PAR_STREAM_FOR_EACH(true, false) {
+        <T, S_IN extends BaseStream<T, S_IN>>
+        void run(TestData<T, S_IN> data, S_IN source, LongConsumer b, Function<S_IN, LongStream> m) {
+            m.apply(source).forEach(e -> {
+                synchronized (data) {
+                    b.accept(e);
+                }
+            });
+        }
+    },
+
+    // Wrap as parallel stream + forEach synchronizing and clear SIZED flag
+    PAR_STREAM_FOR_EACH_CLEAR_SIZED(true, false) {
+        <T, S_IN extends BaseStream<T, S_IN>>
+        void run(TestData<T, S_IN> data, S_IN source, LongConsumer b, Function<S_IN, LongStream> m) {
+            S_IN pipe1 = (S_IN) OpTestCase.chain(source,
+                                                 new FlagDeclaringOp(StreamOpFlag.NOT_SIZED, data.getShape()));
+            m.apply(pipe1).forEach(e -> {
+                synchronized (data) {
+                    b.accept(e);
+                }
+            });
+        }
+    },
+    ;
+
+    // The set of scenarios that clean the SIZED flag
+    public static final Set<LongStreamTestScenario> CLEAR_SIZED_SCENARIOS = Collections.unmodifiableSet(
+            EnumSet.of(PAR_STREAM_TO_ARRAY_CLEAR_SIZED, PAR_STREAM_FOR_EACH_CLEAR_SIZED));
+
+    private boolean isParallel;
+
+    private final boolean isOrdered;
+
+    LongStreamTestScenario(boolean isParallel) {
+        this(isParallel, true);
+    }
+
+    LongStreamTestScenario(boolean isParallel, boolean isOrdered) {
+        this.isParallel = isParallel;
+        this.isOrdered = isOrdered;
+    }
+
+    public StreamShape getShape() {
+        return StreamShape.LONG_VALUE;
+    }
+
+    public boolean isParallel() {
+        return isParallel;
+    }
+
+    public boolean isOrdered() {
+        return isOrdered;
+    }
+
+    public <T, U, S_IN extends BaseStream<T, S_IN>, S_OUT extends BaseStream<U, S_OUT>>
+    void run(TestData<T, S_IN> data, Consumer<U> b, Function<S_IN, S_OUT> m) {
+        try (S_IN source = getStream(data)) {
+            run(data, source, (LongConsumer) b, (Function<S_IN, LongStream>) m);
+        }
+    }
+
+    abstract <T, S_IN extends BaseStream<T, S_IN>>
+    void run(TestData<T, S_IN> data, S_IN source, LongConsumer b, Function<S_IN, LongStream> m);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/OpTestCase.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,682 @@
+/*
+ * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package java.util.stream;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.EnumMap;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.Spliterator;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+import org.testng.annotations.Test;
+
+/**
+ * Base class for streams test cases.  Provides 'exercise' methods for taking
+ * lambdas that construct and modify streams, and evaluates them in different
+ * ways and asserts that they produce equivalent results.
+ */
+@Test
+public abstract class OpTestCase extends LoggingTestCase {
+
+    private final Map<StreamShape, Set<? extends BaseStreamTestScenario>> testScenarios;
+
+    protected OpTestCase() {
+        testScenarios = new EnumMap<>(StreamShape.class);
+        testScenarios.put(StreamShape.REFERENCE, Collections.unmodifiableSet(EnumSet.allOf(StreamTestScenario.class)));
+        testScenarios.put(StreamShape.INT_VALUE, Collections.unmodifiableSet(EnumSet.allOf(IntStreamTestScenario.class)));
+        testScenarios.put(StreamShape.LONG_VALUE, Collections.unmodifiableSet(EnumSet.allOf(LongStreamTestScenario.class)));
+        testScenarios.put(StreamShape.DOUBLE_VALUE, Collections.unmodifiableSet(EnumSet.allOf(DoubleStreamTestScenario.class)));
+    }
+
+    @SuppressWarnings("rawtypes")
+    public static int getStreamFlags(BaseStream s) {
+        return ((AbstractPipeline) s).getStreamFlags();
+    }
+
+    /**
+     * An asserter for results produced when exercising of stream or terminal
+     * tests.
+     *
+     * @param <R> the type of result to assert on
+     */
+    public interface ResultAsserter<R> {
+        /**
+         * Assert a result produced when exercising of stream or terminal
+         * test.
+         *
+         * @param actual the actual result
+         * @param expected the expected result
+         * @param isOrdered true if the pipeline is ordered
+         * @param isParallel true if the pipeline is parallel
+         */
+        void assertResult(R actual, R expected, boolean isOrdered, boolean isParallel);
+    }
+
+    // Exercise stream operations
+
+    public interface BaseStreamTestScenario {
+        StreamShape getShape();
+
+        boolean isParallel();
+
+        boolean isOrdered();
+
+        default <T, S_IN extends BaseStream<T, S_IN>>
+        S_IN getStream(TestData<T, S_IN> data) {
+            return isParallel()
+                   ? data.parallelStream()
+                   : data.stream();
+        }
+
+        <T, U, S_IN extends BaseStream<T, S_IN>, S_OUT extends BaseStream<U, S_OUT>>
+        void run(TestData<T, S_IN> data, Consumer<U> b, Function<S_IN, S_OUT> m);
+    }
+
+    protected <T, U, S_IN extends BaseStream<T, S_IN>, S_OUT extends BaseStream<U, S_OUT>>
+    Collection<U> exerciseOps(TestData<T, S_IN> data, Function<S_IN, S_OUT> m) {
+        return withData(data).stream(m).exercise();
+    }
+
+    // Run multiple versions of exercise(), returning the result of the first, and asserting that others return the same result
+    // If the first version is s -> s.foo(), can be used with s -> s.mapToInt(i -> i).foo().mapToObj(i -> i) to test all shape variants
+    @SafeVarargs
+    protected final<T, U, S_IN extends BaseStream<T, S_IN>, S_OUT extends BaseStream<U, S_OUT>>
+    Collection<U> exerciseOpsMulti(TestData<T, S_IN> data,
+                                   Function<S_IN, S_OUT>... ms) {
+        Collection<U> result = null;
+        for (Function<S_IN, S_OUT> m : ms) {
+            if (result == null)
+                result = withData(data).stream(m).exercise();
+            else {
+                Collection<U> r2 = withData(data).stream(m).exercise();
+                assertEquals(result, r2);
+            }
+        }
+        return result;
+    }
+
+    // Run multiple versions of exercise() for an Integer stream, returning the result of the first, and asserting that others return the same result
+    // Automates the conversion between Stream<Integer> and {Int,Long,Double}Stream and back, so client sites look like you are passing the same
+    // lambda four times, but in fact they are four different lambdas since they are transforming four different kinds of streams
+    protected final
+    Collection<Integer> exerciseOpsInt(TestData.OfRef<Integer> data,
+                                       Function<Stream<Integer>, Stream<Integer>> mRef,
+                                       Function<IntStream, IntStream> mInt,
+                                       Function<LongStream, LongStream> mLong,
+                                       Function<DoubleStream, DoubleStream> mDouble) {
+        @SuppressWarnings({ "rawtypes", "unchecked" })
+        Function<Stream<Integer>, Stream<Integer>>[] ms = new Function[4];
+        ms[0] = mRef;
+        ms[1] = s -> mInt.apply(s.mapToInt(e -> e)).mapToObj(e -> e);
+        ms[2] = s -> mLong.apply(s.mapToLong(e -> e)).mapToObj(e -> (int) e);
+        ms[3] = s -> mDouble.apply(s.mapToDouble(e -> e)).mapToObj(e -> (int) e);
+        return exerciseOpsMulti(data, ms);
+    }
+
+    // Run multiple versions of exercise() with multiple terminal operations for all kinds of stream, , and asserting against the expected result
+    // If the first version is s -> s.foo(), can be used with s -> s.mapToInt(i -> i).foo().mapToObj(i -> i) to test all shape variants
+    protected final<T, U, R, S_IN extends BaseStream<T, S_IN>, S_OUT extends BaseStream<U, S_OUT>>
+    void exerciseTerminalOpsMulti(TestData<T, S_IN> data,
+                                  R expected,
+                                  Map<String, Function<S_IN, S_OUT>> streams,
+                                  Map<String, Function<S_OUT, R>> terminals) {
+        for (Map.Entry<String, Function<S_IN, S_OUT>> se : streams.entrySet()) {
+            setContext("Intermediate stream", se.getKey());
+            for (Map.Entry<String, Function<S_OUT, R>> te : terminals.entrySet()) {
+                setContext("Terminal stream", te.getKey());
+                withData(data)
+                        .terminal(se.getValue(), te.getValue())
+                        .expectedResult(expected)
+                        .exercise();
+
+            }
+        }
+    }
+
+    // Run multiple versions of exercise() with multiple terminal operation for all kinds of stream, and asserting against the expected result
+    // Automates the conversion between Stream<Integer> and {Int,Long,Double}Stream and back, so client sites look like you are passing the same
+    // lambda four times, but in fact they are four different lambdas since they are transforming four different kinds of streams
+    protected final
+    void exerciseTerminalOpsInt(TestData<Integer, Stream<Integer>> data,
+                                Collection<Integer> expected,
+                                String desc,
+                                Function<Stream<Integer>, Stream<Integer>> mRef,
+                                Function<IntStream, IntStream> mInt,
+                                Function<LongStream, LongStream> mLong,
+                                Function<DoubleStream, DoubleStream> mDouble,
+                                Map<String, Function<Stream<Integer>, Collection<Integer>>> terminals) {
+
+        Map<String, Function<Stream<Integer>, Stream<Integer>>> m = new HashMap<>();
+        m.put("Ref " + desc, mRef);
+        m.put("Int " + desc, s -> mInt.apply(s.mapToInt(e -> e)).mapToObj(e -> e));
+        m.put("Long " + desc, s -> mLong.apply(s.mapToLong(e -> e)).mapToObj(e -> (int) e));
+        m.put("Double " + desc, s -> mDouble.apply(s.mapToDouble(e -> e)).mapToObj(e -> (int) e));
+
+        exerciseTerminalOpsMulti(data, expected, m, terminals);
+    }
+
+
+    protected <T, U, S_OUT extends BaseStream<U, S_OUT>>
+    Collection<U> exerciseOps(Collection<T> data, Function<Stream<T>, S_OUT> m) {
+        TestData.OfRef<T> data1 = TestData.Factory.ofCollection("Collection of type " + data.getClass().getName(), data);
+        return withData(data1).stream(m).exercise();
+    }
+
+    protected <T, U, S_OUT extends BaseStream<U, S_OUT>, I extends Iterable<U>>
+    Collection<U> exerciseOps(Collection<T> data, Function<Stream<T>, S_OUT> m, I expected) {
+        TestData.OfRef<T> data1 = TestData.Factory.ofCollection("Collection of type " + data.getClass().getName(), data);
+        return withData(data1).stream(m).expectedResult(expected).exercise();
+    }
+
+    @SuppressWarnings("unchecked")
+    protected <U, S_OUT extends BaseStream<U, S_OUT>>
+    Collection<U> exerciseOps(int[] data, Function<IntStream, S_OUT> m) {
+        return withData(TestData.Factory.ofArray("int array", data)).stream(m).exercise();
+    }
+
+    protected Collection<Integer> exerciseOps(int[] data, Function<IntStream, IntStream> m, int[] expected) {
+        TestData.OfInt data1 = TestData.Factory.ofArray("int array", data);
+        return withData(data1).stream(m).expectedResult(expected).exercise();
+    }
+
+    protected <T, S_IN extends BaseStream<T, S_IN>> DataStreamBuilder<T, S_IN> withData(TestData<T, S_IN> data) {
+        Objects.requireNonNull(data);
+        return new DataStreamBuilder<>(data);
+    }
+
+    @SuppressWarnings({"rawtypes", "unchecked"})
+    public class DataStreamBuilder<T, S_IN extends BaseStream<T, S_IN>> {
+        final TestData<T, S_IN> data;
+
+        private DataStreamBuilder(TestData<T, S_IN> data) {
+            this.data = Objects.requireNonNull(data);
+        }
+
+        public <U, S_OUT extends BaseStream<U, S_OUT>>
+        ExerciseDataStreamBuilder<T, U, S_IN, S_OUT> ops(IntermediateTestOp... ops) {
+            return new ExerciseDataStreamBuilder<>(data, (S_IN s) -> (S_OUT) chain(s, ops));
+        }
+
+        public <U, S_OUT extends BaseStream<U, S_OUT>> ExerciseDataStreamBuilder<T, U, S_IN, S_OUT>
+        stream(Function<S_IN, S_OUT> m) {
+            return new ExerciseDataStreamBuilder<>(data, m);
+        }
+
+        public <U, S_OUT extends BaseStream<U, S_OUT>> ExerciseDataStreamBuilder<T, U, S_IN, S_OUT>
+        stream(Function<S_IN, S_OUT> m, IntermediateTestOp<U, U> additionalOp) {
+            return new ExerciseDataStreamBuilder<>(data, s -> (S_OUT) chain(m.apply(s), additionalOp));
+        }
+
+        public <R> ExerciseDataTerminalBuilder<T, T, R, S_IN, S_IN>
+        terminal(Function<S_IN, R> terminalF) {
+            return new ExerciseDataTerminalBuilder<>(data, s -> s, terminalF);
+        }
+
+        public <U, R, S_OUT extends BaseStream<U, S_OUT>> ExerciseDataTerminalBuilder<T, U, R, S_IN, S_OUT>
+        terminal(Function<S_IN, S_OUT> streamF, Function<S_OUT, R> terminalF) {
+            return new ExerciseDataTerminalBuilder<>(data, streamF, terminalF);
+        }
+    }
+
+    @SuppressWarnings({"rawtypes", "unchecked"})
+    public class ExerciseDataStreamBuilder<T, U, S_IN extends BaseStream<T, S_IN>, S_OUT extends BaseStream<U, S_OUT>> {
+        final TestData<T, S_IN> data;
+        final Function<S_IN, S_OUT> m;
+        final StreamShape shape;
+
+        Set<BaseStreamTestScenario> testSet = new HashSet<>();
+
+        Collection<U> refResult;
+
+        Consumer<TestData<T, S_IN>> before = LambdaTestHelpers.bEmpty;
+
+        Consumer<TestData<T, S_IN>> after = LambdaTestHelpers.bEmpty;
+
+        ResultAsserter<Iterable<U>> resultAsserter = (act, exp, ord, par) -> {
+            if (par & !ord) {
+                LambdaTestHelpers.assertContentsUnordered(act, exp);
+            }
+            else {
+                LambdaTestHelpers.assertContentsEqual(act, exp);
+            }
+        };
+
+        private ExerciseDataStreamBuilder(TestData<T, S_IN> data, Function<S_IN, S_OUT> m) {
+            this.data = data;
+
+            this.m = Objects.requireNonNull(m);
+
+            this.shape = ((AbstractPipeline<?, U, ?>) m.apply(data.stream())).getOutputShape();
+
+            // Have to initiate from the output shape of the last stream
+            // This means the stream mapper is required first rather than last
+            testSet.addAll(testScenarios.get(shape));
+        }
+
+        //
+
+        public <I extends Iterable<U>> ExerciseDataStreamBuilder<T, U, S_IN, S_OUT> expectedResult(I expectedResult) {
+            List<U> l = new ArrayList<>();
+            expectedResult.forEach(l::add);
+            refResult = l;
+            return this;
+        }
+
+        public ExerciseDataStreamBuilder<T, U, S_IN, S_OUT> expectedResult(int[] expectedResult) {
+            List l = new ArrayList();
+            for (int anExpectedResult : expectedResult) {
+                l.add(anExpectedResult);
+            }
+            refResult = l;
+            return this;
+        }
+
+        public ExerciseDataStreamBuilder<T, U, S_IN, S_OUT> expectedResult(long[] expectedResult) {
+            List l = new ArrayList();
+            for (long anExpectedResult : expectedResult) {
+                l.add(anExpectedResult);
+            }
+            refResult = l;
+            return this;
+        }
+
+        public ExerciseDataStreamBuilder<T, U, S_IN, S_OUT> expectedResult(double[] expectedResult) {
+            List l = new ArrayList();
+            for (double anExpectedResult : expectedResult) {
+                l.add(anExpectedResult);
+            }
+            refResult = l;
+            return this;
+        }
+
+        public ExerciseDataStreamBuilder<T, U, S_IN, S_OUT> before(Consumer<TestData<T, S_IN>> before) {
+            this.before = Objects.requireNonNull(before);
+            return this;
+        }
+
+        public ExerciseDataStreamBuilder<T, U, S_IN, S_OUT> after(Consumer<TestData<T, S_IN>> after) {
+            this.after = Objects.requireNonNull(after);
+            return this;
+        }
+
+        public ExerciseDataStreamBuilder<T, U, S_IN, S_OUT> without(BaseStreamTestScenario... tests) {
+            return without(Arrays.asList(tests));
+        }
+
+        public ExerciseDataStreamBuilder<T, U, S_IN, S_OUT> without(Collection<? extends BaseStreamTestScenario> tests) {
+            for (BaseStreamTestScenario ts : tests) {
+                if (ts.getShape() == shape) {
+                    testSet.remove(ts);
+                }
+            }
+
+            if (testSet.isEmpty()) {
+                throw new IllegalStateException("Test scenario set is empty");
+            }
+
+            return this;
+        }
+
+        public ExerciseDataStreamBuilder<T, U, S_IN, S_OUT> with(BaseStreamTestScenario... tests) {
+            return with(Arrays.asList(tests));
+        }
+
+        public ExerciseDataStreamBuilder<T, U, S_IN, S_OUT> with(Collection<? extends BaseStreamTestScenario> tests) {
+            testSet = new HashSet<>();
+
+            for (BaseStreamTestScenario ts : tests) {
+                if (ts.getShape() == shape) {
+                    testSet.add(ts);
+                }
+            }
+
+            if (testSet.isEmpty()) {
+                throw new IllegalStateException("Test scenario set is empty");
+            }
+
+            return this;
+        }
+
+        public ExerciseDataStreamBuilder<T, U, S_IN, S_OUT> resultAsserter(ResultAsserter<Iterable<U>> resultAsserter) {
+            this.resultAsserter = resultAsserter;
+            return this;
+        }
+
+        // Build method
+
+        public Collection<U> exercise() {
+            final boolean isStreamOrdered;
+            if (refResult == null) {
+                // Induce the reference result
+                before.accept(data);
+                try (S_OUT sOut = m.apply(data.stream())) {
+                    isStreamOrdered = StreamOpFlag.ORDERED.isKnown(((AbstractPipeline) sOut).getStreamFlags());
+                    Node<U> refNodeResult = ((AbstractPipeline<?, U, ?>) sOut).evaluateToArrayNode(size -> (U[]) new Object[size]);
+                    refResult = LambdaTestHelpers.toBoxedList(refNodeResult.spliterator());
+                }
+                after.accept(data);
+            }
+            else {
+                try (S_OUT sOut = m.apply(data.stream())) {
+                    isStreamOrdered = StreamOpFlag.ORDERED.isKnown(((AbstractPipeline) sOut).getStreamFlags());
+                }
+            }
+
+            List<Error> errors = new ArrayList<>();
+            for (BaseStreamTestScenario test : testSet) {
+                try {
+                    before.accept(data);
+
+                    List<U> result = new ArrayList<>();
+                    test.run(data, LambdaTestHelpers.<U>toBoxingConsumer(result::add), m);
+
+                    Runnable asserter = () -> resultAsserter.assertResult(result, refResult, isStreamOrdered && test.isOrdered(), test.isParallel());
+
+                    if (refResult.size() > 1000) {
+                        LambdaTestHelpers.launderAssertion(
+                                asserter,
+                                () -> String.format("%n%s: [actual size=%d] != [expected size=%d]", test, result.size(), refResult.size()));
+                    }
+                    else {
+                        LambdaTestHelpers.launderAssertion(
+                                asserter,
+                                () -> String.format("%n%s: [actual] %s != [expected] %s", test, result, refResult));
+                    }
+
+                    after.accept(data);
+                } catch (Throwable t) {
+                    errors.add(new Error(String.format("%s: %s", test, t), t));
+                }
+            }
+
+            if (!errors.isEmpty()) {
+                StringBuilder sb = new StringBuilder();
+                int i = 1;
+                for (Error t : errors) {
+                    sb.append(i++).append(": ");
+                    if (t instanceof AssertionError) {
+                        sb.append(t).append("\n");
+                    }
+                    else {
+                        StringWriter sw = new StringWriter();
+                        PrintWriter pw = new PrintWriter(sw);
+
+                        t.getCause().printStackTrace(pw);
+                        pw.flush();
+                        sb.append(t).append("\n").append(sw);
+                    }
+                }
+                sb.append("--");
+
+                fail(String.format("%d failure(s) for test data: %s\n%s", i - 1, data.toString(), sb));
+            }
+
+            return refResult;
+        }
+    }
+
+    // Exercise terminal operations
+
+    interface BaseTerminalTestScenario<U, R, S_OUT extends BaseStream<U, S_OUT>> {
+        boolean requiresSingleStageSource();
+
+        boolean requiresParallelSource();
+
+        default R run(Function<S_OUT, R> terminalF, S_OUT source, StreamShape shape) {
+            return terminalF.apply(source);
+        }
+    }
+
+    @SuppressWarnings({"rawtypes", "unchecked"})
+    enum TerminalTestScenario implements BaseTerminalTestScenario {
+        SINGLE_SEQUENTIAL(true, false),
+
+        SINGLE_SEQUENTIAL_SHORT_CIRCUIT(true, false) {
+            @Override
+            public Object run(Function terminalF, BaseStream source, StreamShape shape) {
+                source = (BaseStream) chain(source, new ShortCircuitOp(shape));
+                return terminalF.apply(source);
+            }
+        },
+
+        SINGLE_PARALLEL(true, true),
+
+        ALL_SEQUENTIAL(false, false),
+
+        ALL_SEQUENTIAL_SHORT_CIRCUIT(false, false) {
+            @Override
+            public Object run(Function terminalF, BaseStream source, StreamShape shape) {
+                source = (BaseStream) chain(source, new ShortCircuitOp(shape));
+                return terminalF.apply(source);
+            }
+        },
+
+        ALL_PARALLEL(false, true),
+
+        ALL_PARALLEL_SEQUENTIAL(false, false) {
+            @Override
+            public Object run(Function terminalF, BaseStream source, StreamShape shape) {
+                return terminalF.apply(source.sequential());
+            }
+        },
+        ;
+
+        private final boolean requiresSingleStageSource;
+        private final boolean isParallel;
+
+        TerminalTestScenario(boolean requiresSingleStageSource, boolean isParallel) {
+            this.requiresSingleStageSource = requiresSingleStageSource;
+            this.isParallel = isParallel;
+        }
+
+        @Override
+        public boolean requiresSingleStageSource() {
+            return requiresSingleStageSource;
+        }
+
+        @Override
+        public boolean requiresParallelSource() {
+            return isParallel;
+        }
+
+    }
+
+    @SuppressWarnings({"rawtypes", "unchecked"})
+    public class ExerciseDataTerminalBuilder<T, U, R, S_IN extends BaseStream<T, S_IN>, S_OUT extends BaseStream<U, S_OUT>> {
+        final TestData<T, S_IN> data;
+        final Function<S_IN, S_OUT> streamF;
+        final Function<S_OUT, R> terminalF;
+
+        R refResult;
+
+        ResultAsserter<R> resultAsserter = (act, exp, ord, par) -> LambdaTestHelpers.assertContentsEqual(act, exp);
+
+        private ExerciseDataTerminalBuilder(TestData<T, S_IN> data, Function<S_IN, S_OUT> streamF, Function<S_OUT, R> terminalF) {
+            this.data = data;
+            this.streamF = Objects.requireNonNull(streamF);
+            this.terminalF = Objects.requireNonNull(terminalF);
+        }
+
+        //
+
+        public ExerciseDataTerminalBuilder<T, U, R, S_IN, S_OUT> expectedResult(R expectedResult) {
+            this.refResult = expectedResult;
+            return this;
+        }
+
+        public ExerciseDataTerminalBuilder<T, U, R, S_IN, S_OUT> equalator(BiConsumer<R, R> equalityAsserter) {
+            resultAsserter = (act, exp, ord, par) -> equalityAsserter.accept(act, exp);
+            return this;
+        }
+
+        public ExerciseDataTerminalBuilder<T, U, R, S_IN, S_OUT> resultAsserter(ResultAsserter<R> resultAsserter) {
+            this.resultAsserter = resultAsserter;
+            return this;
+        }
+
+        // Build method
+
+        public R exercise() {
+            boolean isOrdered;
+            StreamShape shape;
+            Node<U> node;
+            try (S_OUT out = streamF.apply(data.stream()).sequential()) {
+                AbstractPipeline ap = (AbstractPipeline) out;
+                isOrdered = StreamOpFlag.ORDERED.isKnown(ap.getStreamFlags());
+                shape = ap.getOutputShape();
+                // Sequentially collect the output that will be input to the terminal op
+                node = ap.evaluateToArrayNode(size -> (U[]) new Object[size]);
+            }
+
+            EnumSet<TerminalTestScenario> tests = EnumSet.allOf(TerminalTestScenario.class);
+            if (refResult == null) {
+                // Induce the reference result
+                S_OUT source = (S_OUT) createPipeline(shape, node.spliterator(),
+                                                      StreamOpFlag.IS_ORDERED | StreamOpFlag.IS_SIZED,
+                                                      false);
+
+                refResult = (R) TerminalTestScenario.SINGLE_SEQUENTIAL.run(terminalF, source, shape);
+                tests.remove(TerminalTestScenario.SINGLE_SEQUENTIAL);
+            }
+
+            for (BaseTerminalTestScenario test : tests) {
+                S_OUT source;
+                if (test.requiresSingleStageSource()) {
+                    source = (S_OUT) createPipeline(shape, node.spliterator(),
+                                                    StreamOpFlag.IS_ORDERED | StreamOpFlag.IS_SIZED,
+                                                    test.requiresParallelSource());
+                }
+                else {
+                    source = streamF.apply(test.requiresParallelSource()
+                                           ? data.parallelStream() : data.stream());
+                }
+
+                R result;
+                try (source) {
+                    result = (R) test.run(terminalF, source, shape);
+                }
+                LambdaTestHelpers.launderAssertion(
+                        () -> resultAsserter.assertResult(result, refResult, isOrdered, test.requiresParallelSource()),
+                        () -> String.format("%s: %s != %s", test, refResult, result));
+            }
+
+            return refResult;
+        }
+
+        AbstractPipeline createPipeline(StreamShape shape, Spliterator s, int flags, boolean parallel) {
+            switch (shape) {
+                case REFERENCE:    return new ReferencePipeline.Head<>(s, flags, parallel);
+                case INT_VALUE:    return new IntPipeline.Head(s, flags, parallel);
+                case LONG_VALUE:   return new LongPipeline.Head(s, flags, parallel);
+                case DOUBLE_VALUE: return new DoublePipeline.Head(s, flags, parallel);
+                default: throw new IllegalStateException("Unknown shape: " + shape);
+            }
+        }
+    }
+
+    protected <T, R> R exerciseTerminalOps(Collection<T> data, Function<Stream<T>, R> m, R expected) {
+        TestData.OfRef<T> data1
+                = TestData.Factory.ofCollection("Collection of type " + data.getClass().getName(), data);
+        return withData(data1).terminal(m).expectedResult(expected).exercise();
+    }
+
+    protected <T, R, S_IN extends BaseStream<T, S_IN>> R
+    exerciseTerminalOps(TestData<T, S_IN> data,
+                        Function<S_IN, R> terminalF) {
+        return withData(data).terminal(terminalF).exercise();
+    }
+
+    protected <T, U, R, S_IN extends BaseStream<T, S_IN>, S_OUT extends BaseStream<U, S_OUT>> R
+    exerciseTerminalOps(TestData<T, S_IN> data,
+                        Function<S_IN, S_OUT> streamF,
+                        Function<S_OUT, R> terminalF) {
+        return withData(data).terminal(streamF, terminalF).exercise();
+    }
+
+    //
+
+    @SuppressWarnings({"rawtypes", "unchecked"})
+    private static <T> AbstractPipeline<?, T, ?> chain(AbstractPipeline upstream, IntermediateTestOp<?, T> op) {
+        return (AbstractPipeline<?, T, ?>) IntermediateTestOp.chain(upstream, op);
+    }
+
+    @SuppressWarnings({"rawtypes", "unchecked"})
+    private static AbstractPipeline<?, ?, ?> chain(AbstractPipeline pipe, IntermediateTestOp... ops) {
+        for (IntermediateTestOp op : ops)
+            pipe = chain(pipe, op);
+        return pipe;
+    }
+
+    @SuppressWarnings("rawtypes")
+    private static <T> AbstractPipeline<?, T, ?> chain(BaseStream pipe, IntermediateTestOp<?, T> op) {
+        return chain((AbstractPipeline) pipe, op);
+    }
+
+    @SuppressWarnings("rawtypes")
+    public static AbstractPipeline<?, ?, ?> chain(BaseStream pipe, IntermediateTestOp... ops) {
+        return chain((AbstractPipeline) pipe, ops);
+    }
+
+    // Test data
+
+    static class ShortCircuitOp<T> implements StatelessTestOp<T,T> {
+        private final StreamShape shape;
+
+        ShortCircuitOp(StreamShape shape) {
+            this.shape = shape;
+        }
+
+        @Override
+        public Sink<T> opWrapSink(int flags, boolean parallel, Sink<T> sink) {
+            return sink;
+        }
+
+        @Override
+        public int opGetFlags() {
+            return StreamOpFlag.IS_SHORT_CIRCUIT;
+        }
+
+        @Override
+        public StreamShape outputShape() {
+            return shape;
+        }
+
+        @Override
+        public StreamShape inputShape() {
+            return shape;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/SpliteratorTestHelper.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,715 @@
+/*
+ * 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 java.util.stream;
+
+import org.testng.annotations.Test;
+
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Deque;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Spliterator;
+import java.util.function.*;
+
+import static org.testng.Assert.*;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.fail;
+
+/**
+ * Assertion methods for spliterators, to be called from other tests
+ */
+public class SpliteratorTestHelper {
+
+    public interface ContentAsserter<T> {
+        void assertContents(Collection<T> actual, Collection<T> expected, boolean isOrdered);
+    }
+
+    private static ContentAsserter<Object> DEFAULT_CONTENT_ASSERTER
+            = SpliteratorTestHelper::assertContents;
+
+    @SuppressWarnings("unchecked")
+    private static <T> ContentAsserter<T> defaultContentAsserter() {
+        return (ContentAsserter<T>) DEFAULT_CONTENT_ASSERTER;
+    }
+
+    public static void testSpliterator(Supplier<Spliterator<Integer>> supplier) {
+        testSpliterator(supplier, defaultContentAsserter());
+    }
+
+    public static void testSpliterator(Supplier<Spliterator<Integer>> supplier,
+                                       ContentAsserter<Integer> asserter) {
+        testSpliterator(supplier, (Consumer<Integer> b) -> b, asserter);
+    }
+
+    public static void testIntSpliterator(Supplier<Spliterator.OfInt> supplier) {
+        testIntSpliterator(supplier, defaultContentAsserter());
+    }
+
+    public static void testIntSpliterator(Supplier<Spliterator.OfInt> supplier,
+                                          ContentAsserter<Integer> asserter) {
+        class BoxingAdapter implements Consumer<Integer>, IntConsumer {
+            private final Consumer<Integer> b;
+
+            BoxingAdapter(Consumer<Integer> b) {
+                this.b = b;
+            }
+
+            @Override
+            public void accept(Integer value) {
+                throw new IllegalStateException();
+            }
+
+            @Override
+            public void accept(int value) {
+                b.accept(value);
+            }
+        }
+
+        testSpliterator(supplier, BoxingAdapter::new, asserter);
+    }
+
+    public static void testLongSpliterator(Supplier<Spliterator.OfLong> supplier) {
+        testLongSpliterator(supplier, defaultContentAsserter());
+    }
+
+    public static void testLongSpliterator(Supplier<Spliterator.OfLong> supplier,
+                                           ContentAsserter<Long> asserter) {
+        class BoxingAdapter implements Consumer<Long>, LongConsumer {
+            private final Consumer<Long> b;
+
+            BoxingAdapter(Consumer<Long> b) {
+                this.b = b;
+            }
+
+            @Override
+            public void accept(Long value) {
+                throw new IllegalStateException();
+            }
+
+            @Override
+            public void accept(long value) {
+                b.accept(value);
+            }
+        }
+
+        testSpliterator(supplier, BoxingAdapter::new, asserter);
+    }
+
+    public static void testDoubleSpliterator(Supplier<Spliterator.OfDouble> supplier) {
+        testDoubleSpliterator(supplier, defaultContentAsserter());
+    }
+
+    public static void testDoubleSpliterator(Supplier<Spliterator.OfDouble> supplier,
+                                             ContentAsserter<Double> asserter) {
+        class BoxingAdapter implements Consumer<Double>, DoubleConsumer {
+            private final Consumer<Double> b;
+
+            BoxingAdapter(Consumer<Double> b) {
+                this.b = b;
+            }
+
+            @Override
+            public void accept(Double value) {
+                throw new IllegalStateException();
+            }
+
+            @Override
+            public void accept(double value) {
+                b.accept(value);
+            }
+        }
+
+        testSpliterator(supplier, BoxingAdapter::new, asserter);
+    }
+
+    static <T, S extends Spliterator<T>> void testSpliterator(Supplier<S> supplier,
+                                                              UnaryOperator<Consumer<T>> boxingAdapter,
+                                                              ContentAsserter<T> asserter) {
+        ArrayList<T> fromForEach = new ArrayList<>();
+        Spliterator<T> spliterator = supplier.get();
+        Consumer<T> addToFromForEach = boxingAdapter.apply(fromForEach::add);
+        spliterator.forEachRemaining(addToFromForEach);
+
+        Collection<T> exp = Collections.unmodifiableList(fromForEach);
+
+        testNullPointerException(supplier);
+        testForEach(exp, supplier, boxingAdapter, asserter);
+        testTryAdvance(exp, supplier, boxingAdapter, asserter);
+        testMixedTryAdvanceForEach(exp, supplier, boxingAdapter, asserter);
+        testMixedTraverseAndSplit(exp, supplier, boxingAdapter, asserter);
+        testSplitAfterFullTraversal(supplier, boxingAdapter);
+        testSplitOnce(exp, supplier, boxingAdapter, asserter);
+        testSplitSixDeep(exp, supplier, boxingAdapter, asserter);
+        testSplitUntilNull(exp, supplier, boxingAdapter, asserter);
+    }
+
+    //
+
+    private static <T, S extends Spliterator<T>> void testNullPointerException(Supplier<S> s) {
+        S sp = s.get();
+        // Have to check instances and use casts to avoid tripwire messages and
+        // directly test the primitive methods
+        if (sp instanceof Spliterator.OfInt) {
+            Spliterator.OfInt psp = (Spliterator.OfInt) sp;
+            executeAndCatch(NullPointerException.class, () -> psp.forEachRemaining((IntConsumer) null));
+            executeAndCatch(NullPointerException.class, () -> psp.tryAdvance((IntConsumer) null));
+        }
+        else if (sp instanceof Spliterator.OfLong) {
+            Spliterator.OfLong psp = (Spliterator.OfLong) sp;
+            executeAndCatch(NullPointerException.class, () -> psp.forEachRemaining((LongConsumer) null));
+            executeAndCatch(NullPointerException.class, () -> psp.tryAdvance((LongConsumer) null));
+        }
+        else if (sp instanceof Spliterator.OfDouble) {
+            Spliterator.OfDouble psp = (Spliterator.OfDouble) sp;
+            executeAndCatch(NullPointerException.class, () -> psp.forEachRemaining((DoubleConsumer) null));
+            executeAndCatch(NullPointerException.class, () -> psp.tryAdvance((DoubleConsumer) null));
+        }
+        else {
+            executeAndCatch(NullPointerException.class, () -> sp.forEachRemaining(null));
+            executeAndCatch(NullPointerException.class, () -> sp.tryAdvance(null));
+        }
+    }
+
+    private static <T, S extends Spliterator<T>> void testForEach(
+            Collection<T> exp,
+            Supplier<S> supplier,
+            UnaryOperator<Consumer<T>> boxingAdapter,
+            ContentAsserter<T> asserter) {
+        S spliterator = supplier.get();
+        long sizeIfKnown = spliterator.getExactSizeIfKnown();
+        boolean isOrdered = spliterator.hasCharacteristics(Spliterator.ORDERED);
+
+        ArrayList<T> fromForEach = new ArrayList<>();
+        spliterator = supplier.get();
+        Consumer<T> addToFromForEach = boxingAdapter.apply(fromForEach::add);
+        spliterator.forEachRemaining(addToFromForEach);
+
+        // Assert that forEach now produces no elements
+        spliterator.forEachRemaining(boxingAdapter.apply(
+                e -> fail("Spliterator.forEach produced an element after spliterator exhausted: " + e)));
+        // Assert that tryAdvance now produce no elements
+        spliterator.tryAdvance(boxingAdapter.apply(
+                e -> fail("Spliterator.tryAdvance produced an element after spliterator exhausted: " + e)));
+
+        // assert that size, tryAdvance, and forEach are consistent
+        if (sizeIfKnown >= 0) {
+            assertEquals(sizeIfKnown, exp.size());
+        }
+        assertEquals(fromForEach.size(), exp.size());
+
+        asserter.assertContents(fromForEach, exp, isOrdered);
+    }
+
+    private static <T, S extends Spliterator<T>> void testTryAdvance(
+            Collection<T> exp,
+            Supplier<S> supplier,
+            UnaryOperator<Consumer<T>> boxingAdapter,
+            ContentAsserter<T> asserter) {
+        S spliterator = supplier.get();
+        long sizeIfKnown = spliterator.getExactSizeIfKnown();
+        boolean isOrdered = spliterator.hasCharacteristics(Spliterator.ORDERED);
+
+        spliterator = supplier.get();
+        ArrayList<T> fromTryAdvance = new ArrayList<>();
+        Consumer<T> addToFromTryAdvance = boxingAdapter.apply(fromTryAdvance::add);
+        while (spliterator.tryAdvance(addToFromTryAdvance)) { }
+
+        // Assert that forEach now produces no elements
+        spliterator.forEachRemaining(boxingAdapter.apply(
+                e -> fail("Spliterator.forEach produced an element after spliterator exhausted: " + e)));
+        // Assert that tryAdvance now produce no elements
+        spliterator.tryAdvance(boxingAdapter.apply(
+                e -> fail("Spliterator.tryAdvance produced an element after spliterator exhausted: " + e)));
+
+        // assert that size, tryAdvance, and forEach are consistent
+        if (sizeIfKnown >= 0) {
+            assertEquals(sizeIfKnown, exp.size());
+        }
+        assertEquals(fromTryAdvance.size(), exp.size());
+
+        asserter.assertContents(fromTryAdvance, exp, isOrdered);
+    }
+
+    private static <T, S extends Spliterator<T>> void testMixedTryAdvanceForEach(
+            Collection<T> exp,
+            Supplier<S> supplier,
+            UnaryOperator<Consumer<T>> boxingAdapter,
+            ContentAsserter<T> asserter) {
+        S spliterator = supplier.get();
+        long sizeIfKnown = spliterator.getExactSizeIfKnown();
+        boolean isOrdered = spliterator.hasCharacteristics(Spliterator.ORDERED);
+
+        // tryAdvance first few elements, then forEach rest
+        ArrayList<T> dest = new ArrayList<>();
+        spliterator = supplier.get();
+        Consumer<T> addToDest = boxingAdapter.apply(dest::add);
+        for (int i = 0; i < 10 && spliterator.tryAdvance(addToDest); i++) { }
+        spliterator.forEachRemaining(addToDest);
+
+        // Assert that forEach now produces no elements
+        spliterator.forEachRemaining(boxingAdapter.apply(
+                e -> fail("Spliterator.forEach produced an element after spliterator exhausted: " + e)));
+        // Assert that tryAdvance now produce no elements
+        spliterator.tryAdvance(boxingAdapter.apply(
+                e -> fail("Spliterator.tryAdvance produced an element after spliterator exhausted: " + e)));
+
+        if (sizeIfKnown >= 0) {
+            assertEquals(sizeIfKnown, dest.size());
+        }
+        assertEquals(dest.size(), exp.size());
+
+        asserter.assertContents(dest, exp, isOrdered);
+    }
+
+    private static <T, S extends Spliterator<T>> void testMixedTraverseAndSplit(
+            Collection<T> exp,
+            Supplier<S> supplier,
+            UnaryOperator<Consumer<T>> boxingAdapter,
+            ContentAsserter<T> asserter) {
+        S spliterator = supplier.get();
+        long sizeIfKnown = spliterator.getExactSizeIfKnown();
+        boolean isOrdered = spliterator.hasCharacteristics(Spliterator.ORDERED);
+
+        // tryAdvance first few elements, then forEach rest
+        ArrayList<T> dest = new ArrayList<>();
+        spliterator = supplier.get();
+        Consumer<T> b = boxingAdapter.apply(dest::add);
+
+        Spliterator<T> spl1, spl2, spl3;
+        spliterator.tryAdvance(b);
+        spl2 = spliterator.trySplit();
+        if (spl2 != null) {
+            spl2.tryAdvance(b);
+            spl1 = spl2.trySplit();
+            if (spl1 != null) {
+                spl1.tryAdvance(b);
+                spl1.forEachRemaining(b);
+            }
+            spl2.tryAdvance(b);
+            spl2.forEachRemaining(b);
+        }
+        spliterator.tryAdvance(b);
+        spl3 = spliterator.trySplit();
+        if (spl3 != null) {
+            spl3.tryAdvance(b);
+            spl3.forEachRemaining(b);
+        }
+        spliterator.tryAdvance(b);
+        spliterator.forEachRemaining(b);
+
+        if (sizeIfKnown >= 0) {
+            assertEquals(sizeIfKnown, dest.size());
+        }
+        assertEquals(dest.size(), exp.size());
+
+        asserter.assertContents(dest, exp, isOrdered);
+    }
+
+    private static <T, S extends Spliterator<T>> void testSplitAfterFullTraversal(
+            Supplier<S> supplier,
+            UnaryOperator<Consumer<T>> boxingAdapter) {
+        // Full traversal using tryAdvance
+        Spliterator<T> spliterator = supplier.get();
+        while (spliterator.tryAdvance(boxingAdapter.apply(e -> { }))) { }
+        Spliterator<T> split = spliterator.trySplit();
+        assertNull(split);
+
+        // Full traversal using forEach
+        spliterator = supplier.get();
+        spliterator.forEachRemaining(boxingAdapter.apply(e -> { }));
+        split = spliterator.trySplit();
+        assertNull(split);
+
+        // Full traversal using tryAdvance then forEach
+        spliterator = supplier.get();
+        spliterator.tryAdvance(boxingAdapter.apply(e -> { }));
+        spliterator.forEachRemaining(boxingAdapter.apply(e -> { }));
+        split = spliterator.trySplit();
+        assertNull(split);
+    }
+
+    private static <T, S extends Spliterator<T>> void testSplitOnce(
+            Collection<T> exp,
+            Supplier<S> supplier,
+            UnaryOperator<Consumer<T>> boxingAdapter,
+            ContentAsserter<T> asserter) {
+        S spliterator = supplier.get();
+        long sizeIfKnown = spliterator.getExactSizeIfKnown();
+        boolean isOrdered = spliterator.hasCharacteristics(Spliterator.ORDERED);
+
+        ArrayList<T> fromSplit = new ArrayList<>();
+        Spliterator<T> s1 = supplier.get();
+        Spliterator<T> s2 = s1.trySplit();
+        long s1Size = s1.getExactSizeIfKnown();
+        long s2Size = (s2 != null) ? s2.getExactSizeIfKnown() : 0;
+        Consumer<T> addToFromSplit = boxingAdapter.apply(fromSplit::add);
+        if (s2 != null)
+            s2.forEachRemaining(addToFromSplit);
+        s1.forEachRemaining(addToFromSplit);
+
+        if (sizeIfKnown >= 0) {
+            assertEquals(sizeIfKnown, fromSplit.size());
+            if (s1Size >= 0 && s2Size >= 0)
+                assertEquals(sizeIfKnown, s1Size + s2Size);
+        }
+
+        asserter.assertContents(fromSplit, exp, isOrdered);
+    }
+
+    private static <T, S extends Spliterator<T>> void testSplitSixDeep(
+            Collection<T> exp,
+            Supplier<S> supplier,
+            UnaryOperator<Consumer<T>> boxingAdapter,
+            ContentAsserter<T> asserter) {
+        S spliterator = supplier.get();
+        boolean isOrdered = spliterator.hasCharacteristics(Spliterator.ORDERED);
+
+        for (int depth=0; depth < 6; depth++) {
+            List<T> dest = new ArrayList<>();
+            spliterator = supplier.get();
+
+            assertSpliterator(spliterator);
+
+            // verify splitting with forEach
+            splitSixDeepVisitor(depth, 0, dest, spliterator, boxingAdapter, spliterator.characteristics(), false);
+            asserter.assertContents(dest, exp, isOrdered);
+
+            // verify splitting with tryAdvance
+            dest.clear();
+            spliterator = supplier.get();
+            splitSixDeepVisitor(depth, 0, dest, spliterator, boxingAdapter, spliterator.characteristics(), true);
+            asserter.assertContents(dest, exp, isOrdered);
+        }
+    }
+
+    private static <T, S extends Spliterator<T>>
+    void splitSixDeepVisitor(int depth, int curLevel,
+                             List<T> dest, S spliterator, UnaryOperator<Consumer<T>> boxingAdapter,
+                             int rootCharacteristics, boolean useTryAdvance) {
+        if (curLevel < depth) {
+            long beforeSize = spliterator.getExactSizeIfKnown();
+            Spliterator<T> split = spliterator.trySplit();
+            if (split != null) {
+                assertSpliterator(split, rootCharacteristics);
+                assertSpliterator(spliterator, rootCharacteristics);
+
+                if ((rootCharacteristics & Spliterator.SUBSIZED) != 0 &&
+                    (rootCharacteristics & Spliterator.SIZED) != 0) {
+                    assertEquals(beforeSize, split.estimateSize() + spliterator.estimateSize());
+                }
+                splitSixDeepVisitor(depth, curLevel + 1, dest, split, boxingAdapter, rootCharacteristics, useTryAdvance);
+            }
+            splitSixDeepVisitor(depth, curLevel + 1, dest, spliterator, boxingAdapter, rootCharacteristics, useTryAdvance);
+        }
+        else {
+            long sizeIfKnown = spliterator.getExactSizeIfKnown();
+            if (useTryAdvance) {
+                Consumer<T> addToDest = boxingAdapter.apply(dest::add);
+                int count = 0;
+                while (spliterator.tryAdvance(addToDest)) {
+                    ++count;
+                }
+
+                if (sizeIfKnown >= 0)
+                    assertEquals(sizeIfKnown, count);
+
+                // Assert that forEach now produces no elements
+                spliterator.forEachRemaining(boxingAdapter.apply(
+                        e -> fail("Spliterator.forEach produced an element after spliterator exhausted: " + e)));
+
+                Spliterator<T> split = spliterator.trySplit();
+                assertNull(split);
+            }
+            else {
+                List<T> leafDest = new ArrayList<>();
+                Consumer<T> addToLeafDest = boxingAdapter.apply(leafDest::add);
+                spliterator.forEachRemaining(addToLeafDest);
+
+                if (sizeIfKnown >= 0)
+                    assertEquals(sizeIfKnown, leafDest.size());
+
+                // Assert that forEach now produces no elements
+                spliterator.tryAdvance(boxingAdapter.apply(
+                        e -> fail("Spliterator.tryAdvance produced an element after spliterator exhausted: " + e)));
+
+                Spliterator<T> split = spliterator.trySplit();
+                assertNull(split);
+
+                dest.addAll(leafDest);
+            }
+        }
+    }
+
+    private static <T, S extends Spliterator<T>> void testSplitUntilNull(
+            Collection<T> exp,
+            Supplier<S> supplier,
+            UnaryOperator<Consumer<T>> boxingAdapter,
+            ContentAsserter<T> asserter) {
+        Spliterator<T> s = supplier.get();
+        boolean isOrdered = s.hasCharacteristics(Spliterator.ORDERED);
+        assertSpliterator(s);
+
+        List<T> splits = new ArrayList<>();
+        Consumer<T> c = boxingAdapter.apply(splits::add);
+
+        testSplitUntilNull(new SplitNode<T>(c, s));
+        asserter.assertContents(splits, exp, isOrdered);
+    }
+
+    private static class SplitNode<T> {
+        // Constant for every node
+        final Consumer<T> c;
+        final int rootCharacteristics;
+
+        final Spliterator<T> s;
+
+        SplitNode(Consumer<T> c, Spliterator<T> s) {
+            this(c, s.characteristics(), s);
+        }
+
+        private SplitNode(Consumer<T> c, int rootCharacteristics, Spliterator<T> s) {
+            this.c = c;
+            this.rootCharacteristics = rootCharacteristics;
+            this.s = s;
+        }
+
+        SplitNode<T> fromSplit(Spliterator<T> split) {
+            return new SplitNode<>(c, rootCharacteristics, split);
+        }
+    }
+
+    /**
+     * Set the maximum stack capacity to 0.25MB. This should be more than enough to detect a bad spliterator
+     * while not unduly disrupting test infrastructure given the test data sizes that are used are small.
+     * Note that j.u.c.ForkJoinPool sets the max queue size to 64M (1 << 26).
+     */
+    private static final int MAXIMUM_STACK_CAPACITY = 1 << 18; // 0.25MB
+
+    private static <T> void testSplitUntilNull(SplitNode<T> e) {
+        // Use an explicit stack to avoid a StackOverflowException when testing a Spliterator
+        // that when repeatedly split produces a right-balanced (and maybe degenerate) tree, or
+        // for a spliterator that is badly behaved.
+        Deque<SplitNode<T>> stack = new ArrayDeque<>();
+        stack.push(e);
+
+        int iteration = 0;
+        while (!stack.isEmpty()) {
+            assertTrue(iteration++ < MAXIMUM_STACK_CAPACITY, "Exceeded maximum stack modification count of 1 << 18");
+
+            e = stack.pop();
+            Spliterator<T> parentAndRightSplit = e.s;
+
+            long parentEstimateSize = parentAndRightSplit.estimateSize();
+            assertTrue(parentEstimateSize >= 0,
+                       String.format("Split size estimate %d < 0", parentEstimateSize));
+
+            long parentSize = parentAndRightSplit.getExactSizeIfKnown();
+            Spliterator<T> leftSplit = parentAndRightSplit.trySplit();
+            if (leftSplit == null) {
+                parentAndRightSplit.forEachRemaining(e.c);
+                continue;
+            }
+
+            assertSpliterator(leftSplit, e.rootCharacteristics);
+            assertSpliterator(parentAndRightSplit, e.rootCharacteristics);
+
+            if (parentEstimateSize != Long.MAX_VALUE && leftSplit.estimateSize() > 0
+                && parentAndRightSplit.estimateSize() > 0) {
+                assertTrue(leftSplit.estimateSize() < parentEstimateSize,
+                           String.format("Left split size estimate %d >= parent split size estimate %d",
+                                         leftSplit.estimateSize(), parentEstimateSize));
+                assertTrue(parentAndRightSplit.estimateSize() < parentEstimateSize,
+                           String.format("Right split size estimate %d >= parent split size estimate %d",
+                                         leftSplit.estimateSize(), parentEstimateSize));
+            }
+            else {
+                assertTrue(leftSplit.estimateSize() <= parentEstimateSize,
+                           String.format("Left split size estimate %d > parent split size estimate %d",
+                                         leftSplit.estimateSize(), parentEstimateSize));
+                assertTrue(parentAndRightSplit.estimateSize() <= parentEstimateSize,
+                           String.format("Right split size estimate %d > parent split size estimate %d",
+                                         leftSplit.estimateSize(), parentEstimateSize));
+            }
+
+            long leftSize = leftSplit.getExactSizeIfKnown();
+            long rightSize = parentAndRightSplit.getExactSizeIfKnown();
+            if (parentSize >= 0 && leftSize >= 0 && rightSize >= 0)
+                assertEquals(parentSize, leftSize + rightSize,
+                             String.format("exact left split size %d + exact right split size %d != parent exact split size %d",
+                                           leftSize, rightSize, parentSize));
+
+            // Add right side to stack first so left side is popped off first
+            stack.push(e.fromSplit(parentAndRightSplit));
+            stack.push(e.fromSplit(leftSplit));
+        }
+    }
+
+    private static void assertSpliterator(Spliterator<?> s, int rootCharacteristics) {
+        if ((rootCharacteristics & Spliterator.SUBSIZED) != 0) {
+            assertTrue(s.hasCharacteristics(Spliterator.SUBSIZED),
+                       "Child split is not SUBSIZED when root split is SUBSIZED");
+        }
+        assertSpliterator(s);
+    }
+
+    private static void assertSpliterator(Spliterator<?> s) {
+        if (s.hasCharacteristics(Spliterator.SUBSIZED)) {
+            assertTrue(s.hasCharacteristics(Spliterator.SIZED));
+        }
+        if (s.hasCharacteristics(Spliterator.SIZED)) {
+            assertTrue(s.estimateSize() != Long.MAX_VALUE);
+            assertTrue(s.getExactSizeIfKnown() >= 0);
+        }
+        try {
+            s.getComparator();
+            assertTrue(s.hasCharacteristics(Spliterator.SORTED));
+        } catch (IllegalStateException e) {
+            assertFalse(s.hasCharacteristics(Spliterator.SORTED));
+        }
+    }
+
+    private static<T> void assertContents(Collection<T> actual, Collection<T> expected, boolean isOrdered) {
+        if (isOrdered) {
+            assertEquals(actual, expected);
+        }
+        else {
+            LambdaTestHelpers.assertContentsUnordered(actual, expected);
+        }
+    }
+
+    private static void executeAndCatch(Class<? extends Exception> expected, Runnable r) {
+        Exception caught = null;
+        try {
+            r.run();
+        }
+        catch (Exception e) {
+            caught = e;
+        }
+
+        assertNotNull(caught,
+                      String.format("No Exception was thrown, expected an Exception of %s to be thrown",
+                                    expected.getName()));
+        assertTrue(expected.isInstance(caught),
+                   String.format("Exception thrown %s not an instance of %s",
+                                 caught.getClass().getName(), expected.getName()));
+    }
+
+    static<U> void mixedTraverseAndSplit(Consumer<U> b, Spliterator<U> splTop) {
+        Spliterator<U> spl1, spl2, spl3;
+        splTop.tryAdvance(b);
+        spl2 = splTop.trySplit();
+        if (spl2 != null) {
+            spl2.tryAdvance(b);
+            spl1 = spl2.trySplit();
+            if (spl1 != null) {
+                spl1.tryAdvance(b);
+                spl1.forEachRemaining(b);
+            }
+            spl2.tryAdvance(b);
+            spl2.forEachRemaining(b);
+        }
+        splTop.tryAdvance(b);
+        spl3 = splTop.trySplit();
+        if (spl3 != null) {
+            spl3.tryAdvance(b);
+            spl3.forEachRemaining(b);
+        }
+        splTop.tryAdvance(b);
+        splTop.forEachRemaining(b);
+    }
+
+    static void mixedTraverseAndSplit(IntConsumer b, Spliterator.OfInt splTop) {
+        Spliterator.OfInt spl1, spl2, spl3;
+        splTop.tryAdvance(b);
+        spl2 = splTop.trySplit();
+        if (spl2 != null) {
+            spl2.tryAdvance(b);
+            spl1 = spl2.trySplit();
+            if (spl1 != null) {
+                spl1.tryAdvance(b);
+                spl1.forEachRemaining(b);
+            }
+            spl2.tryAdvance(b);
+            spl2.forEachRemaining(b);
+        }
+        splTop.tryAdvance(b);
+        spl3 = splTop.trySplit();
+        if (spl3 != null) {
+            spl3.tryAdvance(b);
+            spl3.forEachRemaining(b);
+        }
+        splTop.tryAdvance(b);
+        splTop.forEachRemaining(b);
+    }
+    static void mixedTraverseAndSplit(LongConsumer b, Spliterator.OfLong splTop) {
+        Spliterator.OfLong spl1, spl2, spl3;
+        splTop.tryAdvance(b);
+        spl2 = splTop.trySplit();
+        if (spl2 != null) {
+            spl2.tryAdvance(b);
+            spl1 = spl2.trySplit();
+            if (spl1 != null) {
+                spl1.tryAdvance(b);
+                spl1.forEachRemaining(b);
+            }
+            spl2.tryAdvance(b);
+            spl2.forEachRemaining(b);
+        }
+        splTop.tryAdvance(b);
+        spl3 = splTop.trySplit();
+        if (spl3 != null) {
+            spl3.tryAdvance(b);
+            spl3.forEachRemaining(b);
+        }
+        splTop.tryAdvance(b);
+        splTop.forEachRemaining(b);
+    }
+
+    static void mixedTraverseAndSplit(DoubleConsumer b, Spliterator.OfDouble splTop) {
+        Spliterator.OfDouble spl1, spl2, spl3;
+        splTop.tryAdvance(b);
+        spl2 = splTop.trySplit();
+        if (spl2 != null) {
+            spl2.tryAdvance(b);
+            spl1 = spl2.trySplit();
+            if (spl1 != null) {
+                spl1.tryAdvance(b);
+                spl1.forEachRemaining(b);
+            }
+            spl2.tryAdvance(b);
+            spl2.forEachRemaining(b);
+        }
+        splTop.tryAdvance(b);
+        spl3 = splTop.trySplit();
+        if (spl3 != null) {
+            spl3.tryAdvance(b);
+            spl3.forEachRemaining(b);
+        }
+        splTop.tryAdvance(b);
+        splTop.forEachRemaining(b);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/StatefulTestOp.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,138 @@
+/*
+ * 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 java.util.stream;
+
+import java.util.Spliterator;
+import java.util.function.IntFunction;
+
+/**
+ * The base type for a stateful test operation.
+ */
+interface StatefulTestOp<E> extends IntermediateTestOp<E, E> {
+
+    @SuppressWarnings({"rawtypes", "unchecked"})
+    public static<T> AbstractPipeline chain(AbstractPipeline upstream,
+                                            StatefulTestOp op) {
+        switch (op.outputShape()) {
+            case REFERENCE:
+                return new ReferencePipeline.StatefulOp<Object, T>(upstream, op.inputShape(), op.opGetFlags()) {
+                    @Override
+                    Sink opWrapSink(int flags, Sink sink) {
+                        return op.opWrapSink(flags, isParallel(), sink);
+                    }
+
+                    @Override
+                    <P_IN> Spliterator<T> opEvaluateParallelLazy(PipelineHelper<T> helper,
+                                                                 Spliterator<P_IN> spliterator) {
+                        return op.opEvaluateParallelLazy(helper, spliterator);
+                    }
+
+                    @Override
+                    <P_IN> Node<T> opEvaluateParallel(PipelineHelper<T> helper,
+                                                      Spliterator<P_IN> spliterator,
+                                                      IntFunction<T[]> generator) {
+                        return op.opEvaluateParallel(helper, spliterator, generator);
+                    }
+                };
+            case INT_VALUE:
+                return new IntPipeline.StatefulOp<Object>(upstream, op.inputShape(), op.opGetFlags()) {
+                    @Override
+                    Sink opWrapSink(int flags, Sink sink) {
+                        return op.opWrapSink(flags, isParallel(), sink);
+                    }
+
+                    @Override
+                    <P_IN> Spliterator<Integer> opEvaluateParallelLazy(PipelineHelper<Integer> helper,
+                                                                 Spliterator<P_IN> spliterator) {
+                        return op.opEvaluateParallelLazy(helper, spliterator);
+                    }
+
+                    @Override
+                    <P_IN> Node<Integer> opEvaluateParallel(PipelineHelper<Integer> helper,
+                                                            Spliterator<P_IN> spliterator,
+                                                            IntFunction<Integer[]> generator) {
+                        return (Node<Integer>) op.opEvaluateParallel(helper, spliterator, generator);
+                    }
+                };
+            case LONG_VALUE:
+                return new LongPipeline.StatefulOp<Object>(upstream, op.inputShape(), op.opGetFlags()) {
+                    @Override
+                    Sink opWrapSink(int flags, Sink sink) {
+                        return op.opWrapSink(flags, isParallel(), sink);
+                    }
+
+                    @Override
+                    <P_IN> Spliterator<Long> opEvaluateParallelLazy(PipelineHelper<Long> helper,
+                                                                 Spliterator<P_IN> spliterator) {
+                        return op.opEvaluateParallelLazy(helper, spliterator);
+                    }
+
+                    @Override
+                    <P_IN> Node<Long> opEvaluateParallel(PipelineHelper<Long> helper,
+                                                         Spliterator<P_IN> spliterator,
+                                                         IntFunction<Long[]> generator) {
+                        return (Node<Long>) op.opEvaluateParallel(helper, spliterator, generator);
+                    }
+                };
+            case DOUBLE_VALUE:
+                return new DoublePipeline.StatefulOp<Object>(upstream, op.inputShape(), op.opGetFlags()) {
+                    @Override
+                    Sink opWrapSink(int flags, Sink sink) {
+                        return op.opWrapSink(flags, isParallel(), sink);
+                    }
+
+                    @Override
+                    <P_IN> Spliterator<Double> opEvaluateParallelLazy(PipelineHelper<Double> helper,
+                                                                    Spliterator<P_IN> spliterator) {
+                        return op.opEvaluateParallelLazy(helper, spliterator);
+                    }
+
+                    @Override
+                    <P_IN> Node<Double> opEvaluateParallel(PipelineHelper<Double> helper,
+                                                           Spliterator<P_IN> spliterator,
+                                                           IntFunction<Double[]> generator) {
+                        return (Node<Double>) op.opEvaluateParallel(helper, spliterator, generator);
+                    }
+                };
+            default: throw new IllegalStateException(op.outputShape().toString());
+        }
+    }
+
+    default StreamShape inputShape() { return StreamShape.REFERENCE; }
+
+    default StreamShape outputShape() { return StreamShape.REFERENCE; }
+
+    default int opGetFlags() { return 0; }
+
+    Sink<E> opWrapSink(int flags, boolean parallel, Sink<E> sink);
+
+    @SuppressWarnings("unchecked")
+    default <P_IN> Spliterator<E> opEvaluateParallelLazy(PipelineHelper<E> helper,
+                                                         Spliterator<P_IN> spliterator) {
+        return opEvaluateParallel(helper, spliterator, i -> (E[]) new Object[i]).spliterator();
+    }
+
+    <P_IN> Node<E> opEvaluateParallel(PipelineHelper<E> helper,
+                                      Spliterator<P_IN> spliterator,
+                                      IntFunction<E[]> generator);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/StatelessTestOp.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 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 java.util.stream;
+
+/**
+ * The base type of a stateless test operation
+ */
+interface StatelessTestOp<E_IN, E_OUT> extends IntermediateTestOp<E_IN, E_OUT> {
+
+    @SuppressWarnings({"rawtypes", "unchecked"})
+    public static<T> AbstractPipeline chain(AbstractPipeline upstream,
+                                            StatelessTestOp<?, T> op) {
+        int flags = op.opGetFlags();
+        switch (op.outputShape()) {
+            case REFERENCE:
+                return new ReferencePipeline.StatelessOp<Object, T>(upstream, op.inputShape(), flags) {
+                    public Sink opWrapSink(int flags, Sink<T> sink) {
+                        return op.opWrapSink(flags, isParallel(), sink);
+                    }
+                };
+            case INT_VALUE:
+                return new IntPipeline.StatelessOp<Object>(upstream, op.inputShape(), flags) {
+                    public Sink opWrapSink(int flags, Sink sink) {
+                        return op.opWrapSink(flags, isParallel(), sink);
+                    }
+                };
+            case LONG_VALUE:
+                return new LongPipeline.StatelessOp<Object>(upstream, op.inputShape(), flags) {
+                    @Override
+                    Sink opWrapSink(int flags, Sink sink) {
+                        return op.opWrapSink(flags, isParallel(), sink);
+                    }
+                };
+            case DOUBLE_VALUE:
+                return new DoublePipeline.StatelessOp<Object>(upstream, op.inputShape(), flags) {
+                    @Override
+                    Sink opWrapSink(int flags, Sink sink) {
+                        return op.opWrapSink(flags, isParallel(), sink);
+                    }
+                };
+            default: throw new IllegalStateException(op.outputShape().toString());
+        }
+    }
+
+    default StreamShape inputShape() { return StreamShape.REFERENCE; }
+
+    default StreamShape outputShape() { return StreamShape.REFERENCE; }
+
+    default int opGetFlags() { return 0; }
+
+    Sink<E_IN> opWrapSink(int flags, boolean parallel, Sink<E_OUT> sink);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/StreamOpFlagTestHelper.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,48 @@
+/*
+ * 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 java.util.stream;
+
+import java.util.EnumSet;
+
+public class StreamOpFlagTestHelper {
+
+    /** EnumSet containing stream flags */
+    private static final EnumSet<StreamOpFlag> allStreamFlags;
+
+    static {
+        allStreamFlags = EnumSet.allOf(StreamOpFlag.class);
+        for (StreamOpFlag f : EnumSet.allOf(StreamOpFlag.class))
+            if (!f.isStreamFlag())
+                allStreamFlags.remove(f);
+    }
+
+
+    static EnumSet<StreamOpFlag> allStreamFlags() {
+        // EnumSet is mutable
+        return allStreamFlags.clone();
+    }
+
+    public static boolean isStreamOrdered(Stream<?> s) {
+        return StreamOpFlag.ORDERED.isKnown(OpTestCase.getStreamFlags(s));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/StreamTestDataProvider.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,205 @@
+/*
+ * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package java.util.stream;
+
+import org.testng.annotations.DataProvider;
+
+import java.util.*;
+import java.util.Spliterators;
+import java.util.function.Supplier;
+
+/**
+ * StreamTestDataProvider
+ *
+ * @author Brian Goetz
+ */
+/** TestNG DataProvider for ref-valued streams */
+public class StreamTestDataProvider {
+    private static final Integer[] to0 = new Integer[0];
+    private static final Integer[] to1 = new Integer[1];
+    private static final Integer[] to10 = new Integer[10];
+    private static final Integer[] to100 = new Integer[100];
+    private static final Integer[] to1000 = new Integer[1000];
+    private static final Integer[] reversed = new Integer[100];
+    private static final Integer[] ones = new Integer[100];
+    private static final Integer[] twice = new Integer[200];
+    private static final Integer[] pseudoRandom;
+
+    private static final Object[][] testData;
+    private static final Object[][] withNullTestData;
+    private static final Object[][] spliteratorTestData;
+
+    static {
+        Integer[][] arrays = {to0, to1, to10, to100, to1000};
+        for (Integer[] arr : arrays) {
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = i;
+            }
+        }
+        for (int i = 0; i < reversed.length; i++) {
+            reversed[i] = reversed.length - i;
+        }
+        for (int i = 0; i < ones.length; i++) {
+            ones[i] = 1;
+        }
+        System.arraycopy(to100, 0, twice, 0, to100.length);
+        System.arraycopy(to100, 0, twice, to100.length, to100.length);
+        pseudoRandom = new Integer[LambdaTestHelpers.LONG_STRING.length()];
+        for (int i = 0; i < LambdaTestHelpers.LONG_STRING.length(); i++) {
+            pseudoRandom[i] = (int) LambdaTestHelpers.LONG_STRING.charAt(i);
+        }
+    }
+
+    static final Object[][] arrays = {
+            {"empty", to0},
+            {"0..1", to1},
+            {"0..10", to10},
+            {"0..100", to100},
+            {"0..1000", to1000},
+            {"100x[1]", ones},
+            {"2x[0..100]", twice},
+            {"reverse 0..100", reversed},
+            {"pseudorandom", pseudoRandom}
+    };
+
+    static {
+        {
+            List<Object[]> list = new ArrayList<>();
+            for (Object[] data : arrays) {
+                final Object name = data[0];
+                final Integer[] ints = (Integer[])data[1];
+                final List<Integer> intsAsList = Arrays.asList(ints);
+
+                list.add(arrayDataDescr("array:" + name, ints));
+                list.add(collectionDataDescr("ArrayList.asList:" + name, intsAsList));
+                list.add(collectionDataDescr("ArrayList:" + name, new ArrayList<>(intsAsList)));
+                list.add(streamDataDescr("DelegatingStream(ArrayList):" + name,
+                                         () -> new ArrayList<>(intsAsList).stream()));
+                List<Integer> aList = new ArrayList<>(intsAsList);
+                if (LambdaTestMode.isNormalMode()) {
+                    // Only include sub-lists for normal test execution mode
+                    // This data is serialization-hostile since the state of the
+                    // deserialized sub-list will be out of sync with the
+                    // enclosing list.
+                    list.add(collectionDataDescr("ArrayList.Sublist:" + name,
+                                                 (ints.length) <= 1 ? aList.subList(0, 0) : aList.subList(1, ints.length / 2)));
+                }
+                list.add(collectionDataDescr("LinkedList:" + name, new LinkedList<>(intsAsList)));
+                list.add(collectionDataDescr("HashSet:" + name, new HashSet<>(intsAsList)));
+                list.add(collectionDataDescr("LinkedHashSet:" + name, new LinkedHashSet<>(intsAsList)));
+                list.add(collectionDataDescr("TreeSet:" + name, new TreeSet<>(intsAsList)));
+                SpinedBuffer<Integer> spinedBuffer = new SpinedBuffer<>();
+                intsAsList.forEach(spinedBuffer);
+                list.add(sbDataDescr("SpinedBuffer:" + name, spinedBuffer));
+
+                // @@@ Add more
+            }
+            testData = list.toArray(new Object[0][]);
+        }
+
+        // Simple combination of numbers and null values, probably excessive but may catch
+        // errors for initialization/termination/sequence
+        // @@@ This is separate from the other data for now until nulls are consistently supported by
+        // all operations
+        {
+            List<Object[]> list = new ArrayList<>();
+            int size = 5;
+            for (int i = 0; i < (1 << size) - 2; i++) {
+                Integer[] content = new Integer[size];
+                for (int e = 0; e < size; e++) {
+                    content[e] = (i & (1 << e)) > 0 ? e + 1 : null;
+                }
+
+                // ORDERED
+                list.add(arrayDataDescr("array:" + i, content));
+                // not ORDERED, DISTINCT
+                list.add(collectionDataDescr("HashSet:" + i, new HashSet<>(Arrays.asList(content))));
+            }
+
+            withNullTestData = list.toArray(new Object[0][]);
+        }
+
+        {
+            List<Object[]> spliterators = new ArrayList<>();
+            for (Object[] data : arrays) {
+                final Object name = data[0];
+                final Integer[] ints = (Integer[])data[1];
+
+                spliterators.add(splitDescr("Arrays.s(array):" + name,
+                                            () -> Arrays.spliterator(ints)));
+                spliterators.add(splitDescr("arrays.s(array,o,l):" + name,
+                                            () -> Arrays.spliterator(ints, 0, ints.length/2)));
+                spliterators.add(splitDescr("SpinedBuffer.s():" + name,
+                                            () -> {
+                                                SpinedBuffer<Integer> sb = new SpinedBuffer<>();
+                                                for (Integer i : ints)
+                                                    sb.accept(i);
+                                                return sb.spliterator();
+                                            }));
+                spliterators.add(splitDescr("Iterators.s(Arrays.s(array).iterator(), size):" + name,
+                                            () -> Spliterators.spliterator(Arrays.asList(ints).iterator(), ints.length, 0)));
+                spliterators.add(splitDescr("Iterators.s(Arrays.s(array).iterator()):" + name,
+                                            () -> Spliterators.spliteratorUnknownSize(Arrays.asList(ints).iterator(), 0)));
+                // @@@ Add map and collection spliterators when spliterator() is exposed on Collection or Iterable
+            }
+            spliteratorTestData = spliterators.toArray(new Object[0][]);
+        }
+    }
+
+    static <T> Object[] arrayDataDescr(String description, T[] data) {
+        return new Object[] { description, TestData.Factory.ofArray(description, data)};
+    }
+
+    static <T> Object[] streamDataDescr(String description, Supplier<Stream<T>> supplier) {
+        return new Object[] { description, TestData.Factory.ofSupplier(description, supplier)};
+    }
+
+    static <T> Object[] collectionDataDescr(String description, Collection<T> data) {
+        return new Object[] { description, TestData.Factory.ofCollection(description, data)};
+    }
+
+    static <T> Object[] sbDataDescr(String description, SpinedBuffer<T> data) {
+        return new Object[] { description, TestData.Factory.ofSpinedBuffer(description, data)};
+    }
+
+    static <T> Object[] splitDescr(String description, Supplier<Spliterator<T>> ss) {
+        return new Object[] { description, ss };
+    }
+
+    // Return an array of ( String name, StreamTestData<Integer> )
+    @DataProvider(name = "StreamTestData<Integer>")
+    public static Object[][] makeStreamTestData() {
+        return testData;
+    }
+
+    @DataProvider(name = "withNull:StreamTestData<Integer>")
+    public static Object[][] makeStreamWithNullTestData() {
+        return withNullTestData;
+    }
+
+    // returns an array of (String name, Supplier<Spliterator<Integer>>)
+    @DataProvider(name = "Spliterator<Integer>")
+    public static Object[][] spliteratorProvider() {
+        return spliteratorTestData;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/StreamTestScenario.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,278 @@
+/*
+ * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package java.util.stream;
+
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.Spliterator;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+/**
+ * Test scenarios for reference streams.
+ *
+ * Each scenario is provided with a data source, a function that maps a fresh
+ * stream (as provided by the data source) to a new stream, and a sink to
+ * receive results.  Each scenario describes a different way of computing the
+ * stream contents.  The test driver will ensure that all scenarios produce
+ * the same output (modulo allowable differences in ordering).
+ */
+@SuppressWarnings({"rawtypes", "unchecked"})
+public enum StreamTestScenario implements OpTestCase.BaseStreamTestScenario {
+
+    STREAM_FOR_EACH(false) {
+        <T, U, S_IN extends BaseStream<T, S_IN>>
+        void run(TestData<T, S_IN> data, S_IN source, Consumer<U> b, Function<S_IN, Stream<U>> m) {
+            Stream<U> s = m.apply(source);
+            if (s.isParallel()) {
+                s = s.sequential();
+            }
+            s.forEach(b);
+        }
+    },
+
+    // Collec to list
+    STREAM_COLLECT(false) {
+        <T, U, S_IN extends BaseStream<T, S_IN>>
+        void run(TestData<T, S_IN> data, S_IN source, Consumer<U> b, Function<S_IN, Stream<U>> m) {
+            for (U t : m.apply(source).collect(Collectors.toList())) {
+                b.accept(t);
+            }
+        }
+    },
+
+    // To array
+    STREAM_TO_ARRAY(false) {
+        <T, U, S_IN extends BaseStream<T, S_IN>>
+        void run(TestData<T, S_IN> data, S_IN source, Consumer<U> b, Function<S_IN, Stream<U>> m) {
+            for (Object t : m.apply(source).toArray()) {
+                b.accept((U) t);
+            }
+        }
+    },
+
+    // Wrap as stream, and iterate in pull mode
+    STREAM_ITERATOR(false) {
+        <T, U, S_IN extends BaseStream<T, S_IN>>
+        void run(TestData<T, S_IN> data, S_IN source, Consumer<U> b, Function<S_IN, Stream<U>> m) {
+            for (Iterator<U> seqIter = m.apply(source).iterator(); seqIter.hasNext(); )
+                b.accept(seqIter.next());
+        }
+    },
+
+    // Wrap as stream, and spliterate then iterate in pull mode
+    STREAM_SPLITERATOR(false) {
+        <T, U, S_IN extends BaseStream<T, S_IN>>
+        void run(TestData<T, S_IN> data, S_IN source, Consumer<U> b, Function<S_IN, Stream<U>> m) {
+            for (Spliterator<U> spl = m.apply(source).spliterator(); spl.tryAdvance(b); ) {
+            }
+        }
+    },
+
+    // Wrap as stream, spliterate, then split a few times mixing advances with forEach
+    STREAM_SPLITERATOR_WITH_MIXED_TRAVERSE_AND_SPLIT(false) {
+        <T, U, S_IN extends BaseStream<T, S_IN>>
+        void run(TestData<T, S_IN> data, S_IN source, Consumer<U> b, Function<S_IN, Stream<U>> m) {
+            SpliteratorTestHelper.mixedTraverseAndSplit(b, m.apply(source).spliterator());
+        }
+    },
+
+    // Wrap as stream, and spliterate then iterate in pull mode
+    STREAM_SPLITERATOR_FOREACH(false) {
+        <T, U, S_IN extends BaseStream<T, S_IN>>
+        void run(TestData<T, S_IN> data, S_IN source, Consumer<U> b, Function<S_IN, Stream<U>> m) {
+            m.apply(source).spliterator().forEachRemaining(b);
+        }
+    },
+
+    // Wrap as parallel stream + sequential
+    PAR_STREAM_SEQUENTIAL_FOR_EACH(true) {
+        <T, U, S_IN extends BaseStream<T, S_IN>>
+        void run(TestData<T, S_IN> data, S_IN source, Consumer<U> b, Function<S_IN, Stream<U>> m) {
+            m.apply(source).sequential().forEach(b);
+        }
+    },
+
+    // Wrap as parallel stream + forEachOrdered
+    PAR_STREAM_FOR_EACH_ORDERED(true) {
+        <T, U, S_IN extends BaseStream<T, S_IN>>
+        void run(TestData<T, S_IN> data, S_IN source, Consumer<U> b, Function<S_IN, Stream<U>> m) {
+            // @@@ Want to explicitly select ordered equalator
+            m.apply(source).forEachOrdered(b);
+        }
+    },
+
+    // Wrap as stream, and spliterate then iterate sequentially
+    PAR_STREAM_SPLITERATOR(true) {
+        <T, U, S_IN extends BaseStream<T, S_IN>>
+        void run(TestData<T, S_IN> data, S_IN source, Consumer<U> b, Function<S_IN, Stream<U>> m) {
+            for (Spliterator<U> spl = m.apply(source).spliterator(); spl.tryAdvance(b); ) {
+            }
+        }
+    },
+
+    // Wrap as stream, and spliterate then iterate sequentially
+    PAR_STREAM_SPLITERATOR_FOREACH(true) {
+        <T, U, S_IN extends BaseStream<T, S_IN>>
+        void run(TestData<T, S_IN> data, S_IN source, Consumer<U> b, Function<S_IN, Stream<U>> m) {
+            m.apply(source).spliterator().forEachRemaining(b);
+        }
+    },
+
+    // Wrap as parallel stream + toArray
+    PAR_STREAM_TO_ARRAY(true) {
+        <T, U, S_IN extends BaseStream<T, S_IN>>
+        void run(TestData<T, S_IN> data, S_IN source, Consumer<U> b, Function<S_IN, Stream<U>> m) {
+            for (Object t : m.apply(source).toArray())
+                b.accept((U) t);
+        }
+    },
+
+    // Wrap as parallel stream, get the spliterator, wrap as a stream + toArray
+    PAR_STREAM_SPLITERATOR_STREAM_TO_ARRAY(true) {
+        <T, U, S_IN extends BaseStream<T, S_IN>>
+        void run(TestData<T, S_IN> data, S_IN source, Consumer<U> b, Function<S_IN, Stream<U>> m) {
+            Stream<U> s = m.apply(source);
+            Spliterator<U> sp = s.spliterator();
+            Stream<U> ss = StreamSupport.stream(() -> sp,
+                                                StreamOpFlag.toCharacteristics(OpTestCase.getStreamFlags(s))
+                                                | (sp.getExactSizeIfKnown() < 0 ? 0 : Spliterator.SIZED), true);
+            for (Object t : ss.toArray())
+                b.accept((U) t);
+        }
+    },
+
+    // Wrap as parallel stream + toArray and clear SIZED flag
+    PAR_STREAM_TO_ARRAY_CLEAR_SIZED(true) {
+        <T, U, S_IN extends BaseStream<T, S_IN>>
+        void run(TestData<T, S_IN> data, S_IN source, Consumer<U> b, Function<S_IN, Stream<U>> m) {
+            S_IN pipe1 = (S_IN) OpTestCase.chain(source,
+                                                 new FlagDeclaringOp(StreamOpFlag.NOT_SIZED, data.getShape()));
+            Stream<U> pipe2 = m.apply(pipe1);
+
+            for (Object t : pipe2.toArray())
+                b.accept((U) t);
+        }
+    },
+
+    // Wrap as parallel + collect to list
+    PAR_STREAM_COLLECT_TO_LIST(true) {
+        <T, U, S_IN extends BaseStream<T, S_IN>>
+        void run(TestData<T, S_IN> data, S_IN source, Consumer<U> b, Function<S_IN, Stream<U>> m) {
+            for (U u : m.apply(source).collect(Collectors.toList()))
+                b.accept(u);
+        }
+    },
+
+    // Wrap sequential as parallel, + collect to list
+    STREAM_TO_PAR_STREAM_COLLECT_TO_LIST(true) {
+        public <T, S_IN extends BaseStream<T, S_IN>>
+        S_IN getStream(TestData<T, S_IN> data) {
+            return data.stream().parallel();
+        }
+
+        <T, U, S_IN extends BaseStream<T, S_IN>>
+        void run(TestData<T, S_IN> data, S_IN source, Consumer<U> b, Function<S_IN, Stream<U>> m) {
+            for (U u : m.apply(source).collect(Collectors.toList()))
+                b.accept(u);
+        }
+    },
+
+    // Wrap parallel as sequential,, + collect
+    PAR_STREAM_TO_STREAM_COLLECT_TO_LIST(true) {
+        <T, U, S_IN extends BaseStream<T, S_IN>>
+        void run(TestData<T, S_IN> data, S_IN source, Consumer<U> b, Function<S_IN, Stream<U>> m) {
+            for (U u : m.apply(source).collect(Collectors.toList()))
+                b.accept(u);
+        }
+    },
+
+    // Wrap as parallel stream + forEach synchronizing
+    PAR_STREAM_FOR_EACH(true, false) {
+        <T, U, S_IN extends BaseStream<T, S_IN>>
+        void run(TestData<T, S_IN> data, S_IN source, Consumer<U> b, Function<S_IN, Stream<U>> m) {
+            m.apply(source).forEach(e -> {
+                synchronized (data) {
+                    b.accept(e);
+                }
+            });
+        }
+    },
+
+    // Wrap as parallel stream + forEach synchronizing and clear SIZED flag
+    PAR_STREAM_FOR_EACH_CLEAR_SIZED(true, false) {
+        <T, U, S_IN extends BaseStream<T, S_IN>>
+        void run(TestData<T, S_IN> data, S_IN source, Consumer<U> b, Function<S_IN, Stream<U>> m) {
+            S_IN pipe1 = (S_IN) OpTestCase.chain(source,
+                                                 new FlagDeclaringOp(StreamOpFlag.NOT_SIZED, data.getShape()));
+            m.apply(pipe1).forEach(e -> {
+                synchronized (data) {
+                    b.accept(e);
+                }
+            });
+        }
+    },
+    ;
+
+    // The set of scenarios that clean the SIZED flag
+    public static final Set<StreamTestScenario> CLEAR_SIZED_SCENARIOS = Collections.unmodifiableSet(
+            EnumSet.of(PAR_STREAM_TO_ARRAY_CLEAR_SIZED, PAR_STREAM_FOR_EACH_CLEAR_SIZED));
+
+    private final boolean isParallel;
+
+    private final boolean isOrdered;
+
+    StreamTestScenario(boolean isParallel) {
+        this(isParallel, true);
+    }
+
+    StreamTestScenario(boolean isParallel, boolean isOrdered) {
+        this.isParallel = isParallel;
+        this.isOrdered = isOrdered;
+    }
+
+    public StreamShape getShape() {
+        return StreamShape.REFERENCE;
+    }
+
+    public boolean isParallel() {
+        return isParallel;
+    }
+
+    public boolean isOrdered() {
+        return isOrdered;
+    }
+
+    public <T, U, S_IN extends BaseStream<T, S_IN>, S_OUT extends BaseStream<U, S_OUT>>
+    void run(TestData<T, S_IN> data, Consumer<U> b, Function<S_IN, S_OUT> m) {
+        try (S_IN source = getStream(data)) {
+            run(data, source, b, (Function<S_IN, Stream<U>>) m);
+        }
+    }
+
+    abstract <T, U, S_IN extends BaseStream<T, S_IN>>
+    void run(TestData<T, S_IN> data, S_IN source, Consumer<U> b, Function<S_IN, Stream<U>> m);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/TestData.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,355 @@
+/*
+ * 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 java.util.stream;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.PrimitiveIterator;
+import java.util.Spliterator;
+import java.util.Spliterators;
+import java.util.function.DoubleConsumer;
+import java.util.function.Function;
+import java.util.function.IntConsumer;
+import java.util.function.LongConsumer;
+import java.util.function.Supplier;
+import java.util.function.ToIntFunction;
+
+/** Describes a test data set for use in stream tests */
+public interface TestData<T, S extends BaseStream<T, S>>
+        extends Iterable<T> {
+
+    default int size() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    default Iterator<T> iterator() {
+        return Spliterators.iterator(spliterator());
+    }
+
+    Spliterator<T> spliterator();
+
+    default boolean isOrdered() {
+        return spliterator().hasCharacteristics(Spliterator.ORDERED);
+    }
+
+    StreamShape getShape();
+
+    default <A extends Collection<? super T>> A into(A target) {
+        spliterator().forEachRemaining(target::add);
+        return target;
+    }
+
+    S stream();
+
+    S parallelStream();
+
+    public interface OfRef<T> extends TestData<T, Stream<T>> { }
+
+    public interface OfInt extends TestData<Integer, IntStream> { }
+
+    public interface OfLong extends TestData<Long, LongStream> { }
+
+    public interface OfDouble extends TestData<Double, DoubleStream> { }
+
+    // @@@ Temporary garbage class to avoid triggering bugs with lambdas in static methods in interfaces
+    public static class Factory {
+        public static <T> OfRef<T> ofArray(String name, T[] array) {
+            return new AbstractTestData.RefTestData<>(name, array, Arrays::stream, a -> Arrays.stream(a).parallel(),
+                                                      Arrays::spliterator, a -> a.length);
+        }
+
+        public static <T> OfRef<T> ofCollection(String name, Collection<T> collection) {
+            return new AbstractTestData.RefTestData<>(name, collection, Collection::stream, Collection::parallelStream,
+                                                      Collection::spliterator, Collection::size);
+        }
+
+        public static <T> OfRef<T> ofSpinedBuffer(String name, SpinedBuffer<T> buffer) {
+            return new AbstractTestData.RefTestData<>(name, buffer,
+                                                      b -> StreamSupport.stream(b.spliterator(), false),
+                                                      b -> StreamSupport.stream(b.spliterator(), true),
+                                                      SpinedBuffer::spliterator,
+                                                      b -> (int) b.count());
+        }
+
+        public static <T> OfRef<T> ofSupplier(String name, Supplier<Stream<T>> supplier) {
+            return new AbstractTestData.RefTestData<>(name, supplier,
+                                                      Supplier::get,
+                                                      s -> s.get().parallel(),
+                                                      s -> s.get().spliterator(),
+                                                      s -> (int) s.get().spliterator().getExactSizeIfKnown());
+        }
+
+        public static <T> OfRef<T> ofRefNode(String name, Node<T> node) {
+            return new AbstractTestData.RefTestData<>(name, node,
+                                                      n -> StreamSupport.stream(n::spliterator, Spliterator.SIZED | Spliterator.ORDERED, false),
+                                                      n -> StreamSupport.stream(n::spliterator, Spliterator.SIZED | Spliterator.ORDERED, true),
+                                                      Node::spliterator,
+                                                      n -> (int) n.count());
+        }
+
+        // int factories
+        public static <T> OfInt ofArray(String name, int[] array) {
+            return new AbstractTestData.IntTestData<>(name, array, Arrays::stream, a -> Arrays.stream(a).parallel(),
+                                                      Arrays::spliterator, a -> a.length);
+        }
+
+        public static OfInt ofSpinedBuffer(String name, SpinedBuffer.OfInt buffer) {
+            return new AbstractTestData.IntTestData<>(name, buffer,
+                                                      b -> StreamSupport.intStream(b.spliterator(), false),
+                                                      b -> StreamSupport.intStream(b.spliterator(), true),
+                                                      SpinedBuffer.OfInt::spliterator,
+                                                      b -> (int) b.count());
+        }
+
+        public static OfInt ofIntSupplier(String name, Supplier<IntStream> supplier) {
+            return new AbstractTestData.IntTestData<>(name, supplier,
+                                                      Supplier::get,
+                                                      s -> s.get().parallel(),
+                                                      s -> s.get().spliterator(),
+                                                      s -> (int) s.get().spliterator().getExactSizeIfKnown());
+        }
+
+        public static OfInt ofNode(String name, Node.OfInt node) {
+            int characteristics = Spliterator.SIZED | Spliterator.ORDERED;
+            return new AbstractTestData.IntTestData<>(name, node,
+                                                      n -> StreamSupport.intStream(n::spliterator, characteristics, false),
+                                                      n -> StreamSupport.intStream(n::spliterator, characteristics, true),
+                                                      Node.OfInt::spliterator,
+                                                      n -> (int) n.count());
+        }
+
+        // long factories
+        public static <T> OfLong ofArray(String name, long[] array) {
+            return new AbstractTestData.LongTestData<>(name, array, Arrays::stream, a -> Arrays.stream(a).parallel(),
+                                                       Arrays::spliterator, a -> a.length);
+        }
+
+        public static OfLong ofSpinedBuffer(String name, SpinedBuffer.OfLong buffer) {
+            return new AbstractTestData.LongTestData<>(name, buffer,
+                                                      b -> StreamSupport.longStream(b.spliterator(), false),
+                                                      b -> StreamSupport.longStream(b.spliterator(), true),
+                                                      SpinedBuffer.OfLong::spliterator,
+                                                      b -> (int) b.count());
+        }
+
+        public static OfLong ofLongSupplier(String name, Supplier<LongStream> supplier) {
+            return new AbstractTestData.LongTestData<>(name, supplier,
+                                                      Supplier::get,
+                                                      s -> s.get().parallel(),
+                                                      s -> s.get().spliterator(),
+                                                      s -> (int) s.get().spliterator().getExactSizeIfKnown());
+        }
+
+        public static OfLong ofNode(String name, Node.OfLong node) {
+            int characteristics = Spliterator.SIZED | Spliterator.ORDERED;
+            return new AbstractTestData.LongTestData<>(name, node,
+                                                      n -> StreamSupport.longStream(n::spliterator, characteristics, false),
+                                                      n -> StreamSupport.longStream(n::spliterator, characteristics, true),
+                                                      Node.OfLong::spliterator,
+                                                      n -> (int) n.count());
+        }
+
+        // double factories
+        public static <T> OfDouble ofArray(String name, double[] array) {
+            return new AbstractTestData.DoubleTestData<>(name, array, Arrays::stream, a -> Arrays.stream(a).parallel(),
+                                                         Arrays::spliterator, a -> a.length);
+        }
+
+        public static OfDouble ofSpinedBuffer(String name, SpinedBuffer.OfDouble buffer) {
+            return new AbstractTestData.DoubleTestData<>(name, buffer,
+                                                         b -> StreamSupport.doubleStream(b.spliterator(), false),
+                                                         b -> StreamSupport.doubleStream(b.spliterator(), true),
+                                                         SpinedBuffer.OfDouble::spliterator,
+                                                         b -> (int) b.count());
+        }
+
+        public static OfDouble ofDoubleSupplier(String name, Supplier<DoubleStream> supplier) {
+            return new AbstractTestData.DoubleTestData<>(name, supplier,
+                                                         Supplier::get,
+                                                         s -> s.get().parallel(),
+                                                         s -> s.get().spliterator(),
+                                                         s -> (int) s.get().spliterator().getExactSizeIfKnown());
+        }
+
+        public static OfDouble ofNode(String name, Node.OfDouble node) {
+            int characteristics = Spliterator.SIZED | Spliterator.ORDERED;
+            return new AbstractTestData.DoubleTestData<>(name, node,
+                                                         n -> StreamSupport.doubleStream(n::spliterator, characteristics, false),
+                                                         n -> StreamSupport.doubleStream(n::spliterator, characteristics, true),
+                                                         Node.OfDouble::spliterator,
+                                                         n -> (int) n.count());
+        }
+    }
+
+
+    abstract class AbstractTestData<T, S extends BaseStream<T, S>,
+            T_STATE,
+                                    T_SPLITR extends Spliterator<T>>
+            implements TestData<T, S> {
+        private final String name;
+        private final StreamShape shape;
+        protected final T_STATE state;
+        private final ToIntFunction<T_STATE> sizeFn;
+        private final Function<T_STATE, S> streamFn;
+        private final Function<T_STATE, S> parStreamFn;
+        private final Function<T_STATE, T_SPLITR> splitrFn;
+
+        AbstractTestData(String name,
+                         StreamShape shape,
+                         T_STATE state,
+                         Function<T_STATE, S> streamFn,
+                         Function<T_STATE, S> parStreamFn,
+                         Function<T_STATE, T_SPLITR> splitrFn,
+                         ToIntFunction<T_STATE> sizeFn) {
+            this.name = name;
+            this.shape = shape;
+            this.state = state;
+            this.streamFn = streamFn;
+            this.parStreamFn = parStreamFn;
+            this.splitrFn = splitrFn;
+            this.sizeFn = sizeFn;
+        }
+
+        @Override
+        public StreamShape getShape() {
+            return shape;
+        }
+
+        @Override
+        public String toString() {
+            return getClass().getSimpleName() + "[" + name + "]";
+        }
+
+        @Override
+        public int size() {
+            return sizeFn.applyAsInt(state);
+        }
+
+        @Override
+        public T_SPLITR spliterator() {
+            return splitrFn.apply(state);
+        }
+
+        @Override
+        public S stream() {
+            return streamFn.apply(state);
+        }
+
+        @Override
+        public S parallelStream() {
+            return parStreamFn.apply(state);
+        }
+
+        public static class RefTestData<T, I>
+                extends AbstractTestData<T, Stream<T>, I, Spliterator<T>>
+                implements TestData.OfRef<T> {
+
+            protected RefTestData(String name,
+                                  I state,
+                                  Function<I, Stream<T>> streamFn,
+                                  Function<I, Stream<T>> parStreamFn,
+                                  Function<I, Spliterator<T>> splitrFn,
+                                  ToIntFunction<I> sizeFn) {
+                super(name, StreamShape.REFERENCE, state, streamFn, parStreamFn, splitrFn, sizeFn);
+            }
+
+        }
+
+        static class IntTestData<I>
+                extends AbstractTestData<Integer, IntStream, I, Spliterator.OfInt>
+                implements TestData.OfInt {
+
+            protected IntTestData(String name,
+                                  I state,
+                                  Function<I, IntStream> streamFn,
+                                  Function<I, IntStream> parStreamFn,
+                                  Function<I, Spliterator.OfInt> splitrFn,
+                                  ToIntFunction<I> sizeFn) {
+                super(name, StreamShape.INT_VALUE, state, streamFn, parStreamFn, splitrFn, sizeFn);
+            }
+
+            @Override
+            public PrimitiveIterator.OfInt iterator() {
+                return Spliterators.iterator(spliterator());
+            }
+
+            @Override
+            public <A extends Collection<? super Integer>> A into(A target) {
+                spliterator().forEachRemaining((IntConsumer) target::add);
+                return target;
+            }
+        }
+
+        static class LongTestData<I>
+                extends AbstractTestData<Long, LongStream, I, Spliterator.OfLong>
+                implements TestData.OfLong {
+
+            protected LongTestData(String name,
+                                   I state,
+                                   Function<I, LongStream> streamFn,
+                                   Function<I, LongStream> parStreamFn,
+                                   Function<I, Spliterator.OfLong> splitrFn,
+                                   ToIntFunction<I> sizeFn) {
+                super(name, StreamShape.LONG_VALUE, state, streamFn, parStreamFn, splitrFn, sizeFn);
+            }
+
+            @Override
+            public PrimitiveIterator.OfLong iterator() {
+                return Spliterators.iterator(spliterator());
+            }
+
+            @Override
+            public <A extends Collection<? super Long>> A into(A target) {
+                spliterator().forEachRemaining((LongConsumer) target::add);
+                return target;
+            }
+        }
+
+        static class DoubleTestData<I>
+                extends AbstractTestData<Double, DoubleStream, I, Spliterator.OfDouble>
+                implements OfDouble {
+
+            protected DoubleTestData(String name,
+                                     I state,
+                                     Function<I, DoubleStream> streamFn,
+                                     Function<I, DoubleStream> parStreamFn,
+                                     Function<I, Spliterator.OfDouble> splitrFn,
+                                     ToIntFunction<I> sizeFn) {
+                super(name, StreamShape.DOUBLE_VALUE, state, streamFn, parStreamFn, splitrFn, sizeFn);
+            }
+
+            @Override
+            public PrimitiveIterator.OfDouble iterator() {
+                return Spliterators.iterator(spliterator());
+            }
+
+            @Override
+            public <A extends Collection<? super Double>> A into(A target) {
+                spliterator().forEachRemaining((DoubleConsumer) target::add);
+                return target;
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/TestFlagExpectedOp.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,125 @@
+/*
+ * 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 java.util.stream;
+
+import org.testng.Assert;
+
+import java.util.EnumSet;
+
+class TestFlagExpectedOp<T> extends FlagDeclaringOp<T> {
+
+    static class Builder<T> {
+        final int flags;
+        StreamShape shape = StreamShape.REFERENCE;
+
+        EnumSet<StreamOpFlag> known = EnumSet.noneOf(StreamOpFlag.class);
+        EnumSet<StreamOpFlag> preserve = EnumSet.noneOf(StreamOpFlag.class);
+        EnumSet<StreamOpFlag> notKnown = EnumSet.noneOf(StreamOpFlag.class);
+
+        Builder(int flags) {
+            this.flags = flags;
+        }
+
+        Builder<T> known(EnumSet<StreamOpFlag> known) {
+            this.known = known;
+            return this;
+        }
+
+        Builder<T> preserve(EnumSet<StreamOpFlag> preserve) {
+            this.preserve = preserve;
+            return this;
+        }
+
+        Builder<T> notKnown(EnumSet<StreamOpFlag> notKnown) {
+            this.notKnown = notKnown;
+            return this;
+        }
+
+        Builder<T> shape(StreamShape shape) {
+            this.shape = shape;
+            return this;
+        }
+
+        TestFlagExpectedOp<T> build() {
+            return new TestFlagExpectedOp<>(flags, known, preserve, notKnown, shape);
+        }
+    }
+
+    final EnumSet<StreamOpFlag> known;
+    final EnumSet<StreamOpFlag> preserve;
+    final EnumSet<StreamOpFlag> notKnown;
+    final StreamShape shape;
+
+    TestFlagExpectedOp(int flags,
+                       EnumSet<StreamOpFlag> known,
+                       EnumSet<StreamOpFlag> preserve,
+                       EnumSet<StreamOpFlag> notKnown) {
+        this(flags, known, preserve, notKnown, StreamShape.REFERENCE);
+    }
+
+    TestFlagExpectedOp(int flags,
+                       EnumSet<StreamOpFlag> known,
+                       EnumSet<StreamOpFlag> preserve,
+                       EnumSet<StreamOpFlag> notKnown,
+                       StreamShape shape) {
+        super(flags);
+        this.known = known;
+        this.preserve = preserve;
+        this.notKnown = notKnown;
+        this.shape = shape;
+    }
+
+    @Override
+    public StreamShape outputShape() {
+        return shape;
+    }
+
+    @Override
+    public StreamShape inputShape() {
+        return shape;
+    }
+
+    @Override
+    @SuppressWarnings({"rawtypes", "unchecked"})
+    public Sink<T> opWrapSink(int flags, boolean parallel, Sink upstream) {
+        assertFlags(flags);
+        return upstream;
+    }
+
+    private void assertFlags(int flags) {
+        for (StreamOpFlag f : known) {
+            Assert.assertTrue(f.isKnown(flags),
+                              String.format("Flag %s is not known, but should be known.", f.toString()));
+        }
+
+        for (StreamOpFlag f : preserve) {
+            Assert.assertTrue(f.isPreserved(flags),
+                              String.format("Flag %s is not preserved, but should be preserved.", f.toString()));
+        }
+
+        for (StreamOpFlag f : notKnown) {
+            Assert.assertFalse(f.isKnown(flags),
+                               String.format("Flag %s is known, but should be not known.", f.toString()));
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/stream/bootlib/java.base/java/util/stream/ThowableHelper.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package java.util.stream;
+
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertTrue;
+
+public final class ThowableHelper {
+
+    public static void checkException(Class<? extends Exception> ce, Runnable r) {
+        Exception caught = null;
+        try {
+            r.run();
+        } catch (Exception e) {
+            caught = e;
+        }
+
+        assertNotNull(caught);
+        assertTrue(ce.isInstance(caught));
+    }
+
+    public static void checkNPE(Runnable r) {
+        checkException(NullPointerException.class, r);
+    }
+
+    public static void checkISE(Runnable r) {
+        checkException(IllegalStateException.class, r);
+    }
+}
--- a/jdk/test/java/util/stream/bootlib/java/util/stream/CollectorOps.java	Thu Nov 19 18:18:05 2015 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,113 +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 java.util.stream;
-
-import org.testng.Assert;
-
-import java.util.Spliterator;
-import java.util.function.IntFunction;
-
-/** Test helper class for java.util.stream test framework */
-public final class CollectorOps {
-    private CollectorOps() { }
-
-    public static <E_IN> StatefulTestOp<E_IN> collector() {
-        return new StatefulCollector<>(0, StreamShape.REFERENCE);
-    }
-
-    /* Utility classes for collecting output of intermediate pipeline stages */
-    public static class StatefulCollector<E_IN> implements StatefulTestOp<E_IN> {
-        private final int opFlags;
-        private final StreamShape inputShape;
-
-        public StatefulCollector(int opFlags, StreamShape inputShape) {
-            this.opFlags = opFlags;
-            this.inputShape = inputShape;
-        }
-
-        @Override
-        public StreamShape inputShape() {
-            return inputShape;
-        }
-
-        @Override
-        public StreamShape outputShape() {
-            return inputShape;
-        }
-
-        @Override
-        public int opGetFlags() {
-            return opFlags;
-        }
-
-        @Override
-        public Sink<E_IN> opWrapSink(int flags, boolean parallel, Sink<E_IN> sink) {
-            return sink;
-        }
-
-        @Override
-        public <P_IN> Node<E_IN> opEvaluateParallel(PipelineHelper<E_IN> helper,
-                                                    Spliterator<P_IN> spliterator,
-                                                    IntFunction<E_IN[]> generator) {
-            return helper.evaluate(spliterator, false, generator);
-        }
-    }
-
-    public static class TestParallelSizedOp<T> extends StatefulCollector<T> {
-        public TestParallelSizedOp() {
-            this(StreamShape.REFERENCE);
-        }
-
-        protected TestParallelSizedOp(StreamShape shape) {
-            super(0, shape);
-        }
-
-        @Override
-        public <P_IN> Node<T> opEvaluateParallel(PipelineHelper<T> helper,
-                                                 Spliterator<P_IN> spliterator,
-                                                 IntFunction<T[]> generator) {
-            int flags = helper.getStreamAndOpFlags();
-
-            Assert.assertTrue(StreamOpFlag.SIZED.isKnown(flags));
-            return super.opEvaluateParallel(helper, spliterator, generator);
-        }
-
-        public static class OfInt extends TestParallelSizedOp<Integer> {
-            public OfInt() {
-                super(StreamShape.INT_VALUE);
-            }
-        }
-
-        public static class OfLong extends TestParallelSizedOp<Long> {
-            public OfLong() {
-                super(StreamShape.LONG_VALUE);
-            }
-        }
-
-        public static class OfDouble extends TestParallelSizedOp<Double> {
-            public OfDouble() {
-                super(StreamShape.DOUBLE_VALUE);
-            }
-        }
-    }
-}
--- a/jdk/test/java/util/stream/bootlib/java/util/stream/DefaultMethodStreams.java	Thu Nov 19 18:18:05 2015 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,984 +0,0 @@
-/*
- * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package java.util.stream;
-
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.util.Comparator;
-import java.util.DoubleSummaryStatistics;
-import java.util.IntSummaryStatistics;
-import java.util.Iterator;
-import java.util.LongSummaryStatistics;
-import java.util.Optional;
-import java.util.OptionalDouble;
-import java.util.OptionalInt;
-import java.util.OptionalLong;
-import java.util.PrimitiveIterator;
-import java.util.Set;
-import java.util.Spliterator;
-import java.util.function.BiConsumer;
-import java.util.function.BiFunction;
-import java.util.function.BinaryOperator;
-import java.util.function.Consumer;
-import java.util.function.DoubleBinaryOperator;
-import java.util.function.DoubleConsumer;
-import java.util.function.DoubleFunction;
-import java.util.function.DoublePredicate;
-import java.util.function.DoubleToIntFunction;
-import java.util.function.DoubleToLongFunction;
-import java.util.function.DoubleUnaryOperator;
-import java.util.function.Function;
-import java.util.function.IntBinaryOperator;
-import java.util.function.IntConsumer;
-import java.util.function.IntFunction;
-import java.util.function.IntPredicate;
-import java.util.function.IntToDoubleFunction;
-import java.util.function.IntToLongFunction;
-import java.util.function.IntUnaryOperator;
-import java.util.function.LongBinaryOperator;
-import java.util.function.LongConsumer;
-import java.util.function.LongFunction;
-import java.util.function.LongPredicate;
-import java.util.function.LongToDoubleFunction;
-import java.util.function.LongToIntFunction;
-import java.util.function.LongUnaryOperator;
-import java.util.function.ObjDoubleConsumer;
-import java.util.function.ObjIntConsumer;
-import java.util.function.ObjLongConsumer;
-import java.util.function.Predicate;
-import java.util.function.Supplier;
-import java.util.function.ToDoubleFunction;
-
-import java.util.function.ToIntFunction;
-import java.util.function.ToLongFunction;
-
-import static java.util.stream.Collectors.*;
-
-public final class DefaultMethodStreams {
-
-    static {
-        // Verify that default methods are not overridden
-        verify(DefaultMethodRefStream.class);
-        verify(DefaultMethodIntStream.class);
-        verify(DefaultMethodLongStream.class);
-        verify(DefaultMethodDoubleStream.class);
-    }
-
-    static void verify(Class<?> del) {
-        // Find the stream interface
-        Class<?> s = Stream.of(del.getInterfaces())
-                .filter(c -> BaseStream.class.isAssignableFrom(c))
-                .findFirst().get();
-
-        // Get all default methods on the stream class
-        Set<String> dms = Stream.of(s.getMethods())
-                .filter(m -> !Modifier.isStatic(m.getModifiers()))
-                .filter(m -> !m.isBridge())
-                .filter(Method::isDefault)
-                .map(Method::getName)
-                .collect(toSet());
-
-        // Get all methods on the delegating class
-        Set<String> ims = Stream.of(del.getMethods())
-                .filter(m -> !Modifier.isStatic(m.getModifiers()))
-                .filter(m -> m.getDeclaringClass() == del)
-                .map(Method::getName)
-                .collect(toSet());
-
-        if (ims.stream().anyMatch(dms::contains)) {
-            throw new AssertionError(String.format("%s overrides default methods of %s\n", del, s));
-        }
-    }
-
-    /**
-     * Creates a stream that for the next operation either delegates to
-     * a default method on {@link Stream}, if present for that operation,
-     * otherwise delegates to an underlying stream.
-     *
-     * @param s the underlying stream to be delegated to for non-default
-     * methods.
-     * @param <T> the type of the stream elements
-     * @return the delegating stream
-     */
-    public static <T> Stream<T> delegateTo(Stream<T> s) {
-        return new DefaultMethodRefStream<>(s);
-    }
-
-    /**
-     * Creates a stream that for the next operation either delegates to
-     * a default method on {@link IntStream}, if present for that operation,
-     * otherwise delegates to an underlying stream.
-     *
-     * @param s the underlying stream to be delegated to for non-default
-     * methods.
-     * @return the delegating stream
-     */
-    public static IntStream delegateTo(IntStream s) {
-        return new DefaultMethodIntStream(s);
-    }
-
-    /**
-     * Creates a stream that for the next operation either delegates to
-     * a default method on {@link LongStream}, if present for that operation,
-     * otherwise delegates to an underlying stream.
-     *
-     * @param s the underlying stream to be delegated to for non-default
-     * methods.
-     * @return the delegating stream
-     */
-    public static LongStream delegateTo(LongStream s) {
-        return new DefaultMethodLongStream(s);
-    }
-
-    /**
-     * Creates a stream that for the next operation either delegates to
-     * a default method on {@link DoubleStream}, if present for that operation,
-     * otherwise delegates to an underlying stream.
-     *
-     * @param s the underlying stream to be delegated to for non-default
-     * methods.
-     * @return the delegating stream
-     */
-    public static DoubleStream delegateTo(DoubleStream s) {
-        return new DefaultMethodDoubleStream(s);
-    }
-
-    /**
-     * A stream that delegates the next operation to a default method, if
-     * present, or to the same operation of an underlying stream.
-     *
-     * @param <T> the type of the stream elements
-     */
-    static final class DefaultMethodRefStream<T> implements Stream<T> {
-        final Stream<T> s;
-
-        DefaultMethodRefStream(Stream<T> s) {
-            this.s = s;
-        }
-
-
-        // Delegating non-default methods
-
-        @Override
-        public Stream<T> filter(Predicate<? super T> predicate) {
-            return s.filter(predicate);
-        }
-
-        @Override
-        public <R> Stream<R> map(Function<? super T, ? extends R> mapper) {
-            return s.map(mapper);
-        }
-
-        @Override
-        public IntStream mapToInt(ToIntFunction<? super T> mapper) {
-            return s.mapToInt(mapper);
-        }
-
-        @Override
-        public LongStream mapToLong(ToLongFunction<? super T> mapper) {
-            return s.mapToLong(mapper);
-        }
-
-        @Override
-        public DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper) {
-            return s.mapToDouble(mapper);
-        }
-
-        @Override
-        public <R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper) {
-            return s.flatMap(mapper);
-        }
-
-        @Override
-        public IntStream flatMapToInt(Function<? super T, ? extends IntStream> mapper) {
-            return s.flatMapToInt(mapper);
-        }
-
-        @Override
-        public LongStream flatMapToLong(Function<? super T, ? extends LongStream> mapper) {
-            return s.flatMapToLong(mapper);
-        }
-
-        @Override
-        public DoubleStream flatMapToDouble(Function<? super T, ? extends DoubleStream> mapper) {
-            return s.flatMapToDouble(mapper);
-        }
-
-        @Override
-        public Stream<T> distinct() {
-            return s.distinct();
-        }
-
-        @Override
-        public Stream<T> sorted() {
-            return s.sorted();
-        }
-
-        @Override
-        public Stream<T> sorted(Comparator<? super T> comparator) {
-            return s.sorted(comparator);
-        }
-
-        @Override
-        public Stream<T> peek(Consumer<? super T> action) {
-            return s.peek(action);
-        }
-
-        @Override
-        public Stream<T> limit(long maxSize) {
-            return s.limit(maxSize);
-        }
-
-        @Override
-        public Stream<T> skip(long n) {
-            return s.skip(n);
-        }
-
-        @Override
-        public void forEach(Consumer<? super T> action) {
-            s.forEach(action);
-        }
-
-        @Override
-        public void forEachOrdered(Consumer<? super T> action) {
-            s.forEachOrdered(action);
-        }
-
-        @Override
-        public Object[] toArray() {
-            return s.toArray();
-        }
-
-        @Override
-        public <A> A[] toArray(IntFunction<A[]> generator) {
-            return s.toArray(generator);
-        }
-
-        @Override
-        public T reduce(T identity, BinaryOperator<T> accumulator) {
-            return s.reduce(identity, accumulator);
-        }
-
-        @Override
-        public Optional<T> reduce(BinaryOperator<T> accumulator) {
-            return s.reduce(accumulator);
-        }
-
-        @Override
-        public <U> U reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner) {
-            return s.reduce(identity, accumulator, combiner);
-        }
-
-        @Override
-        public <R> R collect(Supplier<R> supplier, BiConsumer<R, ? super T> accumulator, BiConsumer<R, R> combiner) {
-            return s.collect(supplier, accumulator, combiner);
-        }
-
-        @Override
-        public <R, A> R collect(Collector<? super T, A, R> collector) {
-            return s.collect(collector);
-        }
-
-        @Override
-        public Optional<T> min(Comparator<? super T> comparator) {
-            return s.min(comparator);
-        }
-
-        @Override
-        public Optional<T> max(Comparator<? super T> comparator) {
-            return s.max(comparator);
-        }
-
-        @Override
-        public long count() {
-            return s.count();
-        }
-
-        @Override
-        public boolean anyMatch(Predicate<? super T> predicate) {
-            return s.anyMatch(predicate);
-        }
-
-        @Override
-        public boolean allMatch(Predicate<? super T> predicate) {
-            return s.allMatch(predicate);
-        }
-
-        @Override
-        public boolean noneMatch(Predicate<? super T> predicate) {
-            return s.noneMatch(predicate);
-        }
-
-        @Override
-        public Optional<T> findFirst() {
-            return s.findFirst();
-        }
-
-        @Override
-        public Optional<T> findAny() {
-            return s.findAny();
-        }
-
-        @Override
-        public Iterator<T> iterator() {
-            return s.iterator();
-        }
-
-        @Override
-        public Spliterator<T> spliterator() {
-            return s.spliterator();
-        }
-
-        @Override
-        public boolean isParallel() {
-            return s.isParallel();
-        }
-
-        @Override
-        public Stream<T> sequential() {
-            return s.sequential();
-        }
-
-        @Override
-        public Stream<T> parallel() {
-            return s.parallel();
-        }
-
-        @Override
-        public Stream<T> unordered() {
-            return s.unordered();
-        }
-
-        @Override
-        public Stream<T> onClose(Runnable closeHandler) {
-            return s.onClose(closeHandler);
-        }
-
-        @Override
-        public void close() {
-            s.close();
-        }
-    }
-
-    static final class DefaultMethodIntStream implements IntStream {
-        final IntStream s;
-
-        public DefaultMethodIntStream(IntStream s) {
-            this.s = s;
-        }
-
-
-        // Delegating non-default methods
-
-        @Override
-        public IntStream filter(IntPredicate predicate) {
-            return s.filter(predicate);
-        }
-
-        @Override
-        public IntStream map(IntUnaryOperator mapper) {
-            return s.map(mapper);
-        }
-
-        @Override
-        public <U> Stream<U> mapToObj(IntFunction<? extends U> mapper) {
-            return s.mapToObj(mapper);
-        }
-
-        @Override
-        public LongStream mapToLong(IntToLongFunction mapper) {
-            return s.mapToLong(mapper);
-        }
-
-        @Override
-        public DoubleStream mapToDouble(IntToDoubleFunction mapper) {
-            return s.mapToDouble(mapper);
-        }
-
-        @Override
-        public IntStream flatMap(IntFunction<? extends IntStream> mapper) {
-            return s.flatMap(mapper);
-        }
-
-        @Override
-        public IntStream distinct() {
-            return s.distinct();
-        }
-
-        @Override
-        public IntStream sorted() {
-            return s.sorted();
-        }
-
-        @Override
-        public IntStream peek(IntConsumer action) {
-            return s.peek(action);
-        }
-
-        @Override
-        public IntStream limit(long maxSize) {
-            return s.limit(maxSize);
-        }
-
-        @Override
-        public IntStream skip(long n) {
-            return s.skip(n);
-        }
-
-        @Override
-        public void forEach(IntConsumer action) {
-            s.forEach(action);
-        }
-
-        @Override
-        public void forEachOrdered(IntConsumer action) {
-            s.forEachOrdered(action);
-        }
-
-        @Override
-        public int[] toArray() {
-            return s.toArray();
-        }
-
-        @Override
-        public int reduce(int identity, IntBinaryOperator op) {
-            return s.reduce(identity, op);
-        }
-
-        @Override
-        public OptionalInt reduce(IntBinaryOperator op) {
-            return s.reduce(op);
-        }
-
-        @Override
-        public <R> R collect(Supplier<R> supplier, ObjIntConsumer<R> accumulator, BiConsumer<R, R> combiner) {
-            return s.collect(supplier, accumulator, combiner);
-        }
-
-        @Override
-        public int sum() {
-            return s.sum();
-        }
-
-        @Override
-        public OptionalInt min() {
-            return s.min();
-        }
-
-        @Override
-        public OptionalInt max() {
-            return s.max();
-        }
-
-        @Override
-        public long count() {
-            return s.count();
-        }
-
-        @Override
-        public OptionalDouble average() {
-            return s.average();
-        }
-
-        @Override
-        public IntSummaryStatistics summaryStatistics() {
-            return s.summaryStatistics();
-        }
-
-        @Override
-        public boolean anyMatch(IntPredicate predicate) {
-            return s.anyMatch(predicate);
-        }
-
-        @Override
-        public boolean allMatch(IntPredicate predicate) {
-            return s.allMatch(predicate);
-        }
-
-        @Override
-        public boolean noneMatch(IntPredicate predicate) {
-            return s.noneMatch(predicate);
-        }
-
-        @Override
-        public OptionalInt findFirst() {
-            return s.findFirst();
-        }
-
-        @Override
-        public OptionalInt findAny() {
-            return s.findAny();
-        }
-
-        @Override
-        public LongStream asLongStream() {
-            return s.asLongStream();
-        }
-
-        @Override
-        public DoubleStream asDoubleStream() {
-            return s.asDoubleStream();
-        }
-
-        @Override
-        public Stream<Integer> boxed() {
-            return s.boxed();
-        }
-
-        @Override
-        public IntStream sequential() {
-            return s.sequential();
-        }
-
-        @Override
-        public IntStream parallel() {
-            return s.parallel();
-        }
-
-        @Override
-        public PrimitiveIterator.OfInt iterator() {
-            return s.iterator();
-        }
-
-        @Override
-        public Spliterator.OfInt spliterator() {
-            return s.spliterator();
-        }
-
-        @Override
-        public boolean isParallel() {
-            return s.isParallel();
-        }
-
-        @Override
-        public IntStream unordered() {
-            return s.unordered();
-        }
-
-        @Override
-        public IntStream onClose(Runnable closeHandler) {
-            return s.onClose(closeHandler);
-        }
-
-        @Override
-        public void close() {
-            s.close();
-        }
-    }
-
-    static final class DefaultMethodLongStream implements LongStream {
-        final LongStream s;
-
-        public DefaultMethodLongStream(LongStream s) {
-            this.s = s;
-        }
-
-
-        // Delegating non-default methods
-
-        @Override
-        public void forEach(LongConsumer action) {
-            s.forEach(action);
-        }
-
-        @Override
-        public LongStream filter(LongPredicate predicate) {
-            return s.filter(predicate);
-        }
-
-        @Override
-        public LongStream map(LongUnaryOperator mapper) {
-            return s.map(mapper);
-        }
-
-        @Override
-        public <U> Stream<U> mapToObj(LongFunction<? extends U> mapper) {
-            return s.mapToObj(mapper);
-        }
-
-        @Override
-        public IntStream mapToInt(LongToIntFunction mapper) {
-            return s.mapToInt(mapper);
-        }
-
-        @Override
-        public DoubleStream mapToDouble(LongToDoubleFunction mapper) {
-            return s.mapToDouble(mapper);
-        }
-
-        @Override
-        public LongStream flatMap(LongFunction<? extends LongStream> mapper) {
-            return s.flatMap(mapper);
-        }
-
-        @Override
-        public LongStream distinct() {
-            return s.distinct();
-        }
-
-        @Override
-        public LongStream sorted() {
-            return s.sorted();
-        }
-
-        @Override
-        public LongStream peek(LongConsumer action) {
-            return s.peek(action);
-        }
-
-        @Override
-        public LongStream limit(long maxSize) {
-            return s.limit(maxSize);
-        }
-
-        @Override
-        public LongStream skip(long n) {
-            return s.skip(n);
-        }
-
-        @Override
-        public void forEachOrdered(LongConsumer action) {
-            s.forEachOrdered(action);
-        }
-
-        @Override
-        public long[] toArray() {
-            return s.toArray();
-        }
-
-        @Override
-        public long reduce(long identity, LongBinaryOperator op) {
-            return s.reduce(identity, op);
-        }
-
-        @Override
-        public OptionalLong reduce(LongBinaryOperator op) {
-            return s.reduce(op);
-        }
-
-        @Override
-        public <R> R collect(Supplier<R> supplier, ObjLongConsumer<R> accumulator, BiConsumer<R, R> combiner) {
-            return s.collect(supplier, accumulator, combiner);
-        }
-
-        @Override
-        public long sum() {
-            return s.sum();
-        }
-
-        @Override
-        public OptionalLong min() {
-            return s.min();
-        }
-
-        @Override
-        public OptionalLong max() {
-            return s.max();
-        }
-
-        @Override
-        public long count() {
-            return s.count();
-        }
-
-        @Override
-        public OptionalDouble average() {
-            return s.average();
-        }
-
-        @Override
-        public LongSummaryStatistics summaryStatistics() {
-            return s.summaryStatistics();
-        }
-
-        @Override
-        public boolean anyMatch(LongPredicate predicate) {
-            return s.anyMatch(predicate);
-        }
-
-        @Override
-        public boolean allMatch(LongPredicate predicate) {
-            return s.allMatch(predicate);
-        }
-
-        @Override
-        public boolean noneMatch(LongPredicate predicate) {
-            return s.noneMatch(predicate);
-        }
-
-        @Override
-        public OptionalLong findFirst() {
-            return s.findFirst();
-        }
-
-        @Override
-        public OptionalLong findAny() {
-            return s.findAny();
-        }
-
-        @Override
-        public DoubleStream asDoubleStream() {
-            return s.asDoubleStream();
-        }
-
-        @Override
-        public Stream<Long> boxed() {
-            return s.boxed();
-        }
-
-        @Override
-        public LongStream sequential() {
-            return s.sequential();
-        }
-
-        @Override
-        public LongStream parallel() {
-            return s.parallel();
-        }
-
-        @Override
-        public PrimitiveIterator.OfLong iterator() {
-            return s.iterator();
-        }
-
-        @Override
-        public Spliterator.OfLong spliterator() {
-            return s.spliterator();
-        }
-
-        @Override
-        public boolean isParallel() {
-            return s.isParallel();
-        }
-
-        @Override
-        public LongStream unordered() {
-            return s.unordered();
-        }
-
-        @Override
-        public LongStream onClose(Runnable closeHandler) {
-            return s.onClose(closeHandler);
-        }
-
-        @Override
-        public void close() {
-            s.close();
-        }
-    }
-
-    static final class DefaultMethodDoubleStream implements DoubleStream {
-        final DoubleStream s;
-
-        public DefaultMethodDoubleStream(DoubleStream s) {
-            this.s = s;
-        }
-
-        @Override
-        public DoubleStream filter(DoublePredicate predicate) {
-            return s.filter(predicate);
-        }
-
-        @Override
-        public DoubleStream map(DoubleUnaryOperator mapper) {
-            return s.map(mapper);
-        }
-
-        @Override
-        public <U> Stream<U> mapToObj(DoubleFunction<? extends U> mapper) {
-            return s.mapToObj(mapper);
-        }
-
-        @Override
-        public IntStream mapToInt(DoubleToIntFunction mapper) {
-            return s.mapToInt(mapper);
-        }
-
-        @Override
-        public LongStream mapToLong(DoubleToLongFunction mapper) {
-            return s.mapToLong(mapper);
-        }
-
-        @Override
-        public DoubleStream flatMap(DoubleFunction<? extends DoubleStream> mapper) {
-            return s.flatMap(mapper);
-        }
-
-        @Override
-        public DoubleStream distinct() {
-            return s.distinct();
-        }
-
-        @Override
-        public DoubleStream sorted() {
-            return s.sorted();
-        }
-
-        @Override
-        public DoubleStream peek(DoubleConsumer action) {
-            return s.peek(action);
-        }
-
-        @Override
-        public DoubleStream limit(long maxSize) {
-            return s.limit(maxSize);
-        }
-
-        @Override
-        public DoubleStream skip(long n) {
-            return s.skip(n);
-        }
-
-        @Override
-        public void forEach(DoubleConsumer action) {
-            s.forEach(action);
-        }
-
-        @Override
-        public void forEachOrdered(DoubleConsumer action) {
-            s.forEachOrdered(action);
-        }
-
-        @Override
-        public double[] toArray() {
-            return s.toArray();
-        }
-
-        @Override
-        public double reduce(double identity, DoubleBinaryOperator op) {
-            return s.reduce(identity, op);
-        }
-
-        @Override
-        public OptionalDouble reduce(DoubleBinaryOperator op) {
-            return s.reduce(op);
-        }
-
-        @Override
-        public <R> R collect(Supplier<R> supplier, ObjDoubleConsumer<R> accumulator, BiConsumer<R, R> combiner) {
-            return s.collect(supplier, accumulator, combiner);
-        }
-
-        @Override
-        public double sum() {
-            return s.sum();
-        }
-
-        @Override
-        public OptionalDouble min() {
-            return s.min();
-        }
-
-        @Override
-        public OptionalDouble max() {
-            return s.max();
-        }
-
-        @Override
-        public long count() {
-            return s.count();
-        }
-
-        @Override
-        public OptionalDouble average() {
-            return s.average();
-        }
-
-        @Override
-        public DoubleSummaryStatistics summaryStatistics() {
-            return s.summaryStatistics();
-        }
-
-        @Override
-        public boolean anyMatch(DoublePredicate predicate) {
-            return s.anyMatch(predicate);
-        }
-
-        @Override
-        public boolean allMatch(DoublePredicate predicate) {
-            return s.allMatch(predicate);
-        }
-
-        @Override
-        public boolean noneMatch(DoublePredicate predicate) {
-            return s.noneMatch(predicate);
-        }
-
-        @Override
-        public OptionalDouble findFirst() {
-            return s.findFirst();
-        }
-
-        @Override
-        public OptionalDouble findAny() {
-            return s.findAny();
-        }
-
-        @Override
-        public Stream<Double> boxed() {
-            return s.boxed();
-        }
-
-        @Override
-        public DoubleStream sequential() {
-            return s.sequential();
-        }
-
-        @Override
-        public DoubleStream parallel() {
-            return s.parallel();
-        }
-
-        @Override
-        public PrimitiveIterator.OfDouble iterator() {
-            return s.iterator();
-        }
-
-        @Override
-        public Spliterator.OfDouble spliterator() {
-            return s.spliterator();
-        }
-
-        @Override
-        public boolean isParallel() {
-            return s.isParallel();
-        }
-
-        @Override
-        public DoubleStream unordered() {
-            return s.unordered();
-        }
-
-        @Override
-        public DoubleStream onClose(Runnable closeHandler) {
-            return s.onClose(closeHandler);
-        }
-
-        @Override
-        public void close() {
-            s.close();
-        }
-    }
-}
--- a/jdk/test/java/util/stream/bootlib/java/util/stream/DoubleStreamTestDataProvider.java	Thu Nov 19 18:18:05 2015 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,144 +0,0 @@
-/*
- * Copyright (c) 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 java.util.stream;
-
-import org.testng.annotations.DataProvider;
-
-import java.util.*;
-import java.util.Spliterators;
-import java.util.function.Supplier;
-
-/** TestNG DataProvider for double-valued streams */
-public class DoubleStreamTestDataProvider {
-    private static final double[] to0 = new double[0];
-    private static final double[] to1 = new double[1];
-    private static final double[] to10 = new double[10];
-    private static final double[] to100 = new double[100];
-    private static final double[] to1000 = new double[1000];
-    private static final double[] reversed = new double[100];
-    private static final double[] ones = new double[100];
-    private static final double[] twice = new double[200];
-    private static final double[] pseudoRandom;
-
-    private static final Object[][] testData;
-    private static final Object[][] spliteratorTestData;
-
-    static {
-        double[][] arrays = {to0, to1, to10, to100, to1000};
-        for (double[] arr : arrays) {
-            for (int i = 0; i < arr.length; i++) {
-                arr[i] = i;
-            }
-        }
-        for (int i = 0; i < reversed.length; i++) {
-            reversed[i] = reversed.length - i;
-        }
-        for (int i = 0; i < ones.length; i++) {
-            ones[i] = 1;
-        }
-        System.arraycopy(to100, 0, twice, 0, to100.length);
-        System.arraycopy(to100, 0, twice, to100.length, to100.length);
-        pseudoRandom = new double[LambdaTestHelpers.LONG_STRING.length()];
-        for (int i = 0; i < LambdaTestHelpers.LONG_STRING.length(); i++) {
-            pseudoRandom[i] = (double) LambdaTestHelpers.LONG_STRING.charAt(i);
-        }
-    }
-
-    static final Object[][] arrays = {
-            {"empty", to0},
-            {"0..1", to1},
-            {"0..10", to10},
-            {"0..100", to100},
-            {"0..1000", to1000},
-            {"100x[1]", ones},
-            {"2x[0..100]", twice},
-            {"reverse 0..100", reversed},
-            {"pseudorandom", pseudoRandom}
-    };
-
-    static {
-        {
-            List<Object[]> list = new ArrayList<>();
-            for (Object[] data : arrays) {
-                final Object name = data[0];
-                final double[] doubles = (double[]) data[1];
-
-                list.add(new Object[]{"array:" + name,
-                        TestData.Factory.ofArray("array:" + name, doubles)});
-
-                SpinedBuffer.OfDouble isl = new SpinedBuffer.OfDouble();
-                for (double i : doubles) {
-                    isl.accept(i);
-                }
-                list.add(new Object[]{"SpinedList:" + name,
-                        TestData.Factory.ofSpinedBuffer("SpinedList:" + name, isl)});
-            }
-            testData = list.toArray(new Object[0][]);
-        }
-
-        {
-            List<Object[]> spliterators = new ArrayList<>();
-            for (Object[] data : arrays) {
-                final Object name = data[0];
-                final double[] doubles = (double[]) data[1];
-
-                SpinedBuffer.OfDouble isl = new SpinedBuffer.OfDouble();
-                for (double i : doubles) {
-                    isl.accept(i);
-                }
-
-                spliterators.add(splitDescr("Arrays.s(array):" + name,
-                                            () -> Arrays.spliterator(doubles)));
-                spliterators.add(splitDescr("Arrays.s(array,o,l):" + name,
-                                            () -> Arrays.spliterator(doubles, 0, doubles.length / 2)));
-
-                spliterators.add(splitDescr("SpinedBuffer.s():" + name,
-                                            () -> isl.spliterator()));
-
-                spliterators.add(splitDescr("Primitives.s(SpinedBuffer.iterator(), size):" + name,
-                                            () -> Spliterators.spliterator(isl.iterator(), doubles.length, 0)));
-                spliterators.add(splitDescr("Primitives.s(SpinedBuffer.iterator()):" + name,
-                                            () -> Spliterators.spliteratorUnknownSize(isl.iterator(), 0)));
-                // Need more!
-            }
-            spliteratorTestData = spliterators.toArray(new Object[0][]);
-        }
-
-    }
-
-    static <T> Object[] splitDescr(String description, Supplier<Spliterator.OfDouble> s) {
-        return new Object[] { description, s };
-    }
-
-    // Return an array of ( String name, DoubleStreamTestData )
-    @DataProvider(name = "DoubleStreamTestData")
-    public static Object[][] makeDoubleStreamTestData() {
-        return testData;
-    }
-
-    // returns an array of (String name, Supplier<PrimitiveSpliterator<Double>>)
-    @DataProvider(name = "DoubleSpliterator")
-    public static Object[][] spliteratorProvider() {
-        return spliteratorTestData;
-    }
-}
--- a/jdk/test/java/util/stream/bootlib/java/util/stream/DoubleStreamTestScenario.java	Thu Nov 19 18:18:05 2015 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,232 +0,0 @@
-/*
- * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package java.util.stream;
-
-import java.util.Collections;
-import java.util.EnumSet;
-import java.util.PrimitiveIterator;
-import java.util.Set;
-import java.util.Spliterator;
-import java.util.function.Consumer;
-import java.util.function.DoubleConsumer;
-import java.util.function.Function;
-
-/**
- * Test scenarios for double streams.
- *
- * Each scenario is provided with a data source, a function that maps a fresh
- * stream (as provided by the data source) to a new stream, and a sink to
- * receive results.  Each scenario describes a different way of computing the
- * stream contents.  The test driver will ensure that all scenarios produce
- * the same output (modulo allowable differences in ordering).
- */
-@SuppressWarnings({"rawtypes", "unchecked"})
-public enum DoubleStreamTestScenario implements OpTestCase.BaseStreamTestScenario {
-
-    STREAM_FOR_EACH(false) {
-        <T, S_IN extends BaseStream<T, S_IN>>
-        void run(TestData<T, S_IN> data, S_IN source, DoubleConsumer b, Function<S_IN, DoubleStream> m) {
-            DoubleStream s = m.apply(source);
-            if (s.isParallel()) {
-                s = s.sequential();
-            }
-            s.forEach(b);
-        }
-    },
-
-    STREAM_TO_ARRAY(false) {
-        <T, S_IN extends BaseStream<T, S_IN>>
-        void run(TestData<T, S_IN> data, S_IN source, DoubleConsumer b, Function<S_IN, DoubleStream> m) {
-            for (double t : m.apply(source).toArray()) {
-                b.accept(t);
-            }
-        }
-    },
-
-    STREAM_ITERATOR(false) {
-        <T, S_IN extends BaseStream<T, S_IN>>
-        void run(TestData<T, S_IN> data, S_IN source, DoubleConsumer b, Function<S_IN, DoubleStream> m) {
-            for (PrimitiveIterator.OfDouble seqIter = m.apply(source).iterator(); seqIter.hasNext(); )
-                b.accept(seqIter.nextDouble());
-        }
-    },
-
-    // Wrap as stream, and spliterate then iterate in pull mode
-    STREAM_SPLITERATOR(false) {
-        <T, S_IN extends BaseStream<T, S_IN>>
-        void run(TestData<T, S_IN> data, S_IN source, DoubleConsumer b, Function<S_IN, DoubleStream> m) {
-            for (Spliterator.OfDouble spl = m.apply(source).spliterator(); spl.tryAdvance(b); ) {
-            }
-        }
-    },
-
-    // Wrap as stream, spliterate, then split a few times mixing advances with forEach
-    STREAM_SPLITERATOR_WITH_MIXED_TRAVERSE_AND_SPLIT(false) {
-        <T, S_IN extends BaseStream<T, S_IN>>
-        void run(TestData<T, S_IN> data, S_IN source, DoubleConsumer b, Function<S_IN, DoubleStream> m) {
-            SpliteratorTestHelper.mixedTraverseAndSplit(b, m.apply(source).spliterator());
-        }
-    },
-
-    // Wrap as stream, and spliterate then iterate in pull mode
-    STREAM_SPLITERATOR_FOREACH(false) {
-        <T, S_IN extends BaseStream<T, S_IN>>
-        void run(TestData<T, S_IN> data, S_IN source, DoubleConsumer b, Function<S_IN, DoubleStream> m) {
-            m.apply(source).spliterator().forEachRemaining(b);
-        }
-    },
-
-    PAR_STREAM_SEQUENTIAL_FOR_EACH(true) {
-        <T, S_IN extends BaseStream<T, S_IN>>
-        void run(TestData<T, S_IN> data, S_IN source, DoubleConsumer b, Function<S_IN, DoubleStream> m) {
-            m.apply(source).sequential().forEach(b);
-        }
-    },
-
-    // Wrap as parallel stream + forEachOrdered
-    PAR_STREAM_FOR_EACH_ORDERED(true) {
-        <T, S_IN extends BaseStream<T, S_IN>>
-        void run(TestData<T, S_IN> data, S_IN source, DoubleConsumer b, Function<S_IN, DoubleStream> m) {
-            // @@@ Want to explicitly select ordered equalator
-            m.apply(source).forEachOrdered(b);
-        }
-    },
-
-    // Wrap as stream, and spliterate then iterate sequentially
-    PAR_STREAM_SPLITERATOR(true) {
-        <T, S_IN extends BaseStream<T, S_IN>>
-        void run(TestData<T, S_IN> data, S_IN source, DoubleConsumer b, Function<S_IN, DoubleStream> m) {
-            for (Spliterator.OfDouble spl = m.apply(source).spliterator(); spl.tryAdvance(b); ) {
-            }
-        }
-    },
-
-    // Wrap as stream, and spliterate then iterate sequentially
-    PAR_STREAM_SPLITERATOR_FOREACH(true) {
-        <T, S_IN extends BaseStream<T, S_IN>>
-        void run(TestData<T, S_IN> data, S_IN source, DoubleConsumer b, Function<S_IN, DoubleStream> m) {
-            m.apply(source).spliterator().forEachRemaining(b);
-        }
-    },
-
-    PAR_STREAM_TO_ARRAY(true) {
-        <T, S_IN extends BaseStream<T, S_IN>>
-        void run(TestData<T, S_IN> data, S_IN source, DoubleConsumer b, Function<S_IN, DoubleStream> m) {
-            for (double t : m.apply(source).toArray())
-                b.accept(t);
-        }
-    },
-
-    // Wrap as parallel stream, get the spliterator, wrap as a stream + toArray
-    PAR_STREAM_SPLITERATOR_STREAM_TO_ARRAY(true) {
-        <T, S_IN extends BaseStream<T, S_IN>>
-        void run(TestData<T, S_IN> data, S_IN source, DoubleConsumer b, Function<S_IN, DoubleStream> m) {
-            DoubleStream s = m.apply(source);
-            Spliterator.OfDouble sp = s.spliterator();
-            DoubleStream ss = StreamSupport.doubleStream(() -> sp,
-                                                         StreamOpFlag.toCharacteristics(OpTestCase.getStreamFlags(s))
-                                                         | (sp.getExactSizeIfKnown() < 0 ? 0 : Spliterator.SIZED), true);
-            for (double t : ss.toArray())
-                b.accept(t);
-        }
-    },
-
-    PAR_STREAM_TO_ARRAY_CLEAR_SIZED(true) {
-        <T, S_IN extends BaseStream<T, S_IN>>
-        void run(TestData<T, S_IN> data, S_IN source, DoubleConsumer b, Function<S_IN, DoubleStream> m) {
-            S_IN pipe1 = (S_IN) OpTestCase.chain(source,
-                                                 new FlagDeclaringOp(StreamOpFlag.NOT_SIZED, data.getShape()));
-            DoubleStream pipe2 = m.apply(pipe1);
-
-            for (double t : pipe2.toArray())
-                b.accept(t);
-        }
-    },
-
-    // Wrap as parallel stream + forEach synchronizing
-    PAR_STREAM_FOR_EACH(true, false) {
-        <T, S_IN extends BaseStream<T, S_IN>>
-        void run(TestData<T, S_IN> data, S_IN source, DoubleConsumer b, Function<S_IN, DoubleStream> m) {
-            m.apply(source).forEach(e -> {
-                synchronized (data) {
-                    b.accept(e);
-                }
-            });
-        }
-    },
-
-    // Wrap as parallel stream + forEach synchronizing and clear SIZED flag
-    PAR_STREAM_FOR_EACH_CLEAR_SIZED(true, false) {
-        <T, S_IN extends BaseStream<T, S_IN>>
-        void run(TestData<T, S_IN> data, S_IN source, DoubleConsumer b, Function<S_IN, DoubleStream> m) {
-            S_IN pipe1 = (S_IN) OpTestCase.chain(source,
-                                                 new FlagDeclaringOp(StreamOpFlag.NOT_SIZED, data.getShape()));
-            m.apply(pipe1).forEach(e -> {
-                synchronized (data) {
-                    b.accept(e);
-                }
-            });
-        }
-    },
-    ;
-
-    // The set of scenarios that clean the SIZED flag
-    public static final Set<DoubleStreamTestScenario> CLEAR_SIZED_SCENARIOS = Collections.unmodifiableSet(
-            EnumSet.of(PAR_STREAM_TO_ARRAY_CLEAR_SIZED, PAR_STREAM_FOR_EACH_CLEAR_SIZED));
-
-    private boolean isParallel;
-
-    private final boolean isOrdered;
-
-    DoubleStreamTestScenario(boolean isParallel) {
-        this(isParallel, true);
-    }
-
-    DoubleStreamTestScenario(boolean isParallel, boolean isOrdered) {
-        this.isParallel = isParallel;
-        this.isOrdered = isOrdered;
-    }
-
-    public StreamShape getShape() {
-        return StreamShape.DOUBLE_VALUE;
-    }
-
-    public boolean isParallel() {
-        return isParallel;
-    }
-
-    public boolean isOrdered() {
-        return isOrdered;
-    }
-
-    public <T, U, S_IN extends BaseStream<T, S_IN>, S_OUT extends BaseStream<U, S_OUT>>
-    void run(TestData<T, S_IN> data, Consumer<U> b, Function<S_IN, S_OUT> m) {
-        try (S_IN source = getStream(data)) {
-            run(data, source, (DoubleConsumer) b, (Function<S_IN, DoubleStream>) m);
-        }
-    }
-
-    abstract <T, S_IN extends BaseStream<T, S_IN>>
-    void run(TestData<T, S_IN> data, S_IN source, DoubleConsumer b, Function<S_IN, DoubleStream> m);
-
-}
--- a/jdk/test/java/util/stream/bootlib/java/util/stream/FlagDeclaringOp.java	Thu Nov 19 18:18:05 2015 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,61 +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 java.util.stream;
-
-/**
- * An operation that injects or clears flags but otherwise performs no operation on elements.
- */
-@SuppressWarnings({"rawtypes", "unchecked"})
-public class FlagDeclaringOp<T> implements StatelessTestOp<T, T> {
-    private final int flags;
-    private final StreamShape shape;
-
-    public FlagDeclaringOp(int flags) {
-        this(flags, StreamShape.REFERENCE);
-    }
-
-    public FlagDeclaringOp(int flags, StreamShape shape) {
-        this.flags = flags;
-        this.shape = shape;
-    }
-
-    @Override
-    public StreamShape outputShape() {
-        return shape;
-    }
-
-    @Override
-    public StreamShape inputShape() {
-        return shape;
-    }
-
-    @Override
-    public int opGetFlags() {
-        return flags;
-    }
-
-    @Override
-    public Sink<T> opWrapSink(int flags, boolean parallel, Sink sink) {
-        return sink;
-    }
-}
--- a/jdk/test/java/util/stream/bootlib/java/util/stream/IntStreamTestDataProvider.java	Thu Nov 19 18:18:05 2015 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,158 +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 java.util.stream;
-
-import org.testng.annotations.DataProvider;
-
-import java.util.*;
-import java.util.Spliterators;
-import java.util.function.Supplier;
-
-/** TestNG DataProvider for int-valued streams */
-public class IntStreamTestDataProvider {
-    private static final int[] to0 = new int[0];
-    private static final int[] to1 = new int[1];
-    private static final int[] to10 = new int[10];
-    private static final int[] to100 = new int[100];
-    private static final int[] to1000 = new int[1000];
-    private static final int[] reversed = new int[100];
-    private static final int[] ones = new int[100];
-    private static final int[] twice = new int[200];
-    private static final int[] pseudoRandom;
-
-    private static final Object[][] testData;
-    private static final Object[][] spliteratorTestData;
-
-    static {
-        int[][] arrays = {to0, to1, to10, to100, to1000};
-        for (int[] arr : arrays) {
-            for (int i = 0; i < arr.length; i++) {
-                arr[i] = i;
-            }
-        }
-        for (int i = 0; i < reversed.length; i++) {
-            reversed[i] = reversed.length - i;
-        }
-        for (int i = 0; i < ones.length; i++) {
-            ones[i] = 1;
-        }
-        System.arraycopy(to100, 0, twice, 0, to100.length);
-        System.arraycopy(to100, 0, twice, to100.length, to100.length);
-        pseudoRandom = new int[LambdaTestHelpers.LONG_STRING.length()];
-        for (int i = 0; i < LambdaTestHelpers.LONG_STRING.length(); i++) {
-            pseudoRandom[i] = (int) LambdaTestHelpers.LONG_STRING.charAt(i);
-        }
-    }
-
-    static final Object[][] arrays = {
-            {"empty", to0},
-            {"0..1", to1},
-            {"0..10", to10},
-            {"0..100", to100},
-            {"0..1000", to1000},
-            {"100x[1]", ones},
-            {"2x[0..100]", twice},
-            {"reverse 0..100", reversed},
-            {"pseudorandom", pseudoRandom}
-    };
-
-    static {
-        {
-            List<Object[]> list = new ArrayList<>();
-            for (Object[] data : arrays) {
-                final Object name = data[0];
-                final int[] ints = (int[]) data[1];
-
-                list.add(new Object[]{"array:" +
-                                      name, TestData.Factory.ofArray("array:" + name, ints)});
-
-                SpinedBuffer.OfInt isl = new SpinedBuffer.OfInt();
-                for (int i : ints) {
-                    isl.accept(i);
-                }
-                list.add(new Object[]{"SpinedList:" + name,
-                         TestData.Factory.ofSpinedBuffer("SpinedList:" + name, isl)});
-
-                list.add(streamDataDescr("IntStream.intRange(0,l): " + ints.length,
-                                         () -> IntStream.range(0, ints.length)));
-                list.add(streamDataDescr("IntStream.rangeClosed(0,l): " + ints.length,
-                                         () -> IntStream.rangeClosed(0, ints.length)));
-            }
-            testData = list.toArray(new Object[0][]);
-        }
-
-        {
-            List<Object[]> spliterators = new ArrayList<>();
-            for (Object[] data : arrays) {
-                final Object name = data[0];
-                final int[] ints = (int[]) data[1];
-
-                SpinedBuffer.OfInt isl = new SpinedBuffer.OfInt();
-                for (int i : ints) {
-                    isl.accept(i);
-                }
-
-                spliterators.add(splitDescr("Arrays.s(array):" + name,
-                                            () -> Arrays.spliterator(ints)));
-                spliterators.add(splitDescr("Arrays.s(array,o,l):" + name,
-                                            () -> Arrays.spliterator(ints, 0, ints.length / 2)));
-
-                spliterators.add(splitDescr("SpinedBuffer.s():" + name,
-                                            () -> isl.spliterator()));
-
-                spliterators.add(splitDescr("Primitives.s(SpinedBuffer.iterator(), size):" + name,
-                                            () -> Spliterators.spliterator(isl.iterator(), ints.length, 0)));
-                spliterators.add(splitDescr("Primitives.s(SpinedBuffer.iterator()):" + name,
-                                            () -> Spliterators.spliteratorUnknownSize(isl.iterator(), 0)));
-
-                spliterators.add(splitDescr("IntStream.intRange(0,l):" + name,
-                                            () -> IntStream.range(0, ints.length).spliterator()));
-                spliterators.add(splitDescr("IntStream.intRangeClosed(0,l):" + name,
-                                            () -> IntStream.rangeClosed(0, ints.length).spliterator()));
-                // Need more!
-            }
-            spliteratorTestData = spliterators.toArray(new Object[0][]);
-        }
-
-    }
-
-    static <T> Object[] streamDataDescr(String description, Supplier<IntStream> s) {
-        return new Object[] { description, TestData.Factory.ofIntSupplier(description, s) };
-    }
-
-    static <T> Object[] splitDescr(String description, Supplier<Spliterator.OfInt> s) {
-        return new Object[] { description, s };
-    }
-
-    // Return an array of ( String name, IntStreamTestData )
-    @DataProvider(name = "IntStreamTestData")
-    public static Object[][] makeIntStreamTestData() {
-        return testData;
-    }
-
-    // returns an array of (String name, Supplier<PrimitiveSpliterator<Integer>>)
-    @DataProvider(name = "IntSpliterator")
-    public static Object[][] spliteratorProvider() {
-        return spliteratorTestData;
-    }
-}
--- a/jdk/test/java/util/stream/bootlib/java/util/stream/IntStreamTestScenario.java	Thu Nov 19 18:18:05 2015 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,233 +0,0 @@
-/*
- * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package java.util.stream;
-
-import java.util.Collections;
-import java.util.EnumSet;
-import java.util.PrimitiveIterator;
-import java.util.Set;
-import java.util.Spliterator;
-import java.util.function.Consumer;
-import java.util.function.Function;
-import java.util.function.IntConsumer;
-
-/**
- * Test scenarios for int streams.
- *
- * Each scenario is provided with a data source, a function that maps a fresh
- * stream (as provided by the data source) to a new stream, and a sink to
- * receive results.  Each scenario describes a different way of computing the
- * stream contents.  The test driver will ensure that all scenarios produce
- * the same output (modulo allowable differences in ordering).
- */
-@SuppressWarnings({"rawtypes", "unchecked"})
-public enum IntStreamTestScenario implements OpTestCase.BaseStreamTestScenario {
-
-    STREAM_FOR_EACH(false) {
-        <T, S_IN extends BaseStream<T, S_IN>>
-        void run(TestData<T, S_IN> data, S_IN source, IntConsumer b, Function<S_IN, IntStream> m) {
-            IntStream s = m.apply(source);
-            if (s.isParallel()) {
-                s = s.sequential();
-            }
-            s.forEach(b);
-        }
-    },
-
-    STREAM_TO_ARRAY(false) {
-        <T, S_IN extends BaseStream<T, S_IN>>
-        void run(TestData<T, S_IN> data, S_IN source, IntConsumer b, Function<S_IN, IntStream> m) {
-            for (int t : m.apply(source).toArray()) {
-                b.accept(t);
-            }
-        }
-    },
-
-    STREAM_ITERATOR(false) {
-        <T, S_IN extends BaseStream<T, S_IN>>
-        void run(TestData<T, S_IN> data, S_IN source, IntConsumer b, Function<S_IN, IntStream> m) {
-            for (PrimitiveIterator.OfInt seqIter = m.apply(source).iterator(); seqIter.hasNext(); )
-                b.accept(seqIter.nextInt());
-        }
-    },
-
-    // Wrap as stream, and spliterate then iterate in pull mode
-    STREAM_SPLITERATOR(false) {
-        <T, S_IN extends BaseStream<T, S_IN>>
-        void run(TestData<T, S_IN> data, S_IN source, IntConsumer b, Function<S_IN, IntStream> m) {
-            for (Spliterator.OfInt spl = m.apply(source).spliterator(); spl.tryAdvance(b); ) {
-            }
-        }
-    },
-
-    // Wrap as stream, spliterate, then split a few times mixing advances with forEach
-    STREAM_SPLITERATOR_WITH_MIXED_TRAVERSE_AND_SPLIT(false) {
-        <T, S_IN extends BaseStream<T, S_IN>>
-        void run(TestData<T, S_IN> data, S_IN source, IntConsumer b, Function<S_IN, IntStream> m) {
-            SpliteratorTestHelper.mixedTraverseAndSplit(b, m.apply(source).spliterator());
-        }
-    },
-
-    // Wrap as stream, and spliterate then iterate in pull mode
-    STREAM_SPLITERATOR_FOREACH(false) {
-        <T, S_IN extends BaseStream<T, S_IN>>
-        void run(TestData<T, S_IN> data, S_IN source, IntConsumer b, Function<S_IN, IntStream> m) {
-            m.apply(source).spliterator().forEachRemaining(b);
-        }
-    },
-
-    PAR_STREAM_SEQUENTIAL_FOR_EACH(true) {
-        <T, S_IN extends BaseStream<T, S_IN>>
-        void run(TestData<T, S_IN> data, S_IN source, IntConsumer b, Function<S_IN, IntStream> m) {
-            m.apply(source).sequential().forEach(b);
-        }
-    },
-
-    // Wrap as parallel stream + forEachOrdered
-    PAR_STREAM_FOR_EACH_ORDERED(true) {
-        <T, S_IN extends BaseStream<T, S_IN>>
-        void run(TestData<T, S_IN> data, S_IN source, IntConsumer b, Function<S_IN, IntStream> m) {
-            // @@@ Want to explicitly select ordered equalator
-            m.apply(source).forEachOrdered(b);
-        }
-    },
-
-    // Wrap as stream, and spliterate then iterate sequentially
-    PAR_STREAM_SPLITERATOR(true) {
-        <T, S_IN extends BaseStream<T, S_IN>>
-        void run(TestData<T, S_IN> data, S_IN source, IntConsumer b, Function<S_IN, IntStream> m) {
-            for (Spliterator.OfInt spl = m.apply(source).spliterator(); spl.tryAdvance(b); ) {
-            }
-        }
-    },
-
-    // Wrap as stream, and spliterate then iterate sequentially
-    PAR_STREAM_SPLITERATOR_FOREACH(true) {
-        <T, S_IN extends BaseStream<T, S_IN>>
-        void run(TestData<T, S_IN> data, S_IN source, IntConsumer b, Function<S_IN, IntStream> m) {
-            m.apply(source).spliterator().forEachRemaining(b);
-        }
-    },
-
-    PAR_STREAM_TO_ARRAY(true) {
-        <T, S_IN extends BaseStream<T, S_IN>>
-        void run(TestData<T, S_IN> data, S_IN source, IntConsumer b, Function<S_IN, IntStream> m) {
-            for (int t : m.apply(source).toArray())
-                b.accept(t);
-        }
-    },
-
-    // Wrap as parallel stream, get the spliterator, wrap as a stream + toArray
-    PAR_STREAM_SPLITERATOR_STREAM_TO_ARRAY(true) {
-        <T, S_IN extends BaseStream<T, S_IN>>
-        void run(TestData<T, S_IN> data, S_IN source, IntConsumer b, Function<S_IN, IntStream> m) {
-            IntStream s = m.apply(source);
-            Spliterator.OfInt sp = s.spliterator();
-            IntStream ss = StreamSupport.intStream(() -> sp,
-                                                   StreamOpFlag.toCharacteristics(OpTestCase.getStreamFlags(s))
-                                                   | (sp.getExactSizeIfKnown() < 0 ? 0 : Spliterator.SIZED),
-                                                   true);
-            for (int t : ss.toArray())
-                b.accept(t);
-        }
-    },
-
-    PAR_STREAM_TO_ARRAY_CLEAR_SIZED(true) {
-        <T, S_IN extends BaseStream<T, S_IN>>
-        void run(TestData<T, S_IN> data, S_IN source, IntConsumer b, Function<S_IN, IntStream> m) {
-            S_IN pipe1 = (S_IN) OpTestCase.chain(source,
-                                                 new FlagDeclaringOp(StreamOpFlag.NOT_SIZED, data.getShape()));
-            IntStream pipe2 = m.apply(pipe1);
-
-            for (int t : pipe2.toArray())
-                b.accept(t);
-        }
-    },
-
-    // Wrap as parallel stream + forEach synchronizing
-    PAR_STREAM_FOR_EACH(true, false) {
-        <T, S_IN extends BaseStream<T, S_IN>>
-        void run(TestData<T, S_IN> data, S_IN source, IntConsumer b, Function<S_IN, IntStream> m) {
-            m.apply(source).forEach(e -> {
-                synchronized (data) {
-                    b.accept(e);
-                }
-            });
-        }
-    },
-
-    // Wrap as parallel stream + forEach synchronizing and clear SIZED flag
-    PAR_STREAM_FOR_EACH_CLEAR_SIZED(true, false) {
-        <T, S_IN extends BaseStream<T, S_IN>>
-        void run(TestData<T, S_IN> data, S_IN source, IntConsumer b, Function<S_IN, IntStream> m) {
-            S_IN pipe1 = (S_IN) OpTestCase.chain(source,
-                                                 new FlagDeclaringOp(StreamOpFlag.NOT_SIZED, data.getShape()));
-            m.apply(pipe1).forEach(e -> {
-                synchronized (data) {
-                    b.accept(e);
-                }
-            });
-        }
-    },
-    ;
-
-    // The set of scenarios that clean the SIZED flag
-    public static final Set<IntStreamTestScenario> CLEAR_SIZED_SCENARIOS = Collections.unmodifiableSet(
-            EnumSet.of(PAR_STREAM_TO_ARRAY_CLEAR_SIZED, PAR_STREAM_FOR_EACH_CLEAR_SIZED));
-
-    private final boolean isParallel;
-
-    private final boolean isOrdered;
-
-    IntStreamTestScenario(boolean isParallel) {
-        this(isParallel, true);
-    }
-
-    IntStreamTestScenario(boolean isParallel, boolean isOrdered) {
-        this.isParallel = isParallel;
-        this.isOrdered = isOrdered;
-    }
-
-    public StreamShape getShape() {
-        return StreamShape.INT_VALUE;
-    }
-
-    public boolean isParallel() {
-        return isParallel;
-    }
-
-    public boolean isOrdered() {
-        return isOrdered;
-    }
-
-    public <T, U, S_IN extends BaseStream<T, S_IN>, S_OUT extends BaseStream<U, S_OUT>>
-    void run(TestData<T, S_IN> data, Consumer<U> b, Function<S_IN, S_OUT> m) {
-        try (S_IN source = getStream(data)) {
-            run(data, source, (IntConsumer) b, (Function<S_IN, IntStream>) m);
-        }
-    }
-
-    abstract <T, S_IN extends BaseStream<T, S_IN>>
-    void run(TestData<T, S_IN> data, S_IN source, IntConsumer b, Function<S_IN, IntStream> m);
-
-}
--- a/jdk/test/java/util/stream/bootlib/java/util/stream/IntermediateTestOp.java	Thu Nov 19 18:18:05 2015 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,41 +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 java.util.stream;
-
-/**
- * A base type for test operations
- */
-interface IntermediateTestOp<E_IN, E_OUT> {
-
-    @SuppressWarnings({"rawtypes", "unchecked"})
-    public static<T> AbstractPipeline chain(AbstractPipeline upstream,
-                                            IntermediateTestOp<?, T> op) {
-        if (op instanceof StatelessTestOp)
-            return StatelessTestOp.chain(upstream, (StatelessTestOp) op);
-
-        if (op instanceof StatefulTestOp)
-            return StatefulTestOp.chain(upstream, (StatefulTestOp) op);
-
-        throw new IllegalStateException("Unknown test op type: " + op.getClass().getName());
-    }
-}
--- a/jdk/test/java/util/stream/bootlib/java/util/stream/LambdaTestHelpers.java	Thu Nov 19 18:18:05 2015 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,462 +0,0 @@
-/*
- * Copyright (c) 1997, 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 java.util.stream;
-
-import java.util.*;
-import java.util.function.BiConsumer;
-import java.util.function.BiPredicate;
-import java.util.function.BinaryOperator;
-import java.util.function.Consumer;
-import java.util.function.DoubleBinaryOperator;
-import java.util.function.DoubleConsumer;
-import java.util.function.DoublePredicate;
-import java.util.function.Function;
-import java.util.function.IntBinaryOperator;
-import java.util.function.IntConsumer;
-import java.util.function.IntFunction;
-import java.util.function.IntPredicate;
-import java.util.function.IntUnaryOperator;
-import java.util.function.LongBinaryOperator;
-import java.util.function.LongConsumer;
-import java.util.function.LongPredicate;
-import java.util.function.Predicate;
-import java.util.function.Supplier;
-import java.util.function.ToDoubleFunction;
-import java.util.function.ToIntFunction;
-import java.util.function.ToLongFunction;
-
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertTrue;
-
-/**
- * LambdaTestHelpers -- assertion methods and useful objects for lambda test cases
- */
-public class LambdaTestHelpers {
-    public static final String LONG_STRING = "When in the Course of human events it becomes necessary for one people to dissolve the political bands which have connected them with another and to assume among the powers of the earth, the separate and equal station to which the Laws of Nature and of Nature's God entitle them, a decent respect to the opinions of mankind requires that they should declare the causes which impel them to the separation.";
-
-    @SuppressWarnings("rawtypes")
-    public static final Consumer bEmpty = x -> {  };
-    @SuppressWarnings("rawtypes")
-    public static final IntConsumer bIntEmpty = x -> {  };
-    @SuppressWarnings("rawtypes")
-    public static final BiConsumer bBiEmpty = (x,y) -> { };
-    @SuppressWarnings("rawtypes")
-    public static final Consumer bHashCode = x -> { Objects.hashCode(x); };
-    @SuppressWarnings("rawtypes")
-    public static final BiConsumer bBiHashCode = (x,y) -> { Objects.hash(x, y); };
-    public static final Function<Integer, Integer> mZero = x -> 0;
-    public static final Function<Integer, Integer> mId = x -> x;
-    public static final Function<Integer, Integer> mDoubler = x -> x * 2;
-    public static final Function<Integer, Stream<Integer>> mfId = e -> Collections.singletonList(e).stream();
-    public static final Function<Integer, Stream<Integer>> mfNull = e -> Collections.<Integer>emptyList().stream();
-    public static final Function<Integer, Stream<Integer>> mfLt = e -> {
-        List<Integer> l = new ArrayList<>();
-        for (int i=0; i<e; i++)
-            l.add(i);
-        return l.stream();
-    };
-    public static final ToIntFunction<Integer> imDoubler = x -> x * 2;
-    public static final ToLongFunction<Long> lmDoubler = x -> x * 2;
-    public static final ToDoubleFunction<Double> dmDoubler = x -> x * 2;
-    public static final Predicate<Integer> pFalse = x -> false;
-    public static final Predicate<Integer> pTrue = x -> true;
-    public static final Predicate<Integer> pEven = x -> 0 == x % 2;
-    public static final Predicate<Integer> pOdd = x -> 1 == x % 2;
-    public static final IntPredicate ipFalse = x -> false;
-    public static final IntPredicate ipTrue = x -> true;
-    public static final IntPredicate ipEven = x -> 0 == x % 2;
-    public static final IntPredicate ipOdd = x -> 1 == x % 2;
-    public static final LongPredicate lpFalse = x -> false;
-    public static final LongPredicate lpTrue = x -> true;
-    public static final LongPredicate lpEven = x -> 0 == x % 2;
-    public static final LongPredicate lpOdd = x -> 1 == x % 2;
-    public static final DoublePredicate dpFalse = x -> false;
-    public static final DoublePredicate dpTrue = x -> true;
-    public static final DoublePredicate dpEven = x -> 0 == ((long) x) % 2;
-    public static final DoublePredicate dpOdd = x -> 1 == ((long) x) % 2;
-    public static final BinaryOperator<Integer> rPlus = (x, y) -> x+y;
-    public static final BinaryOperator<Integer> rMax = (x, y) -> Math.max(x, y);
-    public static final BinaryOperator<Integer> rMin = (x, y) -> Math.min(x,y);
-    public static final IntBinaryOperator irPlus = (x, y) -> x+y;
-    public static final IntBinaryOperator irMax = (x, y) -> Math.max(x, y);
-    public static final IntBinaryOperator irMin = (x, y) -> Math.min(x,y);
-    public static final IntUnaryOperator irDoubler = x -> x * 2;
-    public static final LongBinaryOperator lrPlus = (x, y) -> x+y;
-    public static final DoubleBinaryOperator drPlus = (x, y) -> x+y;
-    public static final Comparator<Integer> cInteger = (a, b) -> Integer.compare(a, b);
-    public static final BiPredicate<?, ?> bipFalse = (x, y) -> false;
-    public static final BiPredicate<?, ?> bipTrue = (x, y) -> true;
-    public static final BiPredicate<Integer, Integer> bipBothEven = (x, y) -> 0 == (x % 2 + y % 2);
-    public static final BiPredicate<Integer, Integer> bipBothOdd = (x, y) -> 2 == (x % 2 + y % 2);
-    public static final BiPredicate<?, ?> bipSameString = (x, y) -> String.valueOf(x).equals(String.valueOf(y));
-
-    public static final IntFunction<Integer[]> integerArrayGenerator = s -> new Integer[s];
-
-    public static final IntFunction<Object[]> objectArrayGenerator = s -> new Object[s];
-
-    public static final Function<String, Stream<Character>> flattenChars = string -> {
-        List<Character> l = new ArrayList<>();
-        for (int i=0; i<string.length(); i++)
-            l.add(string.charAt(i));
-        return l.stream();
-    };
-
-    public static final Function<String, IntStream> flattenInt
-            = string -> IntStream.range(0, string.length()).map(string::charAt);
-
-    public static <T, R> Function<T, R> forPredicate(Predicate<? super T> predicate, R forTrue, R forFalse) {
-        Objects.requireNonNull(predicate);
-
-        return t -> predicate.test(t) ? forTrue : forFalse;
-    }
-
-    public static <T> Function<T, T> identity() {
-        return t -> t;
-    }
-
-    public static<V, T, R> Function<V, R> compose(Function<? super T, ? extends R> after, Function<? super V, ? extends T> before) {
-        Objects.requireNonNull(before);
-        return (V v) -> after.apply(before.apply(v));
-    }
-
-    public static List<Integer> empty() {
-        ArrayList<Integer> list = new ArrayList<>();
-        list.add(null);
-        return list;
-    }
-
-    public static List<Integer> countTo(int n) {
-        return range(1, n);
-    }
-
-    public static List<Integer> range(int l, int u) {
-        ArrayList<Integer> list = new ArrayList<>(u - l + 1);
-        for (int i=l; i<=u; i++) {
-            list.add(i);
-        }
-        return list;
-    }
-
-    public static List<Integer> repeat(int value, int n) {
-        ArrayList<Integer> list = new ArrayList<>(n);
-        for (int i=1; i<=n; i++) {
-            list.add(value);
-        }
-        return list;
-    }
-
-    public static List<Double> asDoubles(List<Integer> integers) {
-        ArrayList<Double> list = new ArrayList<>();
-        for (Integer i : integers) {
-            list.add((double) i);
-        }
-        return list;
-    }
-
-    public static List<Long> asLongs(List<Integer> integers) {
-        ArrayList<Long> list = new ArrayList<>();
-        for (Integer i : integers) {
-            list.add((long) i);
-        }
-        return list;
-    }
-
-    public static void assertCountSum(Stream<? super Integer> it, int count, int sum) {
-        assertCountSum(it.iterator(), count, sum);
-    }
-
-    public static void assertCountSum(Iterable<? super Integer> it, int count, int sum) {
-        assertCountSum(it.iterator(), count, sum);
-    }
-
-    public static void assertCountSum(Iterator<? super Integer> it, int count, int sum) {
-        int c = 0;
-        int s = 0;
-        while (it.hasNext()) {
-            int i = (Integer) it.next();
-            c++;
-            s += i;
-        }
-
-        assertEquals(c, count);
-        assertEquals(s, sum);
-    }
-
-    public static void assertConcat(Iterator<Character> it, String result) {
-        StringBuilder sb = new StringBuilder();
-        while (it.hasNext()) {
-            sb.append(it.next());
-        }
-
-        assertEquals(result, sb.toString());
-    }
-
-    public static<T extends Comparable<? super T>> void assertSorted(Iterator<T> i) {
-        i = toBoxedList(i).iterator();
-
-        if (!i.hasNext())
-            return;
-        T last = i.next();
-        while (i.hasNext()) {
-            T t = i.next();
-            assertTrue(last.compareTo(t) <= 0);
-            assertTrue(t.compareTo(last) >= 0);
-            last = t;
-        }
-    }
-
-    public static<T> void assertSorted(Iterator<T> i, Comparator<? super T> comp) {
-        if (i instanceof PrimitiveIterator.OfInt
-                || i instanceof PrimitiveIterator.OfDouble
-                || i instanceof PrimitiveIterator.OfLong) {
-            i = toBoxedList(i).iterator();
-        }
-
-        if (!i.hasNext())
-            return;
-        T last = i.next();
-        while (i.hasNext()) {
-            T t = i.next();
-            assertTrue(comp.compare(last, t) <= 0);
-            assertTrue(comp.compare(t, last) >= 0);
-            last = t;
-        }
-    }
-
-    public static<T extends Comparable<? super T>> void assertSorted(Iterable<T> iter) {
-        assertSorted(iter.iterator());
-    }
-
-    public static<T> void assertSorted(Iterable<T> iter, Comparator<? super T> comp) {
-        assertSorted(iter.iterator(), comp);
-    }
-
-    public static <T> void assertUnique(Iterable<T> iter) {
-        assertUnique(iter.iterator());
-    }
-
-    public static<T> void assertUnique(Iterator<T> iter) {
-        if (!iter.hasNext()) {
-            return;
-        }
-
-        if (iter instanceof PrimitiveIterator.OfInt
-            || iter instanceof PrimitiveIterator.OfDouble
-            || iter instanceof PrimitiveIterator.OfLong) {
-            iter = toBoxedList(iter).iterator();
-        }
-
-        Set<T> uniq = new HashSet<>();
-        while(iter.hasNext()) {
-            T each = iter.next();
-            assertTrue(!uniq.contains(each), "Not unique");
-            uniq.add(each);
-        }
-    }
-
-    public static<T> void assertContents(Iterable<T> actual, Iterable<T> expected) {
-        if (actual instanceof Collection && expected instanceof Collection) {
-            assertEquals(actual, expected);
-        } else {
-            assertContents(actual.iterator(), expected.iterator());
-        }
-    }
-
-    public static<T> void assertContents(Iterator<T> actual, Iterator<T> expected) {
-        assertEquals(toBoxedList(actual), toBoxedList(expected));
-    }
-
-    @SafeVarargs
-    @SuppressWarnings("varargs")
-    public static<T> void assertContents(Iterator<T> actual, T... expected) {
-        assertContents(actual, Arrays.asList(expected).iterator());
-    }
-
-    /**
-     * The all consuming consumer (rampant capitalist) that can accepting a reference or any primitive value.
-     */
-    private static interface OmnivorousConsumer<T>
-            extends Consumer<T>, IntConsumer, LongConsumer, DoubleConsumer { }
-
-    @SuppressWarnings({"rawtypes", "unchecked"})
-    public static<T> Consumer<T> toBoxingConsumer(Consumer<? super T> c) {
-        return (Consumer<T>) new OmnivorousConsumer() {
-            @Override
-            public void accept(Object t) {
-                c.accept((T) t);
-            }
-
-            @Override
-            public void accept(int t) {
-                accept((Object) t);
-            }
-
-            @Override
-            public void accept(long t) {
-                accept((Object) t);
-            }
-
-            @Override
-            public void accept(double t) {
-                accept((Object) t);
-            }
-        };
-    }
-
-    /**
-     * Convert an iterator to a list using forEach with an implementation of
-     * {@link java.util.stream.LambdaTestHelpers.OmnivorousConsumer}.
-     *
-     * This ensures equality comparisons for test results do not trip
-     * the boxing trip-wires.
-     */
-    private static<T> List<T> toBoxedList(Iterator<T> it) {
-        List<T> l = new ArrayList<>();
-        it.forEachRemaining(toBoxingConsumer(l::add));
-        return l;
-    }
-
-    /**
-     * Convert a spliterator to a list using forEach with an implementation of
-     * {@link java.util.stream.LambdaTestHelpers.OmnivorousConsumer}.
-     *
-     * This ensures equality comparisons for test results do not trip
-     * the boxing trip-wires.
-     */
-    public static<T> List<T> toBoxedList(Spliterator<T> sp) {
-        List<T> l = new ArrayList<>();
-        sp.forEachRemaining(toBoxingConsumer(l::add));
-        return l;
-    }
-
-    /**
-     * Convert an iterator to a multi-set, represented as a Map, using forEach with an implementation of
-     * {@link java.util.stream.LambdaTestHelpers.OmnivorousConsumer}.
-     *
-     * This ensures equality comparisons for test results do not trip
-     * the boxing trip-wires.
-     */
-    @SuppressWarnings("unchecked")
-    private static<T> Map<T, Integer> toBoxedMultiset(Iterator<T> it) {
-        Map<Object, Integer> result = new HashMap<>();
-
-        it.forEachRemaining(toBoxingConsumer(o -> {
-                if (result.containsKey(o))
-                    result.put(o, result.get(o) + 1);
-                else
-                    result.put(o, 1);
-            }));
-
-        return (Map<T, Integer>) result;
-    }
-
-    @SuppressWarnings("unchecked")
-    public static<T> Map<T, Integer> toBoxedMultiset(Spliterator<T> it) {
-        Map<Object, Integer> result = new HashMap<>();
-
-        it.forEachRemaining(toBoxingConsumer(o -> {
-                if (result.containsKey(o))
-                    result.put(o, result.get(o) + 1);
-                else
-                    result.put(o, 1);
-            }));
-
-        return (Map<T, Integer>) result;
-    }
-
-    @SuppressWarnings("unchecked")
-    public static void assertContentsEqual(Object a, Object b) {
-        if (a instanceof Iterable && b instanceof Iterable)
-            assertContents((Iterable) a, (Iterable) b);
-        else
-            assertEquals(a, b);
-    }
-
-    public static<T> void assertContentsUnordered(Iterable<T> actual, Iterable<T> expected) {
-        assertContentsUnordered(actual.iterator(), expected.iterator());
-    }
-
-    public static<T> void assertContentsUnordered(Iterator<T> actual, Iterator<T> expected) {
-        assertEquals(toBoxedMultiset(actual), toBoxedMultiset(expected));
-    }
-
-    public static void launderAssertion(Runnable r, Supplier<String> additionalInfo) {
-        try {
-            r.run();
-        }
-        catch (AssertionError ae) {
-            AssertionError cloned = new AssertionError(ae.getMessage() + String.format("%n%s", additionalInfo.get()));
-            cloned.setStackTrace(ae.getStackTrace());
-            if (ae.getCause() != null)
-                cloned.initCause(ae.getCause());
-            throw cloned;
-        }
-    }
-
-    public static <T, S extends BaseStream<T, S>>
-    List<Function<S, S>> permuteStreamFunctions(List<Function<S, S>> opFunctions) {
-        List<List<Function<S, S>>> opFunctionPermutations = perm(opFunctions);
-
-        List<Function<S, S>> appliedFunctions = new ArrayList<>();
-        for (List<Function<S, S>> fs : opFunctionPermutations) {
-            Function<S, S> applied = s -> {
-                for (Function<S, S> f : fs) {
-                    s = f.apply(s);
-                }
-                return s;
-            };
-            appliedFunctions.add(applied);
-        }
-
-        return appliedFunctions;
-    }
-
-    private static <T> List<T> sub(List<T> l, int index) {
-        List<T> subL = new ArrayList<>(l);
-        subL.remove(index);
-        return subL;
-    }
-
-    public static <T> List<List<T>> perm(List<T> l) {
-        List<List<T>> result = new ArrayList<>();
-        for (int i = 0; i < l.size(); i++) {
-            for (List<T> perm : perm(sub(l, i))) {
-                perm.add(0, l.get(i));
-                result.add(perm);
-            }
-        }
-        result.add(new ArrayList<T>());
-
-        return result;
-    }
-
-    public static String flagsToString(int flags) {
-        StringJoiner sj = new StringJoiner(", ", "StreamOpFlag[", "]");
-        if (StreamOpFlag.DISTINCT.isKnown(flags)) sj.add("IS_DISTINCT");
-        if (StreamOpFlag.ORDERED.isKnown(flags)) sj.add("IS_ORDERED");
-        if (StreamOpFlag.SIZED.isKnown(flags)) sj.add("IS_SIZED");
-        if (StreamOpFlag.SORTED.isKnown(flags)) sj.add("IS_SORTED");
-        if (StreamOpFlag.SHORT_CIRCUIT.isKnown(flags)) sj.add("IS_SHORT_CIRCUIT");
-        return sj.toString();
-    }
-}
--- a/jdk/test/java/util/stream/bootlib/java/util/stream/LambdaTestMode.java	Thu Nov 19 18:18:05 2015 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,71 +0,0 @@
-/*
- * Copyright (c) 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 java.util.stream;
-
-/**
- * Runtime modes of test execution.
- */
-public enum LambdaTestMode {
-    /**
-     * Execution mode with no particular runtime constraints.
-     */
-    NORMAL,
-
-    /**
-     * Execution mode where tests are executed for testing lambda serialization
-     * and deserialization.
-     *
-     * <p>This mode may be queried by tests or data supplied by data
-     * providers, which cannot otherwise be assigned to the test group
-     * <em>serialization-hostile</em>, to not execute or declare
-     * serialization-hostile code or data.
-     *
-     * <p>This mode is enabled if the boolean system property
-     * {@code org.openjdk.java.util.stream.sand.mode} is declared with a
-     * {@code true} value.
-     */
-    SERIALIZATION;
-
-    /**
-     * {@code true} if tests are executed in the mode for testing lambda
-     * Serialization ANd Deserialization (SAND).
-     */
-    private static final boolean IS_LAMBDA_SERIALIZATION_MODE =
-            Boolean.getBoolean("org.openjdk.java.util.stream.sand.mode");
-
-    /**
-     *
-     * @return the mode of test execution.
-     */
-    public static LambdaTestMode getMode() {
-        return IS_LAMBDA_SERIALIZATION_MODE ? SERIALIZATION : NORMAL;
-    }
-
-    /**
-     *
-     * @return {@code true} if normal test mode.
-     */
-    public static boolean isNormalMode() {
-        return getMode() == NORMAL;
-    }
-}
--- a/jdk/test/java/util/stream/bootlib/java/util/stream/LoggingTestCase.java	Thu Nov 19 18:18:05 2015 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,67 +0,0 @@
-/*
- * Copyright (c) 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 java.util.stream;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import org.testng.Assert;
-import org.testng.ITestResult;
-import org.testng.annotations.AfterMethod;
-import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.Test;
-
-/**
- * LoggingTestCase
- *
- */
-@Test
-public class LoggingTestCase extends Assert {
-    private Map<String, Object> context = new HashMap<>();
-
-    @BeforeMethod
-    public void before() {
-        context.clear();
-    }
-
-    @AfterMethod
-    public void after(ITestResult result) {
-        if (!result.isSuccess()) {
-            List<Object> list = new ArrayList<>();
-            Collections.addAll(list, result.getParameters());
-            list.add(context.toString());
-            result.setParameters(list.toArray(new Object[list.size()]));
-        }
-    }
-
-    protected void setContext(String key, Object value) {
-        context.put(key, value);
-    }
-
-    protected void clearContext(String key) {
-        context.remove(key);
-    }
-}
--- a/jdk/test/java/util/stream/bootlib/java/util/stream/LongStreamTestDataProvider.java	Thu Nov 19 18:18:05 2015 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,158 +0,0 @@
-/*
- * Copyright (c) 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 java.util.stream;
-
-import org.testng.annotations.DataProvider;
-
-import java.util.*;
-import java.util.Spliterators;
-import java.util.function.Supplier;
-
-/** TestNG DataProvider for long-valued streams */
-public class LongStreamTestDataProvider {
-    private static final long[] to0 = new long[0];
-    private static final long[] to1 = new long[1];
-    private static final long[] to10 = new long[10];
-    private static final long[] to100 = new long[100];
-    private static final long[] to1000 = new long[1000];
-    private static final long[] reversed = new long[100];
-    private static final long[] ones = new long[100];
-    private static final long[] twice = new long[200];
-    private static final long[] pseudoRandom;
-
-    private static final Object[][] testData;
-    private static final Object[][] spliteratorTestData;
-
-    static {
-        long[][] arrays = {to0, to1, to10, to100, to1000};
-        for (long[] arr : arrays) {
-            for (int i = 0; i < arr.length; i++) {
-                arr[i] = i;
-            }
-        }
-        for (int i = 0; i < reversed.length; i++) {
-            reversed[i] = reversed.length - i;
-        }
-        for (int i = 0; i < ones.length; i++) {
-            ones[i] = 1;
-        }
-        System.arraycopy(to100, 0, twice, 0, to100.length);
-        System.arraycopy(to100, 0, twice, to100.length, to100.length);
-        pseudoRandom = new long[LambdaTestHelpers.LONG_STRING.length()];
-        for (int i = 0; i < LambdaTestHelpers.LONG_STRING.length(); i++) {
-            pseudoRandom[i] = (long) LambdaTestHelpers.LONG_STRING.charAt(i);
-        }
-    }
-
-    static final Object[][] arrays = {
-            {"empty", to0},
-            {"0..1", to1},
-            {"0..10", to10},
-            {"0..100", to100},
-            {"0..1000", to1000},
-            {"100x[1]", ones},
-            {"2x[0..100]", twice},
-            {"reverse 0..100", reversed},
-            {"pseudorandom", pseudoRandom}
-    };
-
-    static {
-        {
-            List<Object[]> list = new ArrayList<>();
-            for (Object[] data : arrays) {
-                final Object name = data[0];
-                final long[] longs = (long[]) data[1];
-
-                list.add(new Object[]{"array:" + name,
-                        TestData.Factory.ofArray("array:" + name, longs)});
-
-                SpinedBuffer.OfLong isl = new SpinedBuffer.OfLong();
-                for (long i : longs) {
-                    isl.accept(i);
-                }
-                list.add(new Object[]{"SpinedList:" + name,
-                        TestData.Factory.ofSpinedBuffer("SpinedList:" + name, isl)});
-
-                list.add(streamDataDescr("LongStream.longRange(0,l): " + longs.length,
-                                         () -> LongStream.range(0, longs.length)));
-                list.add(streamDataDescr("LongStream.longRangeClosed(0,l): " + longs.length,
-                                         () -> LongStream.rangeClosed(0, longs.length)));
-            }
-            testData = list.toArray(new Object[0][]);
-        }
-
-        {
-            List<Object[]> spliterators = new ArrayList<>();
-            for (Object[] data : arrays) {
-                final Object name = data[0];
-                final long[] longs = (long[]) data[1];
-
-                SpinedBuffer.OfLong isl = new SpinedBuffer.OfLong();
-                for (long i : longs) {
-                    isl.accept(i);
-                }
-
-                spliterators.add(splitDescr("Arrays.s(array):" + name,
-                                            () -> Arrays.spliterator(longs)));
-                spliterators.add(splitDescr("Arrays.s(array,o,l):" + name,
-                                            () -> Arrays.spliterator(longs, 0, longs.length / 2)));
-
-                spliterators.add(splitDescr("SpinedBuffer.s():" + name,
-                                            () -> isl.spliterator()));
-
-                spliterators.add(splitDescr("Primitives.s(SpinedBuffer.iterator(), size):" + name,
-                                            () -> Spliterators.spliterator(isl.iterator(), longs.length, 0)));
-                spliterators.add(splitDescr("Primitives.s(SpinedBuffer.iterator()):" + name,
-                                            () -> Spliterators.spliteratorUnknownSize(isl.iterator(), 0)));
-
-                spliterators.add(splitDescr("LongStream.longRange(0,l):" + name,
-                                            () -> LongStream.range(0, longs.length).spliterator()));
-                spliterators.add(splitDescr("LongStream.longRangeClosed(0,l):" + name,
-                                            () -> LongStream.rangeClosed(0, longs.length).spliterator()));
-                // Need more!
-            }
-            spliteratorTestData = spliterators.toArray(new Object[0][]);
-        }
-
-    }
-
-    static <T> Object[] streamDataDescr(String description, Supplier<LongStream> s) {
-        return new Object[] { description, TestData.Factory.ofLongSupplier(description, s) };
-    }
-
-    static <T> Object[] splitDescr(String description, Supplier<Spliterator.OfLong> s) {
-        return new Object[] { description, s };
-    }
-
-    // Return an array of ( String name, LongStreamTestData )
-    @DataProvider(name = "LongStreamTestData")
-    public static Object[][] makeLongStreamTestData() {
-        return testData;
-    }
-
-    // returns an array of (String name, Supplier<PrimitiveSpliterator<Long>>)
-    @DataProvider(name = "LongSpliterator")
-    public static Object[][] spliteratorProvider() {
-        return spliteratorTestData;
-    }
-}
--- a/jdk/test/java/util/stream/bootlib/java/util/stream/LongStreamTestScenario.java	Thu Nov 19 18:18:05 2015 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,232 +0,0 @@
-/*
- * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package java.util.stream;
-
-import java.util.Collections;
-import java.util.EnumSet;
-import java.util.PrimitiveIterator;
-import java.util.Set;
-import java.util.Spliterator;
-import java.util.function.Consumer;
-import java.util.function.Function;
-import java.util.function.LongConsumer;
-
-/**
- * Test scenarios for long streams.
- *
- * Each scenario is provided with a data source, a function that maps a fresh
- * stream (as provided by the data source) to a new stream, and a sink to
- * receive results.  Each scenario describes a different way of computing the
- * stream contents.  The test driver will ensure that all scenarios produce
- * the same output (modulo allowable differences in ordering).
- */
-@SuppressWarnings({"rawtypes", "unchecked"})
-public enum LongStreamTestScenario implements OpTestCase.BaseStreamTestScenario {
-
-    STREAM_FOR_EACH(false) {
-        <T, S_IN extends BaseStream<T, S_IN>>
-        void run(TestData<T, S_IN> data, S_IN source, LongConsumer b, Function<S_IN, LongStream> m) {
-            LongStream s = m.apply(source);
-            if (s.isParallel()) {
-                s = s.sequential();
-            }
-            s.forEach(b);
-        }
-    },
-
-    STREAM_TO_ARRAY(false) {
-        <T, S_IN extends BaseStream<T, S_IN>>
-        void run(TestData<T, S_IN> data, S_IN source, LongConsumer b, Function<S_IN, LongStream> m) {
-            for (long t : m.apply(source).toArray()) {
-                b.accept(t);
-            }
-        }
-    },
-
-    STREAM_ITERATOR(false) {
-        <T, S_IN extends BaseStream<T, S_IN>>
-        void run(TestData<T, S_IN> data, S_IN source, LongConsumer b, Function<S_IN, LongStream> m) {
-            for (PrimitiveIterator.OfLong seqIter = m.apply(source).iterator(); seqIter.hasNext(); )
-                b.accept(seqIter.nextLong());
-        }
-    },
-
-    // Wrap as stream, and spliterate then iterate in pull mode
-    STREAM_SPLITERATOR(false) {
-        <T, S_IN extends BaseStream<T, S_IN>>
-        void run(TestData<T, S_IN> data, S_IN source, LongConsumer b, Function<S_IN, LongStream> m) {
-            for (Spliterator.OfLong spl = m.apply(source).spliterator(); spl.tryAdvance(b); ) {
-            }
-        }
-    },
-
-    // Wrap as stream, spliterate, then split a few times mixing advances with forEach
-    STREAM_SPLITERATOR_WITH_MIXED_TRAVERSE_AND_SPLIT(false) {
-        <T, S_IN extends BaseStream<T, S_IN>>
-        void run(TestData<T, S_IN> data, S_IN source, LongConsumer b, Function<S_IN, LongStream> m) {
-            SpliteratorTestHelper.mixedTraverseAndSplit(b, m.apply(source).spliterator());
-        }
-    },
-
-    // Wrap as stream, and spliterate then iterate in pull mode
-    STREAM_SPLITERATOR_FOREACH(false) {
-        <T, S_IN extends BaseStream<T, S_IN>>
-        void run(TestData<T, S_IN> data, S_IN source, LongConsumer b, Function<S_IN, LongStream> m) {
-            m.apply(source).spliterator().forEachRemaining(b);
-        }
-    },
-
-    PAR_STREAM_SEQUENTIAL_FOR_EACH(true) {
-        <T, S_IN extends BaseStream<T, S_IN>>
-        void run(TestData<T, S_IN> data, S_IN source, LongConsumer b, Function<S_IN, LongStream> m) {
-            m.apply(source).sequential().forEach(b);
-        }
-    },
-
-    // Wrap as parallel stream + forEachOrdered
-    PAR_STREAM_FOR_EACH_ORDERED(true) {
-        <T, S_IN extends BaseStream<T, S_IN>>
-        void run(TestData<T, S_IN> data, S_IN source, LongConsumer b, Function<S_IN, LongStream> m) {
-            // @@@ Want to explicitly select ordered equalator
-            m.apply(source).forEachOrdered(b);
-        }
-    },
-
-    // Wrap as stream, and spliterate then iterate sequentially
-    PAR_STREAM_SPLITERATOR(true) {
-        <T, S_IN extends BaseStream<T, S_IN>>
-        void run(TestData<T, S_IN> data, S_IN source, LongConsumer b, Function<S_IN, LongStream> m) {
-            for (Spliterator.OfLong spl = m.apply(source).spliterator(); spl.tryAdvance(b); ) {
-            }
-        }
-    },
-
-    // Wrap as stream, and spliterate then iterate sequentially
-    PAR_STREAM_SPLITERATOR_FOREACH(true) {
-        <T, S_IN extends BaseStream<T, S_IN>>
-        void run(TestData<T, S_IN> data, S_IN source, LongConsumer b, Function<S_IN, LongStream> m) {
-            m.apply(source).spliterator().forEachRemaining(b);
-        }
-    },
-
-    PAR_STREAM_TO_ARRAY(true) {
-        <T, S_IN extends BaseStream<T, S_IN>>
-        void run(TestData<T, S_IN> data, S_IN source, LongConsumer b, Function<S_IN, LongStream> m) {
-            for (long t : m.apply(source).toArray())
-                b.accept(t);
-        }
-    },
-
-    // Wrap as parallel stream, get the spliterator, wrap as a stream + toArray
-    PAR_STREAM_SPLITERATOR_STREAM_TO_ARRAY(true) {
-        <T, S_IN extends BaseStream<T, S_IN>>
-        void run(TestData<T, S_IN> data, S_IN source, LongConsumer b, Function<S_IN, LongStream> m) {
-            LongStream s = m.apply(source);
-            Spliterator.OfLong sp = s.spliterator();
-            LongStream ss = StreamSupport.longStream(() -> sp,
-                                                     StreamOpFlag.toCharacteristics(OpTestCase.getStreamFlags(s))
-                                                     | (sp.getExactSizeIfKnown() < 0 ? 0 : Spliterator.SIZED), true);
-            for (long t : ss.toArray())
-                b.accept(t);
-        }
-    },
-
-    PAR_STREAM_TO_ARRAY_CLEAR_SIZED(true) {
-        <T, S_IN extends BaseStream<T, S_IN>>
-        void run(TestData<T, S_IN> data, S_IN source, LongConsumer b, Function<S_IN, LongStream> m) {
-            S_IN pipe1 = (S_IN) OpTestCase.chain(source,
-                                                 new FlagDeclaringOp(StreamOpFlag.NOT_SIZED, data.getShape()));
-            LongStream pipe2 = m.apply(pipe1);
-
-            for (long t : pipe2.toArray())
-                b.accept(t);
-        }
-    },
-
-    // Wrap as parallel stream + forEach synchronizing
-    PAR_STREAM_FOR_EACH(true, false) {
-        <T, S_IN extends BaseStream<T, S_IN>>
-        void run(TestData<T, S_IN> data, S_IN source, LongConsumer b, Function<S_IN, LongStream> m) {
-            m.apply(source).forEach(e -> {
-                synchronized (data) {
-                    b.accept(e);
-                }
-            });
-        }
-    },
-
-    // Wrap as parallel stream + forEach synchronizing and clear SIZED flag
-    PAR_STREAM_FOR_EACH_CLEAR_SIZED(true, false) {
-        <T, S_IN extends BaseStream<T, S_IN>>
-        void run(TestData<T, S_IN> data, S_IN source, LongConsumer b, Function<S_IN, LongStream> m) {
-            S_IN pipe1 = (S_IN) OpTestCase.chain(source,
-                                                 new FlagDeclaringOp(StreamOpFlag.NOT_SIZED, data.getShape()));
-            m.apply(pipe1).forEach(e -> {
-                synchronized (data) {
-                    b.accept(e);
-                }
-            });
-        }
-    },
-    ;
-
-    // The set of scenarios that clean the SIZED flag
-    public static final Set<LongStreamTestScenario> CLEAR_SIZED_SCENARIOS = Collections.unmodifiableSet(
-            EnumSet.of(PAR_STREAM_TO_ARRAY_CLEAR_SIZED, PAR_STREAM_FOR_EACH_CLEAR_SIZED));
-
-    private boolean isParallel;
-
-    private final boolean isOrdered;
-
-    LongStreamTestScenario(boolean isParallel) {
-        this(isParallel, true);
-    }
-
-    LongStreamTestScenario(boolean isParallel, boolean isOrdered) {
-        this.isParallel = isParallel;
-        this.isOrdered = isOrdered;
-    }
-
-    public StreamShape getShape() {
-        return StreamShape.LONG_VALUE;
-    }
-
-    public boolean isParallel() {
-        return isParallel;
-    }
-
-    public boolean isOrdered() {
-        return isOrdered;
-    }
-
-    public <T, U, S_IN extends BaseStream<T, S_IN>, S_OUT extends BaseStream<U, S_OUT>>
-    void run(TestData<T, S_IN> data, Consumer<U> b, Function<S_IN, S_OUT> m) {
-        try (S_IN source = getStream(data)) {
-            run(data, source, (LongConsumer) b, (Function<S_IN, LongStream>) m);
-        }
-    }
-
-    abstract <T, S_IN extends BaseStream<T, S_IN>>
-    void run(TestData<T, S_IN> data, S_IN source, LongConsumer b, Function<S_IN, LongStream> m);
-
-}
--- a/jdk/test/java/util/stream/bootlib/java/util/stream/OpTestCase.java	Thu Nov 19 18:18:05 2015 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,682 +0,0 @@
-/*
- * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package java.util.stream;
-
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.EnumMap;
-import java.util.EnumSet;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-import java.util.Spliterator;
-import java.util.function.BiConsumer;
-import java.util.function.Consumer;
-import java.util.function.Function;
-
-import org.testng.annotations.Test;
-
-/**
- * Base class for streams test cases.  Provides 'exercise' methods for taking
- * lambdas that construct and modify streams, and evaluates them in different
- * ways and asserts that they produce equivalent results.
- */
-@Test
-public abstract class OpTestCase extends LoggingTestCase {
-
-    private final Map<StreamShape, Set<? extends BaseStreamTestScenario>> testScenarios;
-
-    protected OpTestCase() {
-        testScenarios = new EnumMap<>(StreamShape.class);
-        testScenarios.put(StreamShape.REFERENCE, Collections.unmodifiableSet(EnumSet.allOf(StreamTestScenario.class)));
-        testScenarios.put(StreamShape.INT_VALUE, Collections.unmodifiableSet(EnumSet.allOf(IntStreamTestScenario.class)));
-        testScenarios.put(StreamShape.LONG_VALUE, Collections.unmodifiableSet(EnumSet.allOf(LongStreamTestScenario.class)));
-        testScenarios.put(StreamShape.DOUBLE_VALUE, Collections.unmodifiableSet(EnumSet.allOf(DoubleStreamTestScenario.class)));
-    }
-
-    @SuppressWarnings("rawtypes")
-    public static int getStreamFlags(BaseStream s) {
-        return ((AbstractPipeline) s).getStreamFlags();
-    }
-
-    /**
-     * An asserter for results produced when exercising of stream or terminal
-     * tests.
-     *
-     * @param <R> the type of result to assert on
-     */
-    public interface ResultAsserter<R> {
-        /**
-         * Assert a result produced when exercising of stream or terminal
-         * test.
-         *
-         * @param actual the actual result
-         * @param expected the expected result
-         * @param isOrdered true if the pipeline is ordered
-         * @param isParallel true if the pipeline is parallel
-         */
-        void assertResult(R actual, R expected, boolean isOrdered, boolean isParallel);
-    }
-
-    // Exercise stream operations
-
-    public interface BaseStreamTestScenario {
-        StreamShape getShape();
-
-        boolean isParallel();
-
-        boolean isOrdered();
-
-        default <T, S_IN extends BaseStream<T, S_IN>>
-        S_IN getStream(TestData<T, S_IN> data) {
-            return isParallel()
-                   ? data.parallelStream()
-                   : data.stream();
-        }
-
-        <T, U, S_IN extends BaseStream<T, S_IN>, S_OUT extends BaseStream<U, S_OUT>>
-        void run(TestData<T, S_IN> data, Consumer<U> b, Function<S_IN, S_OUT> m);
-    }
-
-    protected <T, U, S_IN extends BaseStream<T, S_IN>, S_OUT extends BaseStream<U, S_OUT>>
-    Collection<U> exerciseOps(TestData<T, S_IN> data, Function<S_IN, S_OUT> m) {
-        return withData(data).stream(m).exercise();
-    }
-
-    // Run multiple versions of exercise(), returning the result of the first, and asserting that others return the same result
-    // If the first version is s -> s.foo(), can be used with s -> s.mapToInt(i -> i).foo().mapToObj(i -> i) to test all shape variants
-    @SafeVarargs
-    protected final<T, U, S_IN extends BaseStream<T, S_IN>, S_OUT extends BaseStream<U, S_OUT>>
-    Collection<U> exerciseOpsMulti(TestData<T, S_IN> data,
-                                   Function<S_IN, S_OUT>... ms) {
-        Collection<U> result = null;
-        for (Function<S_IN, S_OUT> m : ms) {
-            if (result == null)
-                result = withData(data).stream(m).exercise();
-            else {
-                Collection<U> r2 = withData(data).stream(m).exercise();
-                assertEquals(result, r2);
-            }
-        }
-        return result;
-    }
-
-    // Run multiple versions of exercise() for an Integer stream, returning the result of the first, and asserting that others return the same result
-    // Automates the conversion between Stream<Integer> and {Int,Long,Double}Stream and back, so client sites look like you are passing the same
-    // lambda four times, but in fact they are four different lambdas since they are transforming four different kinds of streams
-    protected final
-    Collection<Integer> exerciseOpsInt(TestData.OfRef<Integer> data,
-                                       Function<Stream<Integer>, Stream<Integer>> mRef,
-                                       Function<IntStream, IntStream> mInt,
-                                       Function<LongStream, LongStream> mLong,
-                                       Function<DoubleStream, DoubleStream> mDouble) {
-        @SuppressWarnings({ "rawtypes", "unchecked" })
-        Function<Stream<Integer>, Stream<Integer>>[] ms = new Function[4];
-        ms[0] = mRef;
-        ms[1] = s -> mInt.apply(s.mapToInt(e -> e)).mapToObj(e -> e);
-        ms[2] = s -> mLong.apply(s.mapToLong(e -> e)).mapToObj(e -> (int) e);
-        ms[3] = s -> mDouble.apply(s.mapToDouble(e -> e)).mapToObj(e -> (int) e);
-        return exerciseOpsMulti(data, ms);
-    }
-
-    // Run multiple versions of exercise() with multiple terminal operations for all kinds of stream, , and asserting against the expected result
-    // If the first version is s -> s.foo(), can be used with s -> s.mapToInt(i -> i).foo().mapToObj(i -> i) to test all shape variants
-    protected final<T, U, R, S_IN extends BaseStream<T, S_IN>, S_OUT extends BaseStream<U, S_OUT>>
-    void exerciseTerminalOpsMulti(TestData<T, S_IN> data,
-                                  R expected,
-                                  Map<String, Function<S_IN, S_OUT>> streams,
-                                  Map<String, Function<S_OUT, R>> terminals) {
-        for (Map.Entry<String, Function<S_IN, S_OUT>> se : streams.entrySet()) {
-            setContext("Intermediate stream", se.getKey());
-            for (Map.Entry<String, Function<S_OUT, R>> te : terminals.entrySet()) {
-                setContext("Terminal stream", te.getKey());
-                withData(data)
-                        .terminal(se.getValue(), te.getValue())
-                        .expectedResult(expected)
-                        .exercise();
-
-            }
-        }
-    }
-
-    // Run multiple versions of exercise() with multiple terminal operation for all kinds of stream, and asserting against the expected result
-    // Automates the conversion between Stream<Integer> and {Int,Long,Double}Stream and back, so client sites look like you are passing the same
-    // lambda four times, but in fact they are four different lambdas since they are transforming four different kinds of streams
-    protected final
-    void exerciseTerminalOpsInt(TestData<Integer, Stream<Integer>> data,
-                                Collection<Integer> expected,
-                                String desc,
-                                Function<Stream<Integer>, Stream<Integer>> mRef,
-                                Function<IntStream, IntStream> mInt,
-                                Function<LongStream, LongStream> mLong,
-                                Function<DoubleStream, DoubleStream> mDouble,
-                                Map<String, Function<Stream<Integer>, Collection<Integer>>> terminals) {
-
-        Map<String, Function<Stream<Integer>, Stream<Integer>>> m = new HashMap<>();
-        m.put("Ref " + desc, mRef);
-        m.put("Int " + desc, s -> mInt.apply(s.mapToInt(e -> e)).mapToObj(e -> e));
-        m.put("Long " + desc, s -> mLong.apply(s.mapToLong(e -> e)).mapToObj(e -> (int) e));
-        m.put("Double " + desc, s -> mDouble.apply(s.mapToDouble(e -> e)).mapToObj(e -> (int) e));
-
-        exerciseTerminalOpsMulti(data, expected, m, terminals);
-    }
-
-
-    protected <T, U, S_OUT extends BaseStream<U, S_OUT>>
-    Collection<U> exerciseOps(Collection<T> data, Function<Stream<T>, S_OUT> m) {
-        TestData.OfRef<T> data1 = TestData.Factory.ofCollection("Collection of type " + data.getClass().getName(), data);
-        return withData(data1).stream(m).exercise();
-    }
-
-    protected <T, U, S_OUT extends BaseStream<U, S_OUT>, I extends Iterable<U>>
-    Collection<U> exerciseOps(Collection<T> data, Function<Stream<T>, S_OUT> m, I expected) {
-        TestData.OfRef<T> data1 = TestData.Factory.ofCollection("Collection of type " + data.getClass().getName(), data);
-        return withData(data1).stream(m).expectedResult(expected).exercise();
-    }
-
-    @SuppressWarnings("unchecked")
-    protected <U, S_OUT extends BaseStream<U, S_OUT>>
-    Collection<U> exerciseOps(int[] data, Function<IntStream, S_OUT> m) {
-        return withData(TestData.Factory.ofArray("int array", data)).stream(m).exercise();
-    }
-
-    protected Collection<Integer> exerciseOps(int[] data, Function<IntStream, IntStream> m, int[] expected) {
-        TestData.OfInt data1 = TestData.Factory.ofArray("int array", data);
-        return withData(data1).stream(m).expectedResult(expected).exercise();
-    }
-
-    protected <T, S_IN extends BaseStream<T, S_IN>> DataStreamBuilder<T, S_IN> withData(TestData<T, S_IN> data) {
-        Objects.requireNonNull(data);
-        return new DataStreamBuilder<>(data);
-    }
-
-    @SuppressWarnings({"rawtypes", "unchecked"})
-    public class DataStreamBuilder<T, S_IN extends BaseStream<T, S_IN>> {
-        final TestData<T, S_IN> data;
-
-        private DataStreamBuilder(TestData<T, S_IN> data) {
-            this.data = Objects.requireNonNull(data);
-        }
-
-        public <U, S_OUT extends BaseStream<U, S_OUT>>
-        ExerciseDataStreamBuilder<T, U, S_IN, S_OUT> ops(IntermediateTestOp... ops) {
-            return new ExerciseDataStreamBuilder<>(data, (S_IN s) -> (S_OUT) chain(s, ops));
-        }
-
-        public <U, S_OUT extends BaseStream<U, S_OUT>> ExerciseDataStreamBuilder<T, U, S_IN, S_OUT>
-        stream(Function<S_IN, S_OUT> m) {
-            return new ExerciseDataStreamBuilder<>(data, m);
-        }
-
-        public <U, S_OUT extends BaseStream<U, S_OUT>> ExerciseDataStreamBuilder<T, U, S_IN, S_OUT>
-        stream(Function<S_IN, S_OUT> m, IntermediateTestOp<U, U> additionalOp) {
-            return new ExerciseDataStreamBuilder<>(data, s -> (S_OUT) chain(m.apply(s), additionalOp));
-        }
-
-        public <R> ExerciseDataTerminalBuilder<T, T, R, S_IN, S_IN>
-        terminal(Function<S_IN, R> terminalF) {
-            return new ExerciseDataTerminalBuilder<>(data, s -> s, terminalF);
-        }
-
-        public <U, R, S_OUT extends BaseStream<U, S_OUT>> ExerciseDataTerminalBuilder<T, U, R, S_IN, S_OUT>
-        terminal(Function<S_IN, S_OUT> streamF, Function<S_OUT, R> terminalF) {
-            return new ExerciseDataTerminalBuilder<>(data, streamF, terminalF);
-        }
-    }
-
-    @SuppressWarnings({"rawtypes", "unchecked"})
-    public class ExerciseDataStreamBuilder<T, U, S_IN extends BaseStream<T, S_IN>, S_OUT extends BaseStream<U, S_OUT>> {
-        final TestData<T, S_IN> data;
-        final Function<S_IN, S_OUT> m;
-        final StreamShape shape;
-
-        Set<BaseStreamTestScenario> testSet = new HashSet<>();
-
-        Collection<U> refResult;
-
-        Consumer<TestData<T, S_IN>> before = LambdaTestHelpers.bEmpty;
-
-        Consumer<TestData<T, S_IN>> after = LambdaTestHelpers.bEmpty;
-
-        ResultAsserter<Iterable<U>> resultAsserter = (act, exp, ord, par) -> {
-            if (par & !ord) {
-                LambdaTestHelpers.assertContentsUnordered(act, exp);
-            }
-            else {
-                LambdaTestHelpers.assertContentsEqual(act, exp);
-            }
-        };
-
-        private ExerciseDataStreamBuilder(TestData<T, S_IN> data, Function<S_IN, S_OUT> m) {
-            this.data = data;
-
-            this.m = Objects.requireNonNull(m);
-
-            this.shape = ((AbstractPipeline<?, U, ?>) m.apply(data.stream())).getOutputShape();
-
-            // Have to initiate from the output shape of the last stream
-            // This means the stream mapper is required first rather than last
-            testSet.addAll(testScenarios.get(shape));
-        }
-
-        //
-
-        public <I extends Iterable<U>> ExerciseDataStreamBuilder<T, U, S_IN, S_OUT> expectedResult(I expectedResult) {
-            List<U> l = new ArrayList<>();
-            expectedResult.forEach(l::add);
-            refResult = l;
-            return this;
-        }
-
-        public ExerciseDataStreamBuilder<T, U, S_IN, S_OUT> expectedResult(int[] expectedResult) {
-            List l = new ArrayList();
-            for (int anExpectedResult : expectedResult) {
-                l.add(anExpectedResult);
-            }
-            refResult = l;
-            return this;
-        }
-
-        public ExerciseDataStreamBuilder<T, U, S_IN, S_OUT> expectedResult(long[] expectedResult) {
-            List l = new ArrayList();
-            for (long anExpectedResult : expectedResult) {
-                l.add(anExpectedResult);
-            }
-            refResult = l;
-            return this;
-        }
-
-        public ExerciseDataStreamBuilder<T, U, S_IN, S_OUT> expectedResult(double[] expectedResult) {
-            List l = new ArrayList();
-            for (double anExpectedResult : expectedResult) {
-                l.add(anExpectedResult);
-            }
-            refResult = l;
-            return this;
-        }
-
-        public ExerciseDataStreamBuilder<T, U, S_IN, S_OUT> before(Consumer<TestData<T, S_IN>> before) {
-            this.before = Objects.requireNonNull(before);
-            return this;
-        }
-
-        public ExerciseDataStreamBuilder<T, U, S_IN, S_OUT> after(Consumer<TestData<T, S_IN>> after) {
-            this.after = Objects.requireNonNull(after);
-            return this;
-        }
-
-        public ExerciseDataStreamBuilder<T, U, S_IN, S_OUT> without(BaseStreamTestScenario... tests) {
-            return without(Arrays.asList(tests));
-        }
-
-        public ExerciseDataStreamBuilder<T, U, S_IN, S_OUT> without(Collection<? extends BaseStreamTestScenario> tests) {
-            for (BaseStreamTestScenario ts : tests) {
-                if (ts.getShape() == shape) {
-                    testSet.remove(ts);
-                }
-            }
-
-            if (testSet.isEmpty()) {
-                throw new IllegalStateException("Test scenario set is empty");
-            }
-
-            return this;
-        }
-
-        public ExerciseDataStreamBuilder<T, U, S_IN, S_OUT> with(BaseStreamTestScenario... tests) {
-            return with(Arrays.asList(tests));
-        }
-
-        public ExerciseDataStreamBuilder<T, U, S_IN, S_OUT> with(Collection<? extends BaseStreamTestScenario> tests) {
-            testSet = new HashSet<>();
-
-            for (BaseStreamTestScenario ts : tests) {
-                if (ts.getShape() == shape) {
-                    testSet.add(ts);
-                }
-            }
-
-            if (testSet.isEmpty()) {
-                throw new IllegalStateException("Test scenario set is empty");
-            }
-
-            return this;
-        }
-
-        public ExerciseDataStreamBuilder<T, U, S_IN, S_OUT> resultAsserter(ResultAsserter<Iterable<U>> resultAsserter) {
-            this.resultAsserter = resultAsserter;
-            return this;
-        }
-
-        // Build method
-
-        public Collection<U> exercise() {
-            final boolean isStreamOrdered;
-            if (refResult == null) {
-                // Induce the reference result
-                before.accept(data);
-                try (S_OUT sOut = m.apply(data.stream())) {
-                    isStreamOrdered = StreamOpFlag.ORDERED.isKnown(((AbstractPipeline) sOut).getStreamFlags());
-                    Node<U> refNodeResult = ((AbstractPipeline<?, U, ?>) sOut).evaluateToArrayNode(size -> (U[]) new Object[size]);
-                    refResult = LambdaTestHelpers.toBoxedList(refNodeResult.spliterator());
-                }
-                after.accept(data);
-            }
-            else {
-                try (S_OUT sOut = m.apply(data.stream())) {
-                    isStreamOrdered = StreamOpFlag.ORDERED.isKnown(((AbstractPipeline) sOut).getStreamFlags());
-                }
-            }
-
-            List<Error> errors = new ArrayList<>();
-            for (BaseStreamTestScenario test : testSet) {
-                try {
-                    before.accept(data);
-
-                    List<U> result = new ArrayList<>();
-                    test.run(data, LambdaTestHelpers.<U>toBoxingConsumer(result::add), m);
-
-                    Runnable asserter = () -> resultAsserter.assertResult(result, refResult, isStreamOrdered && test.isOrdered(), test.isParallel());
-
-                    if (refResult.size() > 1000) {
-                        LambdaTestHelpers.launderAssertion(
-                                asserter,
-                                () -> String.format("%n%s: [actual size=%d] != [expected size=%d]", test, result.size(), refResult.size()));
-                    }
-                    else {
-                        LambdaTestHelpers.launderAssertion(
-                                asserter,
-                                () -> String.format("%n%s: [actual] %s != [expected] %s", test, result, refResult));
-                    }
-
-                    after.accept(data);
-                } catch (Throwable t) {
-                    errors.add(new Error(String.format("%s: %s", test, t), t));
-                }
-            }
-
-            if (!errors.isEmpty()) {
-                StringBuilder sb = new StringBuilder();
-                int i = 1;
-                for (Error t : errors) {
-                    sb.append(i++).append(": ");
-                    if (t instanceof AssertionError) {
-                        sb.append(t).append("\n");
-                    }
-                    else {
-                        StringWriter sw = new StringWriter();
-                        PrintWriter pw = new PrintWriter(sw);
-
-                        t.getCause().printStackTrace(pw);
-                        pw.flush();
-                        sb.append(t).append("\n").append(sw);
-                    }
-                }
-                sb.append("--");
-
-                fail(String.format("%d failure(s) for test data: %s\n%s", i - 1, data.toString(), sb));
-            }
-
-            return refResult;
-        }
-    }
-
-    // Exercise terminal operations
-
-    interface BaseTerminalTestScenario<U, R, S_OUT extends BaseStream<U, S_OUT>> {
-        boolean requiresSingleStageSource();
-
-        boolean requiresParallelSource();
-
-        default R run(Function<S_OUT, R> terminalF, S_OUT source, StreamShape shape) {
-            return terminalF.apply(source);
-        }
-    }
-
-    @SuppressWarnings({"rawtypes", "unchecked"})
-    enum TerminalTestScenario implements BaseTerminalTestScenario {
-        SINGLE_SEQUENTIAL(true, false),
-
-        SINGLE_SEQUENTIAL_SHORT_CIRCUIT(true, false) {
-            @Override
-            public Object run(Function terminalF, BaseStream source, StreamShape shape) {
-                source = (BaseStream) chain(source, new ShortCircuitOp(shape));
-                return terminalF.apply(source);
-            }
-        },
-
-        SINGLE_PARALLEL(true, true),
-
-        ALL_SEQUENTIAL(false, false),
-
-        ALL_SEQUENTIAL_SHORT_CIRCUIT(false, false) {
-            @Override
-            public Object run(Function terminalF, BaseStream source, StreamShape shape) {
-                source = (BaseStream) chain(source, new ShortCircuitOp(shape));
-                return terminalF.apply(source);
-            }
-        },
-
-        ALL_PARALLEL(false, true),
-
-        ALL_PARALLEL_SEQUENTIAL(false, false) {
-            @Override
-            public Object run(Function terminalF, BaseStream source, StreamShape shape) {
-                return terminalF.apply(source.sequential());
-            }
-        },
-        ;
-
-        private final boolean requiresSingleStageSource;
-        private final boolean isParallel;
-
-        TerminalTestScenario(boolean requiresSingleStageSource, boolean isParallel) {
-            this.requiresSingleStageSource = requiresSingleStageSource;
-            this.isParallel = isParallel;
-        }
-
-        @Override
-        public boolean requiresSingleStageSource() {
-            return requiresSingleStageSource;
-        }
-
-        @Override
-        public boolean requiresParallelSource() {
-            return isParallel;
-        }
-
-    }
-
-    @SuppressWarnings({"rawtypes", "unchecked"})
-    public class ExerciseDataTerminalBuilder<T, U, R, S_IN extends BaseStream<T, S_IN>, S_OUT extends BaseStream<U, S_OUT>> {
-        final TestData<T, S_IN> data;
-        final Function<S_IN, S_OUT> streamF;
-        final Function<S_OUT, R> terminalF;
-
-        R refResult;
-
-        ResultAsserter<R> resultAsserter = (act, exp, ord, par) -> LambdaTestHelpers.assertContentsEqual(act, exp);
-
-        private ExerciseDataTerminalBuilder(TestData<T, S_IN> data, Function<S_IN, S_OUT> streamF, Function<S_OUT, R> terminalF) {
-            this.data = data;
-            this.streamF = Objects.requireNonNull(streamF);
-            this.terminalF = Objects.requireNonNull(terminalF);
-        }
-
-        //
-
-        public ExerciseDataTerminalBuilder<T, U, R, S_IN, S_OUT> expectedResult(R expectedResult) {
-            this.refResult = expectedResult;
-            return this;
-        }
-
-        public ExerciseDataTerminalBuilder<T, U, R, S_IN, S_OUT> equalator(BiConsumer<R, R> equalityAsserter) {
-            resultAsserter = (act, exp, ord, par) -> equalityAsserter.accept(act, exp);
-            return this;
-        }
-
-        public ExerciseDataTerminalBuilder<T, U, R, S_IN, S_OUT> resultAsserter(ResultAsserter<R> resultAsserter) {
-            this.resultAsserter = resultAsserter;
-            return this;
-        }
-
-        // Build method
-
-        public R exercise() {
-            boolean isOrdered;
-            StreamShape shape;
-            Node<U> node;
-            try (S_OUT out = streamF.apply(data.stream()).sequential()) {
-                AbstractPipeline ap = (AbstractPipeline) out;
-                isOrdered = StreamOpFlag.ORDERED.isKnown(ap.getStreamFlags());
-                shape = ap.getOutputShape();
-                // Sequentially collect the output that will be input to the terminal op
-                node = ap.evaluateToArrayNode(size -> (U[]) new Object[size]);
-            }
-
-            EnumSet<TerminalTestScenario> tests = EnumSet.allOf(TerminalTestScenario.class);
-            if (refResult == null) {
-                // Induce the reference result
-                S_OUT source = (S_OUT) createPipeline(shape, node.spliterator(),
-                                                      StreamOpFlag.IS_ORDERED | StreamOpFlag.IS_SIZED,
-                                                      false);
-
-                refResult = (R) TerminalTestScenario.SINGLE_SEQUENTIAL.run(terminalF, source, shape);
-                tests.remove(TerminalTestScenario.SINGLE_SEQUENTIAL);
-            }
-
-            for (BaseTerminalTestScenario test : tests) {
-                S_OUT source;
-                if (test.requiresSingleStageSource()) {
-                    source = (S_OUT) createPipeline(shape, node.spliterator(),
-                                                    StreamOpFlag.IS_ORDERED | StreamOpFlag.IS_SIZED,
-                                                    test.requiresParallelSource());
-                }
-                else {
-                    source = streamF.apply(test.requiresParallelSource()
-                                           ? data.parallelStream() : data.stream());
-                }
-
-                R result;
-                try (source) {
-                    result = (R) test.run(terminalF, source, shape);
-                }
-                LambdaTestHelpers.launderAssertion(
-                        () -> resultAsserter.assertResult(result, refResult, isOrdered, test.requiresParallelSource()),
-                        () -> String.format("%s: %s != %s", test, refResult, result));
-            }
-
-            return refResult;
-        }
-
-        AbstractPipeline createPipeline(StreamShape shape, Spliterator s, int flags, boolean parallel) {
-            switch (shape) {
-                case REFERENCE:    return new ReferencePipeline.Head<>(s, flags, parallel);
-                case INT_VALUE:    return new IntPipeline.Head(s, flags, parallel);
-                case LONG_VALUE:   return new LongPipeline.Head(s, flags, parallel);
-                case DOUBLE_VALUE: return new DoublePipeline.Head(s, flags, parallel);
-                default: throw new IllegalStateException("Unknown shape: " + shape);
-            }
-        }
-    }
-
-    protected <T, R> R exerciseTerminalOps(Collection<T> data, Function<Stream<T>, R> m, R expected) {
-        TestData.OfRef<T> data1
-                = TestData.Factory.ofCollection("Collection of type " + data.getClass().getName(), data);
-        return withData(data1).terminal(m).expectedResult(expected).exercise();
-    }
-
-    protected <T, R, S_IN extends BaseStream<T, S_IN>> R
-    exerciseTerminalOps(TestData<T, S_IN> data,
-                        Function<S_IN, R> terminalF) {
-        return withData(data).terminal(terminalF).exercise();
-    }
-
-    protected <T, U, R, S_IN extends BaseStream<T, S_IN>, S_OUT extends BaseStream<U, S_OUT>> R
-    exerciseTerminalOps(TestData<T, S_IN> data,
-                        Function<S_IN, S_OUT> streamF,
-                        Function<S_OUT, R> terminalF) {
-        return withData(data).terminal(streamF, terminalF).exercise();
-    }
-
-    //
-
-    @SuppressWarnings({"rawtypes", "unchecked"})
-    private static <T> AbstractPipeline<?, T, ?> chain(AbstractPipeline upstream, IntermediateTestOp<?, T> op) {
-        return (AbstractPipeline<?, T, ?>) IntermediateTestOp.chain(upstream, op);
-    }
-
-    @SuppressWarnings({"rawtypes", "unchecked"})
-    private static AbstractPipeline<?, ?, ?> chain(AbstractPipeline pipe, IntermediateTestOp... ops) {
-        for (IntermediateTestOp op : ops)
-            pipe = chain(pipe, op);
-        return pipe;
-    }
-
-    @SuppressWarnings("rawtypes")
-    private static <T> AbstractPipeline<?, T, ?> chain(BaseStream pipe, IntermediateTestOp<?, T> op) {
-        return chain((AbstractPipeline) pipe, op);
-    }
-
-    @SuppressWarnings("rawtypes")
-    public static AbstractPipeline<?, ?, ?> chain(BaseStream pipe, IntermediateTestOp... ops) {
-        return chain((AbstractPipeline) pipe, ops);
-    }
-
-    // Test data
-
-    static class ShortCircuitOp<T> implements StatelessTestOp<T,T> {
-        private final StreamShape shape;
-
-        ShortCircuitOp(StreamShape shape) {
-            this.shape = shape;
-        }
-
-        @Override
-        public Sink<T> opWrapSink(int flags, boolean parallel, Sink<T> sink) {
-            return sink;
-        }
-
-        @Override
-        public int opGetFlags() {
-            return StreamOpFlag.IS_SHORT_CIRCUIT;
-        }
-
-        @Override
-        public StreamShape outputShape() {
-            return shape;
-        }
-
-        @Override
-        public StreamShape inputShape() {
-            return shape;
-        }
-    }
-}
--- a/jdk/test/java/util/stream/bootlib/java/util/stream/SpliteratorTestHelper.java	Thu Nov 19 18:18:05 2015 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,715 +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 java.util.stream;
-
-import org.testng.annotations.Test;
-
-import java.util.ArrayDeque;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Deque;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Spliterator;
-import java.util.function.*;
-
-import static org.testng.Assert.*;
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.fail;
-
-/**
- * Assertion methods for spliterators, to be called from other tests
- */
-public class SpliteratorTestHelper {
-
-    public interface ContentAsserter<T> {
-        void assertContents(Collection<T> actual, Collection<T> expected, boolean isOrdered);
-    }
-
-    private static ContentAsserter<Object> DEFAULT_CONTENT_ASSERTER
-            = SpliteratorTestHelper::assertContents;
-
-    @SuppressWarnings("unchecked")
-    private static <T> ContentAsserter<T> defaultContentAsserter() {
-        return (ContentAsserter<T>) DEFAULT_CONTENT_ASSERTER;
-    }
-
-    public static void testSpliterator(Supplier<Spliterator<Integer>> supplier) {
-        testSpliterator(supplier, defaultContentAsserter());
-    }
-
-    public static void testSpliterator(Supplier<Spliterator<Integer>> supplier,
-                                       ContentAsserter<Integer> asserter) {
-        testSpliterator(supplier, (Consumer<Integer> b) -> b, asserter);
-    }
-
-    public static void testIntSpliterator(Supplier<Spliterator.OfInt> supplier) {
-        testIntSpliterator(supplier, defaultContentAsserter());
-    }
-
-    public static void testIntSpliterator(Supplier<Spliterator.OfInt> supplier,
-                                          ContentAsserter<Integer> asserter) {
-        class BoxingAdapter implements Consumer<Integer>, IntConsumer {
-            private final Consumer<Integer> b;
-
-            BoxingAdapter(Consumer<Integer> b) {
-                this.b = b;
-            }
-
-            @Override
-            public void accept(Integer value) {
-                throw new IllegalStateException();
-            }
-
-            @Override
-            public void accept(int value) {
-                b.accept(value);
-            }
-        }
-
-        testSpliterator(supplier, BoxingAdapter::new, asserter);
-    }
-
-    public static void testLongSpliterator(Supplier<Spliterator.OfLong> supplier) {
-        testLongSpliterator(supplier, defaultContentAsserter());
-    }
-
-    public static void testLongSpliterator(Supplier<Spliterator.OfLong> supplier,
-                                           ContentAsserter<Long> asserter) {
-        class BoxingAdapter implements Consumer<Long>, LongConsumer {
-            private final Consumer<Long> b;
-
-            BoxingAdapter(Consumer<Long> b) {
-                this.b = b;
-            }
-
-            @Override
-            public void accept(Long value) {
-                throw new IllegalStateException();
-            }
-
-            @Override
-            public void accept(long value) {
-                b.accept(value);
-            }
-        }
-
-        testSpliterator(supplier, BoxingAdapter::new, asserter);
-    }
-
-    public static void testDoubleSpliterator(Supplier<Spliterator.OfDouble> supplier) {
-        testDoubleSpliterator(supplier, defaultContentAsserter());
-    }
-
-    public static void testDoubleSpliterator(Supplier<Spliterator.OfDouble> supplier,
-                                             ContentAsserter<Double> asserter) {
-        class BoxingAdapter implements Consumer<Double>, DoubleConsumer {
-            private final Consumer<Double> b;
-
-            BoxingAdapter(Consumer<Double> b) {
-                this.b = b;
-            }
-
-            @Override
-            public void accept(Double value) {
-                throw new IllegalStateException();
-            }
-
-            @Override
-            public void accept(double value) {
-                b.accept(value);
-            }
-        }
-
-        testSpliterator(supplier, BoxingAdapter::new, asserter);
-    }
-
-    static <T, S extends Spliterator<T>> void testSpliterator(Supplier<S> supplier,
-                                                              UnaryOperator<Consumer<T>> boxingAdapter,
-                                                              ContentAsserter<T> asserter) {
-        ArrayList<T> fromForEach = new ArrayList<>();
-        Spliterator<T> spliterator = supplier.get();
-        Consumer<T> addToFromForEach = boxingAdapter.apply(fromForEach::add);
-        spliterator.forEachRemaining(addToFromForEach);
-
-        Collection<T> exp = Collections.unmodifiableList(fromForEach);
-
-        testNullPointerException(supplier);
-        testForEach(exp, supplier, boxingAdapter, asserter);
-        testTryAdvance(exp, supplier, boxingAdapter, asserter);
-        testMixedTryAdvanceForEach(exp, supplier, boxingAdapter, asserter);
-        testMixedTraverseAndSplit(exp, supplier, boxingAdapter, asserter);
-        testSplitAfterFullTraversal(supplier, boxingAdapter);
-        testSplitOnce(exp, supplier, boxingAdapter, asserter);
-        testSplitSixDeep(exp, supplier, boxingAdapter, asserter);
-        testSplitUntilNull(exp, supplier, boxingAdapter, asserter);
-    }
-
-    //
-
-    private static <T, S extends Spliterator<T>> void testNullPointerException(Supplier<S> s) {
-        S sp = s.get();
-        // Have to check instances and use casts to avoid tripwire messages and
-        // directly test the primitive methods
-        if (sp instanceof Spliterator.OfInt) {
-            Spliterator.OfInt psp = (Spliterator.OfInt) sp;
-            executeAndCatch(NullPointerException.class, () -> psp.forEachRemaining((IntConsumer) null));
-            executeAndCatch(NullPointerException.class, () -> psp.tryAdvance((IntConsumer) null));
-        }
-        else if (sp instanceof Spliterator.OfLong) {
-            Spliterator.OfLong psp = (Spliterator.OfLong) sp;
-            executeAndCatch(NullPointerException.class, () -> psp.forEachRemaining((LongConsumer) null));
-            executeAndCatch(NullPointerException.class, () -> psp.tryAdvance((LongConsumer) null));
-        }
-        else if (sp instanceof Spliterator.OfDouble) {
-            Spliterator.OfDouble psp = (Spliterator.OfDouble) sp;
-            executeAndCatch(NullPointerException.class, () -> psp.forEachRemaining((DoubleConsumer) null));
-            executeAndCatch(NullPointerException.class, () -> psp.tryAdvance((DoubleConsumer) null));
-        }
-        else {
-            executeAndCatch(NullPointerException.class, () -> sp.forEachRemaining(null));
-            executeAndCatch(NullPointerException.class, () -> sp.tryAdvance(null));
-        }
-    }
-
-    private static <T, S extends Spliterator<T>> void testForEach(
-            Collection<T> exp,
-            Supplier<S> supplier,
-            UnaryOperator<Consumer<T>> boxingAdapter,
-            ContentAsserter<T> asserter) {
-        S spliterator = supplier.get();
-        long sizeIfKnown = spliterator.getExactSizeIfKnown();
-        boolean isOrdered = spliterator.hasCharacteristics(Spliterator.ORDERED);
-
-        ArrayList<T> fromForEach = new ArrayList<>();
-        spliterator = supplier.get();
-        Consumer<T> addToFromForEach = boxingAdapter.apply(fromForEach::add);
-        spliterator.forEachRemaining(addToFromForEach);
-
-        // Assert that forEach now produces no elements
-        spliterator.forEachRemaining(boxingAdapter.apply(
-                e -> fail("Spliterator.forEach produced an element after spliterator exhausted: " + e)));
-        // Assert that tryAdvance now produce no elements
-        spliterator.tryAdvance(boxingAdapter.apply(
-                e -> fail("Spliterator.tryAdvance produced an element after spliterator exhausted: " + e)));
-
-        // assert that size, tryAdvance, and forEach are consistent
-        if (sizeIfKnown >= 0) {
-            assertEquals(sizeIfKnown, exp.size());
-        }
-        assertEquals(fromForEach.size(), exp.size());
-
-        asserter.assertContents(fromForEach, exp, isOrdered);
-    }
-
-    private static <T, S extends Spliterator<T>> void testTryAdvance(
-            Collection<T> exp,
-            Supplier<S> supplier,
-            UnaryOperator<Consumer<T>> boxingAdapter,
-            ContentAsserter<T> asserter) {
-        S spliterator = supplier.get();
-        long sizeIfKnown = spliterator.getExactSizeIfKnown();
-        boolean isOrdered = spliterator.hasCharacteristics(Spliterator.ORDERED);
-
-        spliterator = supplier.get();
-        ArrayList<T> fromTryAdvance = new ArrayList<>();
-        Consumer<T> addToFromTryAdvance = boxingAdapter.apply(fromTryAdvance::add);
-        while (spliterator.tryAdvance(addToFromTryAdvance)) { }
-
-        // Assert that forEach now produces no elements
-        spliterator.forEachRemaining(boxingAdapter.apply(
-                e -> fail("Spliterator.forEach produced an element after spliterator exhausted: " + e)));
-        // Assert that tryAdvance now produce no elements
-        spliterator.tryAdvance(boxingAdapter.apply(
-                e -> fail("Spliterator.tryAdvance produced an element after spliterator exhausted: " + e)));
-
-        // assert that size, tryAdvance, and forEach are consistent
-        if (sizeIfKnown >= 0) {
-            assertEquals(sizeIfKnown, exp.size());
-        }
-        assertEquals(fromTryAdvance.size(), exp.size());
-
-        asserter.assertContents(fromTryAdvance, exp, isOrdered);
-    }
-
-    private static <T, S extends Spliterator<T>> void testMixedTryAdvanceForEach(
-            Collection<T> exp,
-            Supplier<S> supplier,
-            UnaryOperator<Consumer<T>> boxingAdapter,
-            ContentAsserter<T> asserter) {
-        S spliterator = supplier.get();
-        long sizeIfKnown = spliterator.getExactSizeIfKnown();
-        boolean isOrdered = spliterator.hasCharacteristics(Spliterator.ORDERED);
-
-        // tryAdvance first few elements, then forEach rest
-        ArrayList<T> dest = new ArrayList<>();
-        spliterator = supplier.get();
-        Consumer<T> addToDest = boxingAdapter.apply(dest::add);
-        for (int i = 0; i < 10 && spliterator.tryAdvance(addToDest); i++) { }
-        spliterator.forEachRemaining(addToDest);
-
-        // Assert that forEach now produces no elements
-        spliterator.forEachRemaining(boxingAdapter.apply(
-                e -> fail("Spliterator.forEach produced an element after spliterator exhausted: " + e)));
-        // Assert that tryAdvance now produce no elements
-        spliterator.tryAdvance(boxingAdapter.apply(
-                e -> fail("Spliterator.tryAdvance produced an element after spliterator exhausted: " + e)));
-
-        if (sizeIfKnown >= 0) {
-            assertEquals(sizeIfKnown, dest.size());
-        }
-        assertEquals(dest.size(), exp.size());
-
-        asserter.assertContents(dest, exp, isOrdered);
-    }
-
-    private static <T, S extends Spliterator<T>> void testMixedTraverseAndSplit(
-            Collection<T> exp,
-            Supplier<S> supplier,
-            UnaryOperator<Consumer<T>> boxingAdapter,
-            ContentAsserter<T> asserter) {
-        S spliterator = supplier.get();
-        long sizeIfKnown = spliterator.getExactSizeIfKnown();
-        boolean isOrdered = spliterator.hasCharacteristics(Spliterator.ORDERED);
-
-        // tryAdvance first few elements, then forEach rest
-        ArrayList<T> dest = new ArrayList<>();
-        spliterator = supplier.get();
-        Consumer<T> b = boxingAdapter.apply(dest::add);
-
-        Spliterator<T> spl1, spl2, spl3;
-        spliterator.tryAdvance(b);
-        spl2 = spliterator.trySplit();
-        if (spl2 != null) {
-            spl2.tryAdvance(b);
-            spl1 = spl2.trySplit();
-            if (spl1 != null) {
-                spl1.tryAdvance(b);
-                spl1.forEachRemaining(b);
-            }
-            spl2.tryAdvance(b);
-            spl2.forEachRemaining(b);
-        }
-        spliterator.tryAdvance(b);
-        spl3 = spliterator.trySplit();
-        if (spl3 != null) {
-            spl3.tryAdvance(b);
-            spl3.forEachRemaining(b);
-        }
-        spliterator.tryAdvance(b);
-        spliterator.forEachRemaining(b);
-
-        if (sizeIfKnown >= 0) {
-            assertEquals(sizeIfKnown, dest.size());
-        }
-        assertEquals(dest.size(), exp.size());
-
-        asserter.assertContents(dest, exp, isOrdered);
-    }
-
-    private static <T, S extends Spliterator<T>> void testSplitAfterFullTraversal(
-            Supplier<S> supplier,
-            UnaryOperator<Consumer<T>> boxingAdapter) {
-        // Full traversal using tryAdvance
-        Spliterator<T> spliterator = supplier.get();
-        while (spliterator.tryAdvance(boxingAdapter.apply(e -> { }))) { }
-        Spliterator<T> split = spliterator.trySplit();
-        assertNull(split);
-
-        // Full traversal using forEach
-        spliterator = supplier.get();
-        spliterator.forEachRemaining(boxingAdapter.apply(e -> { }));
-        split = spliterator.trySplit();
-        assertNull(split);
-
-        // Full traversal using tryAdvance then forEach
-        spliterator = supplier.get();
-        spliterator.tryAdvance(boxingAdapter.apply(e -> { }));
-        spliterator.forEachRemaining(boxingAdapter.apply(e -> { }));
-        split = spliterator.trySplit();
-        assertNull(split);
-    }
-
-    private static <T, S extends Spliterator<T>> void testSplitOnce(
-            Collection<T> exp,
-            Supplier<S> supplier,
-            UnaryOperator<Consumer<T>> boxingAdapter,
-            ContentAsserter<T> asserter) {
-        S spliterator = supplier.get();
-        long sizeIfKnown = spliterator.getExactSizeIfKnown();
-        boolean isOrdered = spliterator.hasCharacteristics(Spliterator.ORDERED);
-
-        ArrayList<T> fromSplit = new ArrayList<>();
-        Spliterator<T> s1 = supplier.get();
-        Spliterator<T> s2 = s1.trySplit();
-        long s1Size = s1.getExactSizeIfKnown();
-        long s2Size = (s2 != null) ? s2.getExactSizeIfKnown() : 0;
-        Consumer<T> addToFromSplit = boxingAdapter.apply(fromSplit::add);
-        if (s2 != null)
-            s2.forEachRemaining(addToFromSplit);
-        s1.forEachRemaining(addToFromSplit);
-
-        if (sizeIfKnown >= 0) {
-            assertEquals(sizeIfKnown, fromSplit.size());
-            if (s1Size >= 0 && s2Size >= 0)
-                assertEquals(sizeIfKnown, s1Size + s2Size);
-        }
-
-        asserter.assertContents(fromSplit, exp, isOrdered);
-    }
-
-    private static <T, S extends Spliterator<T>> void testSplitSixDeep(
-            Collection<T> exp,
-            Supplier<S> supplier,
-            UnaryOperator<Consumer<T>> boxingAdapter,
-            ContentAsserter<T> asserter) {
-        S spliterator = supplier.get();
-        boolean isOrdered = spliterator.hasCharacteristics(Spliterator.ORDERED);
-
-        for (int depth=0; depth < 6; depth++) {
-            List<T> dest = new ArrayList<>();
-            spliterator = supplier.get();
-
-            assertSpliterator(spliterator);
-
-            // verify splitting with forEach
-            splitSixDeepVisitor(depth, 0, dest, spliterator, boxingAdapter, spliterator.characteristics(), false);
-            asserter.assertContents(dest, exp, isOrdered);
-
-            // verify splitting with tryAdvance
-            dest.clear();
-            spliterator = supplier.get();
-            splitSixDeepVisitor(depth, 0, dest, spliterator, boxingAdapter, spliterator.characteristics(), true);
-            asserter.assertContents(dest, exp, isOrdered);
-        }
-    }
-
-    private static <T, S extends Spliterator<T>>
-    void splitSixDeepVisitor(int depth, int curLevel,
-                             List<T> dest, S spliterator, UnaryOperator<Consumer<T>> boxingAdapter,
-                             int rootCharacteristics, boolean useTryAdvance) {
-        if (curLevel < depth) {
-            long beforeSize = spliterator.getExactSizeIfKnown();
-            Spliterator<T> split = spliterator.trySplit();
-            if (split != null) {
-                assertSpliterator(split, rootCharacteristics);
-                assertSpliterator(spliterator, rootCharacteristics);
-
-                if ((rootCharacteristics & Spliterator.SUBSIZED) != 0 &&
-                    (rootCharacteristics & Spliterator.SIZED) != 0) {
-                    assertEquals(beforeSize, split.estimateSize() + spliterator.estimateSize());
-                }
-                splitSixDeepVisitor(depth, curLevel + 1, dest, split, boxingAdapter, rootCharacteristics, useTryAdvance);
-            }
-            splitSixDeepVisitor(depth, curLevel + 1, dest, spliterator, boxingAdapter, rootCharacteristics, useTryAdvance);
-        }
-        else {
-            long sizeIfKnown = spliterator.getExactSizeIfKnown();
-            if (useTryAdvance) {
-                Consumer<T> addToDest = boxingAdapter.apply(dest::add);
-                int count = 0;
-                while (spliterator.tryAdvance(addToDest)) {
-                    ++count;
-                }
-
-                if (sizeIfKnown >= 0)
-                    assertEquals(sizeIfKnown, count);
-
-                // Assert that forEach now produces no elements
-                spliterator.forEachRemaining(boxingAdapter.apply(
-                        e -> fail("Spliterator.forEach produced an element after spliterator exhausted: " + e)));
-
-                Spliterator<T> split = spliterator.trySplit();
-                assertNull(split);
-            }
-            else {
-                List<T> leafDest = new ArrayList<>();
-                Consumer<T> addToLeafDest = boxingAdapter.apply(leafDest::add);
-                spliterator.forEachRemaining(addToLeafDest);
-
-                if (sizeIfKnown >= 0)
-                    assertEquals(sizeIfKnown, leafDest.size());
-
-                // Assert that forEach now produces no elements
-                spliterator.tryAdvance(boxingAdapter.apply(
-                        e -> fail("Spliterator.tryAdvance produced an element after spliterator exhausted: " + e)));
-
-                Spliterator<T> split = spliterator.trySplit();
-                assertNull(split);
-
-                dest.addAll(leafDest);
-            }
-        }
-    }
-
-    private static <T, S extends Spliterator<T>> void testSplitUntilNull(
-            Collection<T> exp,
-            Supplier<S> supplier,
-            UnaryOperator<Consumer<T>> boxingAdapter,
-            ContentAsserter<T> asserter) {
-        Spliterator<T> s = supplier.get();
-        boolean isOrdered = s.hasCharacteristics(Spliterator.ORDERED);
-        assertSpliterator(s);
-
-        List<T> splits = new ArrayList<>();
-        Consumer<T> c = boxingAdapter.apply(splits::add);
-
-        testSplitUntilNull(new SplitNode<T>(c, s));
-        asserter.assertContents(splits, exp, isOrdered);
-    }
-
-    private static class SplitNode<T> {
-        // Constant for every node
-        final Consumer<T> c;
-        final int rootCharacteristics;
-
-        final Spliterator<T> s;
-
-        SplitNode(Consumer<T> c, Spliterator<T> s) {
-            this(c, s.characteristics(), s);
-        }
-
-        private SplitNode(Consumer<T> c, int rootCharacteristics, Spliterator<T> s) {
-            this.c = c;
-            this.rootCharacteristics = rootCharacteristics;
-            this.s = s;
-        }
-
-        SplitNode<T> fromSplit(Spliterator<T> split) {
-            return new SplitNode<>(c, rootCharacteristics, split);
-        }
-    }
-
-    /**
-     * Set the maximum stack capacity to 0.25MB. This should be more than enough to detect a bad spliterator
-     * while not unduly disrupting test infrastructure given the test data sizes that are used are small.
-     * Note that j.u.c.ForkJoinPool sets the max queue size to 64M (1 << 26).
-     */
-    private static final int MAXIMUM_STACK_CAPACITY = 1 << 18; // 0.25MB
-
-    private static <T> void testSplitUntilNull(SplitNode<T> e) {
-        // Use an explicit stack to avoid a StackOverflowException when testing a Spliterator
-        // that when repeatedly split produces a right-balanced (and maybe degenerate) tree, or
-        // for a spliterator that is badly behaved.
-        Deque<SplitNode<T>> stack = new ArrayDeque<>();
-        stack.push(e);
-
-        int iteration = 0;
-        while (!stack.isEmpty()) {
-            assertTrue(iteration++ < MAXIMUM_STACK_CAPACITY, "Exceeded maximum stack modification count of 1 << 18");
-
-            e = stack.pop();
-            Spliterator<T> parentAndRightSplit = e.s;
-
-            long parentEstimateSize = parentAndRightSplit.estimateSize();
-            assertTrue(parentEstimateSize >= 0,
-                       String.format("Split size estimate %d < 0", parentEstimateSize));
-
-            long parentSize = parentAndRightSplit.getExactSizeIfKnown();
-            Spliterator<T> leftSplit = parentAndRightSplit.trySplit();
-            if (leftSplit == null) {
-                parentAndRightSplit.forEachRemaining(e.c);
-                continue;
-            }
-
-            assertSpliterator(leftSplit, e.rootCharacteristics);
-            assertSpliterator(parentAndRightSplit, e.rootCharacteristics);
-
-            if (parentEstimateSize != Long.MAX_VALUE && leftSplit.estimateSize() > 0
-                && parentAndRightSplit.estimateSize() > 0) {
-                assertTrue(leftSplit.estimateSize() < parentEstimateSize,
-                           String.format("Left split size estimate %d >= parent split size estimate %d",
-                                         leftSplit.estimateSize(), parentEstimateSize));
-                assertTrue(parentAndRightSplit.estimateSize() < parentEstimateSize,
-                           String.format("Right split size estimate %d >= parent split size estimate %d",
-                                         leftSplit.estimateSize(), parentEstimateSize));
-            }
-            else {
-                assertTrue(leftSplit.estimateSize() <= parentEstimateSize,
-                           String.format("Left split size estimate %d > parent split size estimate %d",
-                                         leftSplit.estimateSize(), parentEstimateSize));
-                assertTrue(parentAndRightSplit.estimateSize() <= parentEstimateSize,
-                           String.format("Right split size estimate %d > parent split size estimate %d",
-                                         leftSplit.estimateSize(), parentEstimateSize));
-            }
-
-            long leftSize = leftSplit.getExactSizeIfKnown();
-            long rightSize = parentAndRightSplit.getExactSizeIfKnown();
-            if (parentSize >= 0 && leftSize >= 0 && rightSize >= 0)
-                assertEquals(parentSize, leftSize + rightSize,
-                             String.format("exact left split size %d + exact right split size %d != parent exact split size %d",
-                                           leftSize, rightSize, parentSize));
-
-            // Add right side to stack first so left side is popped off first
-            stack.push(e.fromSplit(parentAndRightSplit));
-            stack.push(e.fromSplit(leftSplit));
-        }
-    }
-
-    private static void assertSpliterator(Spliterator<?> s, int rootCharacteristics) {
-        if ((rootCharacteristics & Spliterator.SUBSIZED) != 0) {
-            assertTrue(s.hasCharacteristics(Spliterator.SUBSIZED),
-                       "Child split is not SUBSIZED when root split is SUBSIZED");
-        }
-        assertSpliterator(s);
-    }
-
-    private static void assertSpliterator(Spliterator<?> s) {
-        if (s.hasCharacteristics(Spliterator.SUBSIZED)) {
-            assertTrue(s.hasCharacteristics(Spliterator.SIZED));
-        }
-        if (s.hasCharacteristics(Spliterator.SIZED)) {
-            assertTrue(s.estimateSize() != Long.MAX_VALUE);
-            assertTrue(s.getExactSizeIfKnown() >= 0);
-        }
-        try {
-            s.getComparator();
-            assertTrue(s.hasCharacteristics(Spliterator.SORTED));
-        } catch (IllegalStateException e) {
-            assertFalse(s.hasCharacteristics(Spliterator.SORTED));
-        }
-    }
-
-    private static<T> void assertContents(Collection<T> actual, Collection<T> expected, boolean isOrdered) {
-        if (isOrdered) {
-            assertEquals(actual, expected);
-        }
-        else {
-            LambdaTestHelpers.assertContentsUnordered(actual, expected);
-        }
-    }
-
-    private static void executeAndCatch(Class<? extends Exception> expected, Runnable r) {
-        Exception caught = null;
-        try {
-            r.run();
-        }
-        catch (Exception e) {
-            caught = e;
-        }
-
-        assertNotNull(caught,
-                      String.format("No Exception was thrown, expected an Exception of %s to be thrown",
-                                    expected.getName()));
-        assertTrue(expected.isInstance(caught),
-                   String.format("Exception thrown %s not an instance of %s",
-                                 caught.getClass().getName(), expected.getName()));
-    }
-
-    static<U> void mixedTraverseAndSplit(Consumer<U> b, Spliterator<U> splTop) {
-        Spliterator<U> spl1, spl2, spl3;
-        splTop.tryAdvance(b);
-        spl2 = splTop.trySplit();
-        if (spl2 != null) {
-            spl2.tryAdvance(b);
-            spl1 = spl2.trySplit();
-            if (spl1 != null) {
-                spl1.tryAdvance(b);
-                spl1.forEachRemaining(b);
-            }
-            spl2.tryAdvance(b);
-            spl2.forEachRemaining(b);
-        }
-        splTop.tryAdvance(b);
-        spl3 = splTop.trySplit();
-        if (spl3 != null) {
-            spl3.tryAdvance(b);
-            spl3.forEachRemaining(b);
-        }
-        splTop.tryAdvance(b);
-        splTop.forEachRemaining(b);
-    }
-
-    static void mixedTraverseAndSplit(IntConsumer b, Spliterator.OfInt splTop) {
-        Spliterator.OfInt spl1, spl2, spl3;
-        splTop.tryAdvance(b);
-        spl2 = splTop.trySplit();
-        if (spl2 != null) {
-            spl2.tryAdvance(b);
-            spl1 = spl2.trySplit();
-            if (spl1 != null) {
-                spl1.tryAdvance(b);
-                spl1.forEachRemaining(b);
-            }
-            spl2.tryAdvance(b);
-            spl2.forEachRemaining(b);
-        }
-        splTop.tryAdvance(b);
-        spl3 = splTop.trySplit();
-        if (spl3 != null) {
-            spl3.tryAdvance(b);
-            spl3.forEachRemaining(b);
-        }
-        splTop.tryAdvance(b);
-        splTop.forEachRemaining(b);
-    }
-    static void mixedTraverseAndSplit(LongConsumer b, Spliterator.OfLong splTop) {
-        Spliterator.OfLong spl1, spl2, spl3;
-        splTop.tryAdvance(b);
-        spl2 = splTop.trySplit();
-        if (spl2 != null) {
-            spl2.tryAdvance(b);
-            spl1 = spl2.trySplit();
-            if (spl1 != null) {
-                spl1.tryAdvance(b);
-                spl1.forEachRemaining(b);
-            }
-            spl2.tryAdvance(b);
-            spl2.forEachRemaining(b);
-        }
-        splTop.tryAdvance(b);
-        spl3 = splTop.trySplit();
-        if (spl3 != null) {
-            spl3.tryAdvance(b);
-            spl3.forEachRemaining(b);
-        }
-        splTop.tryAdvance(b);
-        splTop.forEachRemaining(b);
-    }
-
-    static void mixedTraverseAndSplit(DoubleConsumer b, Spliterator.OfDouble splTop) {
-        Spliterator.OfDouble spl1, spl2, spl3;
-        splTop.tryAdvance(b);
-        spl2 = splTop.trySplit();
-        if (spl2 != null) {
-            spl2.tryAdvance(b);
-            spl1 = spl2.trySplit();
-            if (spl1 != null) {
-                spl1.tryAdvance(b);
-                spl1.forEachRemaining(b);
-            }
-            spl2.tryAdvance(b);
-            spl2.forEachRemaining(b);
-        }
-        splTop.tryAdvance(b);
-        spl3 = splTop.trySplit();
-        if (spl3 != null) {
-            spl3.tryAdvance(b);
-            spl3.forEachRemaining(b);
-        }
-        splTop.tryAdvance(b);
-        splTop.forEachRemaining(b);
-    }
-}
--- a/jdk/test/java/util/stream/bootlib/java/util/stream/StatefulTestOp.java	Thu Nov 19 18:18:05 2015 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,138 +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 java.util.stream;
-
-import java.util.Spliterator;
-import java.util.function.IntFunction;
-
-/**
- * The base type for a stateful test operation.
- */
-interface StatefulTestOp<E> extends IntermediateTestOp<E, E> {
-
-    @SuppressWarnings({"rawtypes", "unchecked"})
-    public static<T> AbstractPipeline chain(AbstractPipeline upstream,
-                                            StatefulTestOp op) {
-        switch (op.outputShape()) {
-            case REFERENCE:
-                return new ReferencePipeline.StatefulOp<Object, T>(upstream, op.inputShape(), op.opGetFlags()) {
-                    @Override
-                    Sink opWrapSink(int flags, Sink sink) {
-                        return op.opWrapSink(flags, isParallel(), sink);
-                    }
-
-                    @Override
-                    <P_IN> Spliterator<T> opEvaluateParallelLazy(PipelineHelper<T> helper,
-                                                                 Spliterator<P_IN> spliterator) {
-                        return op.opEvaluateParallelLazy(helper, spliterator);
-                    }
-
-                    @Override
-                    <P_IN> Node<T> opEvaluateParallel(PipelineHelper<T> helper,
-                                                      Spliterator<P_IN> spliterator,
-                                                      IntFunction<T[]> generator) {
-                        return op.opEvaluateParallel(helper, spliterator, generator);
-                    }
-                };
-            case INT_VALUE:
-                return new IntPipeline.StatefulOp<Object>(upstream, op.inputShape(), op.opGetFlags()) {
-                    @Override
-                    Sink opWrapSink(int flags, Sink sink) {
-                        return op.opWrapSink(flags, isParallel(), sink);
-                    }
-
-                    @Override
-                    <P_IN> Spliterator<Integer> opEvaluateParallelLazy(PipelineHelper<Integer> helper,
-                                                                 Spliterator<P_IN> spliterator) {
-                        return op.opEvaluateParallelLazy(helper, spliterator);
-                    }
-
-                    @Override
-                    <P_IN> Node<Integer> opEvaluateParallel(PipelineHelper<Integer> helper,
-                                                            Spliterator<P_IN> spliterator,
-                                                            IntFunction<Integer[]> generator) {
-                        return (Node<Integer>) op.opEvaluateParallel(helper, spliterator, generator);
-                    }
-                };
-            case LONG_VALUE:
-                return new LongPipeline.StatefulOp<Object>(upstream, op.inputShape(), op.opGetFlags()) {
-                    @Override
-                    Sink opWrapSink(int flags, Sink sink) {
-                        return op.opWrapSink(flags, isParallel(), sink);
-                    }
-
-                    @Override
-                    <P_IN> Spliterator<Long> opEvaluateParallelLazy(PipelineHelper<Long> helper,
-                                                                 Spliterator<P_IN> spliterator) {
-                        return op.opEvaluateParallelLazy(helper, spliterator);
-                    }
-
-                    @Override
-                    <P_IN> Node<Long> opEvaluateParallel(PipelineHelper<Long> helper,
-                                                         Spliterator<P_IN> spliterator,
-                                                         IntFunction<Long[]> generator) {
-                        return (Node<Long>) op.opEvaluateParallel(helper, spliterator, generator);
-                    }
-                };
-            case DOUBLE_VALUE:
-                return new DoublePipeline.StatefulOp<Object>(upstream, op.inputShape(), op.opGetFlags()) {
-                    @Override
-                    Sink opWrapSink(int flags, Sink sink) {
-                        return op.opWrapSink(flags, isParallel(), sink);
-                    }
-
-                    @Override
-                    <P_IN> Spliterator<Double> opEvaluateParallelLazy(PipelineHelper<Double> helper,
-                                                                    Spliterator<P_IN> spliterator) {
-                        return op.opEvaluateParallelLazy(helper, spliterator);
-                    }
-
-                    @Override
-                    <P_IN> Node<Double> opEvaluateParallel(PipelineHelper<Double> helper,
-                                                           Spliterator<P_IN> spliterator,
-                                                           IntFunction<Double[]> generator) {
-                        return (Node<Double>) op.opEvaluateParallel(helper, spliterator, generator);
-                    }
-                };
-            default: throw new IllegalStateException(op.outputShape().toString());
-        }
-    }
-
-    default StreamShape inputShape() { return StreamShape.REFERENCE; }
-
-    default StreamShape outputShape() { return StreamShape.REFERENCE; }
-
-    default int opGetFlags() { return 0; }
-
-    Sink<E> opWrapSink(int flags, boolean parallel, Sink<E> sink);
-
-    @SuppressWarnings("unchecked")
-    default <P_IN> Spliterator<E> opEvaluateParallelLazy(PipelineHelper<E> helper,
-                                                         Spliterator<P_IN> spliterator) {
-        return opEvaluateParallel(helper, spliterator, i -> (E[]) new Object[i]).spliterator();
-    }
-
-    <P_IN> Node<E> opEvaluateParallel(PipelineHelper<E> helper,
-                                      Spliterator<P_IN> spliterator,
-                                      IntFunction<E[]> generator);
-}
--- a/jdk/test/java/util/stream/bootlib/java/util/stream/StatelessTestOp.java	Thu Nov 19 18:18:05 2015 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,73 +0,0 @@
-/*
- * Copyright (c) 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 java.util.stream;
-
-/**
- * The base type of a stateless test operation
- */
-interface StatelessTestOp<E_IN, E_OUT> extends IntermediateTestOp<E_IN, E_OUT> {
-
-    @SuppressWarnings({"rawtypes", "unchecked"})
-    public static<T> AbstractPipeline chain(AbstractPipeline upstream,
-                                            StatelessTestOp<?, T> op) {
-        int flags = op.opGetFlags();
-        switch (op.outputShape()) {
-            case REFERENCE:
-                return new ReferencePipeline.StatelessOp<Object, T>(upstream, op.inputShape(), flags) {
-                    public Sink opWrapSink(int flags, Sink<T> sink) {
-                        return op.opWrapSink(flags, isParallel(), sink);
-                    }
-                };
-            case INT_VALUE:
-                return new IntPipeline.StatelessOp<Object>(upstream, op.inputShape(), flags) {
-                    public Sink opWrapSink(int flags, Sink sink) {
-                        return op.opWrapSink(flags, isParallel(), sink);
-                    }
-                };
-            case LONG_VALUE:
-                return new LongPipeline.StatelessOp<Object>(upstream, op.inputShape(), flags) {
-                    @Override
-                    Sink opWrapSink(int flags, Sink sink) {
-                        return op.opWrapSink(flags, isParallel(), sink);
-                    }
-                };
-            case DOUBLE_VALUE:
-                return new DoublePipeline.StatelessOp<Object>(upstream, op.inputShape(), flags) {
-                    @Override
-                    Sink opWrapSink(int flags, Sink sink) {
-                        return op.opWrapSink(flags, isParallel(), sink);
-                    }
-                };
-            default: throw new IllegalStateException(op.outputShape().toString());
-        }
-    }
-
-    default StreamShape inputShape() { return StreamShape.REFERENCE; }
-
-    default StreamShape outputShape() { return StreamShape.REFERENCE; }
-
-    default int opGetFlags() { return 0; }
-
-    Sink<E_IN> opWrapSink(int flags, boolean parallel, Sink<E_OUT> sink);
-}
-
--- a/jdk/test/java/util/stream/bootlib/java/util/stream/StreamOpFlagTestHelper.java	Thu Nov 19 18:18:05 2015 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,48 +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 java.util.stream;
-
-import java.util.EnumSet;
-
-public class StreamOpFlagTestHelper {
-
-    /** EnumSet containing stream flags */
-    private static final EnumSet<StreamOpFlag> allStreamFlags;
-
-    static {
-        allStreamFlags = EnumSet.allOf(StreamOpFlag.class);
-        for (StreamOpFlag f : EnumSet.allOf(StreamOpFlag.class))
-            if (!f.isStreamFlag())
-                allStreamFlags.remove(f);
-    }
-
-
-    static EnumSet<StreamOpFlag> allStreamFlags() {
-        // EnumSet is mutable
-        return allStreamFlags.clone();
-    }
-
-    public static boolean isStreamOrdered(Stream<?> s) {
-        return StreamOpFlag.ORDERED.isKnown(OpTestCase.getStreamFlags(s));
-    }
-}
--- a/jdk/test/java/util/stream/bootlib/java/util/stream/StreamTestDataProvider.java	Thu Nov 19 18:18:05 2015 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,205 +0,0 @@
-/*
- * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package java.util.stream;
-
-import org.testng.annotations.DataProvider;
-
-import java.util.*;
-import java.util.Spliterators;
-import java.util.function.Supplier;
-
-/**
- * StreamTestDataProvider
- *
- * @author Brian Goetz
- */
-/** TestNG DataProvider for ref-valued streams */
-public class StreamTestDataProvider {
-    private static final Integer[] to0 = new Integer[0];
-    private static final Integer[] to1 = new Integer[1];
-    private static final Integer[] to10 = new Integer[10];
-    private static final Integer[] to100 = new Integer[100];
-    private static final Integer[] to1000 = new Integer[1000];
-    private static final Integer[] reversed = new Integer[100];
-    private static final Integer[] ones = new Integer[100];
-    private static final Integer[] twice = new Integer[200];
-    private static final Integer[] pseudoRandom;
-
-    private static final Object[][] testData;
-    private static final Object[][] withNullTestData;
-    private static final Object[][] spliteratorTestData;
-
-    static {
-        Integer[][] arrays = {to0, to1, to10, to100, to1000};
-        for (Integer[] arr : arrays) {
-            for (int i = 0; i < arr.length; i++) {
-                arr[i] = i;
-            }
-        }
-        for (int i = 0; i < reversed.length; i++) {
-            reversed[i] = reversed.length - i;
-        }
-        for (int i = 0; i < ones.length; i++) {
-            ones[i] = 1;
-        }
-        System.arraycopy(to100, 0, twice, 0, to100.length);
-        System.arraycopy(to100, 0, twice, to100.length, to100.length);
-        pseudoRandom = new Integer[LambdaTestHelpers.LONG_STRING.length()];
-        for (int i = 0; i < LambdaTestHelpers.LONG_STRING.length(); i++) {
-            pseudoRandom[i] = (int) LambdaTestHelpers.LONG_STRING.charAt(i);
-        }
-    }
-
-    static final Object[][] arrays = {
-            {"empty", to0},
-            {"0..1", to1},
-            {"0..10", to10},
-            {"0..100", to100},
-            {"0..1000", to1000},
-            {"100x[1]", ones},
-            {"2x[0..100]", twice},
-            {"reverse 0..100", reversed},
-            {"pseudorandom", pseudoRandom}
-    };
-
-    static {
-        {
-            List<Object[]> list = new ArrayList<>();
-            for (Object[] data : arrays) {
-                final Object name = data[0];
-                final Integer[] ints = (Integer[])data[1];
-                final List<Integer> intsAsList = Arrays.asList(ints);
-
-                list.add(arrayDataDescr("array:" + name, ints));
-                list.add(collectionDataDescr("ArrayList.asList:" + name, intsAsList));
-                list.add(collectionDataDescr("ArrayList:" + name, new ArrayList<>(intsAsList)));
-                list.add(streamDataDescr("DelegatingStream(ArrayList):" + name,
-                                         () -> new ArrayList<>(intsAsList).stream()));
-                List<Integer> aList = new ArrayList<>(intsAsList);
-                if (LambdaTestMode.isNormalMode()) {
-                    // Only include sub-lists for normal test execution mode
-                    // This data is serialization-hostile since the state of the
-                    // deserialized sub-list will be out of sync with the
-                    // enclosing list.
-                    list.add(collectionDataDescr("ArrayList.Sublist:" + name,
-                                                 (ints.length) <= 1 ? aList.subList(0, 0) : aList.subList(1, ints.length / 2)));
-                }
-                list.add(collectionDataDescr("LinkedList:" + name, new LinkedList<>(intsAsList)));
-                list.add(collectionDataDescr("HashSet:" + name, new HashSet<>(intsAsList)));
-                list.add(collectionDataDescr("LinkedHashSet:" + name, new LinkedHashSet<>(intsAsList)));
-                list.add(collectionDataDescr("TreeSet:" + name, new TreeSet<>(intsAsList)));
-                SpinedBuffer<Integer> spinedBuffer = new SpinedBuffer<>();
-                intsAsList.forEach(spinedBuffer);
-                list.add(sbDataDescr("SpinedBuffer:" + name, spinedBuffer));
-
-                // @@@ Add more
-            }
-            testData = list.toArray(new Object[0][]);
-        }
-
-        // Simple combination of numbers and null values, probably excessive but may catch
-        // errors for initialization/termination/sequence
-        // @@@ This is separate from the other data for now until nulls are consistently supported by
-        // all operations
-        {
-            List<Object[]> list = new ArrayList<>();
-            int size = 5;
-            for (int i = 0; i < (1 << size) - 2; i++) {
-                Integer[] content = new Integer[size];
-                for (int e = 0; e < size; e++) {
-                    content[e] = (i & (1 << e)) > 0 ? e + 1 : null;
-                }
-
-                // ORDERED
-                list.add(arrayDataDescr("array:" + i, content));
-                // not ORDERED, DISTINCT
-                list.add(collectionDataDescr("HashSet:" + i, new HashSet<>(Arrays.asList(content))));
-            }
-
-            withNullTestData = list.toArray(new Object[0][]);
-        }
-
-        {
-            List<Object[]> spliterators = new ArrayList<>();
-            for (Object[] data : arrays) {
-                final Object name = data[0];
-                final Integer[] ints = (Integer[])data[1];
-
-                spliterators.add(splitDescr("Arrays.s(array):" + name,
-                                            () -> Arrays.spliterator(ints)));
-                spliterators.add(splitDescr("arrays.s(array,o,l):" + name,
-                                            () -> Arrays.spliterator(ints, 0, ints.length/2)));
-                spliterators.add(splitDescr("SpinedBuffer.s():" + name,
-                                            () -> {
-                                                SpinedBuffer<Integer> sb = new SpinedBuffer<>();
-                                                for (Integer i : ints)
-                                                    sb.accept(i);
-                                                return sb.spliterator();
-                                            }));
-                spliterators.add(splitDescr("Iterators.s(Arrays.s(array).iterator(), size):" + name,
-                                            () -> Spliterators.spliterator(Arrays.asList(ints).iterator(), ints.length, 0)));
-                spliterators.add(splitDescr("Iterators.s(Arrays.s(array).iterator()):" + name,
-                                            () -> Spliterators.spliteratorUnknownSize(Arrays.asList(ints).iterator(), 0)));
-                // @@@ Add map and collection spliterators when spliterator() is exposed on Collection or Iterable
-            }
-            spliteratorTestData = spliterators.toArray(new Object[0][]);
-        }
-    }
-
-    static <T> Object[] arrayDataDescr(String description, T[] data) {
-        return new Object[] { description, TestData.Factory.ofArray(description, data)};
-    }
-
-    static <T> Object[] streamDataDescr(String description, Supplier<Stream<T>> supplier) {
-        return new Object[] { description, TestData.Factory.ofSupplier(description, supplier)};
-    }
-
-    static <T> Object[] collectionDataDescr(String description, Collection<T> data) {
-        return new Object[] { description, TestData.Factory.ofCollection(description, data)};
-    }
-
-    static <T> Object[] sbDataDescr(String description, SpinedBuffer<T> data) {
-        return new Object[] { description, TestData.Factory.ofSpinedBuffer(description, data)};
-    }
-
-    static <T> Object[] splitDescr(String description, Supplier<Spliterator<T>> ss) {
-        return new Object[] { description, ss };
-    }
-
-    // Return an array of ( String name, StreamTestData<Integer> )
-    @DataProvider(name = "StreamTestData<Integer>")
-    public static Object[][] makeStreamTestData() {
-        return testData;
-    }
-
-    @DataProvider(name = "withNull:StreamTestData<Integer>")
-    public static Object[][] makeStreamWithNullTestData() {
-        return withNullTestData;
-    }
-
-    // returns an array of (String name, Supplier<Spliterator<Integer>>)
-    @DataProvider(name = "Spliterator<Integer>")
-    public static Object[][] spliteratorProvider() {
-        return spliteratorTestData;
-    }
-}
--- a/jdk/test/java/util/stream/bootlib/java/util/stream/StreamTestScenario.java	Thu Nov 19 18:18:05 2015 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,278 +0,0 @@
-/*
- * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package java.util.stream;
-
-import java.util.Collections;
-import java.util.EnumSet;
-import java.util.Iterator;
-import java.util.Set;
-import java.util.Spliterator;
-import java.util.function.Consumer;
-import java.util.function.Function;
-
-/**
- * Test scenarios for reference streams.
- *
- * Each scenario is provided with a data source, a function that maps a fresh
- * stream (as provided by the data source) to a new stream, and a sink to
- * receive results.  Each scenario describes a different way of computing the
- * stream contents.  The test driver will ensure that all scenarios produce
- * the same output (modulo allowable differences in ordering).
- */
-@SuppressWarnings({"rawtypes", "unchecked"})
-public enum StreamTestScenario implements OpTestCase.BaseStreamTestScenario {
-
-    STREAM_FOR_EACH(false) {
-        <T, U, S_IN extends BaseStream<T, S_IN>>
-        void run(TestData<T, S_IN> data, S_IN source, Consumer<U> b, Function<S_IN, Stream<U>> m) {
-            Stream<U> s = m.apply(source);
-            if (s.isParallel()) {
-                s = s.sequential();
-            }
-            s.forEach(b);
-        }
-    },
-
-    // Collec to list
-    STREAM_COLLECT(false) {
-        <T, U, S_IN extends BaseStream<T, S_IN>>
-        void run(TestData<T, S_IN> data, S_IN source, Consumer<U> b, Function<S_IN, Stream<U>> m) {
-            for (U t : m.apply(source).collect(Collectors.toList())) {
-                b.accept(t);
-            }
-        }
-    },
-
-    // To array
-    STREAM_TO_ARRAY(false) {
-        <T, U, S_IN extends BaseStream<T, S_IN>>
-        void run(TestData<T, S_IN> data, S_IN source, Consumer<U> b, Function<S_IN, Stream<U>> m) {
-            for (Object t : m.apply(source).toArray()) {
-                b.accept((U) t);
-            }
-        }
-    },
-
-    // Wrap as stream, and iterate in pull mode
-    STREAM_ITERATOR(false) {
-        <T, U, S_IN extends BaseStream<T, S_IN>>
-        void run(TestData<T, S_IN> data, S_IN source, Consumer<U> b, Function<S_IN, Stream<U>> m) {
-            for (Iterator<U> seqIter = m.apply(source).iterator(); seqIter.hasNext(); )
-                b.accept(seqIter.next());
-        }
-    },
-
-    // Wrap as stream, and spliterate then iterate in pull mode
-    STREAM_SPLITERATOR(false) {
-        <T, U, S_IN extends BaseStream<T, S_IN>>
-        void run(TestData<T, S_IN> data, S_IN source, Consumer<U> b, Function<S_IN, Stream<U>> m) {
-            for (Spliterator<U> spl = m.apply(source).spliterator(); spl.tryAdvance(b); ) {
-            }
-        }
-    },
-
-    // Wrap as stream, spliterate, then split a few times mixing advances with forEach
-    STREAM_SPLITERATOR_WITH_MIXED_TRAVERSE_AND_SPLIT(false) {
-        <T, U, S_IN extends BaseStream<T, S_IN>>
-        void run(TestData<T, S_IN> data, S_IN source, Consumer<U> b, Function<S_IN, Stream<U>> m) {
-            SpliteratorTestHelper.mixedTraverseAndSplit(b, m.apply(source).spliterator());
-        }
-    },
-
-    // Wrap as stream, and spliterate then iterate in pull mode
-    STREAM_SPLITERATOR_FOREACH(false) {
-        <T, U, S_IN extends BaseStream<T, S_IN>>
-        void run(TestData<T, S_IN> data, S_IN source, Consumer<U> b, Function<S_IN, Stream<U>> m) {
-            m.apply(source).spliterator().forEachRemaining(b);
-        }
-    },
-
-    // Wrap as parallel stream + sequential
-    PAR_STREAM_SEQUENTIAL_FOR_EACH(true) {
-        <T, U, S_IN extends BaseStream<T, S_IN>>
-        void run(TestData<T, S_IN> data, S_IN source, Consumer<U> b, Function<S_IN, Stream<U>> m) {
-            m.apply(source).sequential().forEach(b);
-        }
-    },
-
-    // Wrap as parallel stream + forEachOrdered
-    PAR_STREAM_FOR_EACH_ORDERED(true) {
-        <T, U, S_IN extends BaseStream<T, S_IN>>
-        void run(TestData<T, S_IN> data, S_IN source, Consumer<U> b, Function<S_IN, Stream<U>> m) {
-            // @@@ Want to explicitly select ordered equalator
-            m.apply(source).forEachOrdered(b);
-        }
-    },
-
-    // Wrap as stream, and spliterate then iterate sequentially
-    PAR_STREAM_SPLITERATOR(true) {
-        <T, U, S_IN extends BaseStream<T, S_IN>>
-        void run(TestData<T, S_IN> data, S_IN source, Consumer<U> b, Function<S_IN, Stream<U>> m) {
-            for (Spliterator<U> spl = m.apply(source).spliterator(); spl.tryAdvance(b); ) {
-            }
-        }
-    },
-
-    // Wrap as stream, and spliterate then iterate sequentially
-    PAR_STREAM_SPLITERATOR_FOREACH(true) {
-        <T, U, S_IN extends BaseStream<T, S_IN>>
-        void run(TestData<T, S_IN> data, S_IN source, Consumer<U> b, Function<S_IN, Stream<U>> m) {
-            m.apply(source).spliterator().forEachRemaining(b);
-        }
-    },
-
-    // Wrap as parallel stream + toArray
-    PAR_STREAM_TO_ARRAY(true) {
-        <T, U, S_IN extends BaseStream<T, S_IN>>
-        void run(TestData<T, S_IN> data, S_IN source, Consumer<U> b, Function<S_IN, Stream<U>> m) {
-            for (Object t : m.apply(source).toArray())
-                b.accept((U) t);
-        }
-    },
-
-    // Wrap as parallel stream, get the spliterator, wrap as a stream + toArray
-    PAR_STREAM_SPLITERATOR_STREAM_TO_ARRAY(true) {
-        <T, U, S_IN extends BaseStream<T, S_IN>>
-        void run(TestData<T, S_IN> data, S_IN source, Consumer<U> b, Function<S_IN, Stream<U>> m) {
-            Stream<U> s = m.apply(source);
-            Spliterator<U> sp = s.spliterator();
-            Stream<U> ss = StreamSupport.stream(() -> sp,
-                                                StreamOpFlag.toCharacteristics(OpTestCase.getStreamFlags(s))
-                                                | (sp.getExactSizeIfKnown() < 0 ? 0 : Spliterator.SIZED), true);
-            for (Object t : ss.toArray())
-                b.accept((U) t);
-        }
-    },
-
-    // Wrap as parallel stream + toArray and clear SIZED flag
-    PAR_STREAM_TO_ARRAY_CLEAR_SIZED(true) {
-        <T, U, S_IN extends BaseStream<T, S_IN>>
-        void run(TestData<T, S_IN> data, S_IN source, Consumer<U> b, Function<S_IN, Stream<U>> m) {
-            S_IN pipe1 = (S_IN) OpTestCase.chain(source,
-                                                 new FlagDeclaringOp(StreamOpFlag.NOT_SIZED, data.getShape()));
-            Stream<U> pipe2 = m.apply(pipe1);
-
-            for (Object t : pipe2.toArray())
-                b.accept((U) t);
-        }
-    },
-
-    // Wrap as parallel + collect to list
-    PAR_STREAM_COLLECT_TO_LIST(true) {
-        <T, U, S_IN extends BaseStream<T, S_IN>>
-        void run(TestData<T, S_IN> data, S_IN source, Consumer<U> b, Function<S_IN, Stream<U>> m) {
-            for (U u : m.apply(source).collect(Collectors.toList()))
-                b.accept(u);
-        }
-    },
-
-    // Wrap sequential as parallel, + collect to list
-    STREAM_TO_PAR_STREAM_COLLECT_TO_LIST(true) {
-        public <T, S_IN extends BaseStream<T, S_IN>>
-        S_IN getStream(TestData<T, S_IN> data) {
-            return data.stream().parallel();
-        }
-
-        <T, U, S_IN extends BaseStream<T, S_IN>>
-        void run(TestData<T, S_IN> data, S_IN source, Consumer<U> b, Function<S_IN, Stream<U>> m) {
-            for (U u : m.apply(source).collect(Collectors.toList()))
-                b.accept(u);
-        }
-    },
-
-    // Wrap parallel as sequential,, + collect
-    PAR_STREAM_TO_STREAM_COLLECT_TO_LIST(true) {
-        <T, U, S_IN extends BaseStream<T, S_IN>>
-        void run(TestData<T, S_IN> data, S_IN source, Consumer<U> b, Function<S_IN, Stream<U>> m) {
-            for (U u : m.apply(source).collect(Collectors.toList()))
-                b.accept(u);
-        }
-    },
-
-    // Wrap as parallel stream + forEach synchronizing
-    PAR_STREAM_FOR_EACH(true, false) {
-        <T, U, S_IN extends BaseStream<T, S_IN>>
-        void run(TestData<T, S_IN> data, S_IN source, Consumer<U> b, Function<S_IN, Stream<U>> m) {
-            m.apply(source).forEach(e -> {
-                synchronized (data) {
-                    b.accept(e);
-                }
-            });
-        }
-    },
-
-    // Wrap as parallel stream + forEach synchronizing and clear SIZED flag
-    PAR_STREAM_FOR_EACH_CLEAR_SIZED(true, false) {
-        <T, U, S_IN extends BaseStream<T, S_IN>>
-        void run(TestData<T, S_IN> data, S_IN source, Consumer<U> b, Function<S_IN, Stream<U>> m) {
-            S_IN pipe1 = (S_IN) OpTestCase.chain(source,
-                                                 new FlagDeclaringOp(StreamOpFlag.NOT_SIZED, data.getShape()));
-            m.apply(pipe1).forEach(e -> {
-                synchronized (data) {
-                    b.accept(e);
-                }
-            });
-        }
-    },
-    ;
-
-    // The set of scenarios that clean the SIZED flag
-    public static final Set<StreamTestScenario> CLEAR_SIZED_SCENARIOS = Collections.unmodifiableSet(
-            EnumSet.of(PAR_STREAM_TO_ARRAY_CLEAR_SIZED, PAR_STREAM_FOR_EACH_CLEAR_SIZED));
-
-    private final boolean isParallel;
-
-    private final boolean isOrdered;
-
-    StreamTestScenario(boolean isParallel) {
-        this(isParallel, true);
-    }
-
-    StreamTestScenario(boolean isParallel, boolean isOrdered) {
-        this.isParallel = isParallel;
-        this.isOrdered = isOrdered;
-    }
-
-    public StreamShape getShape() {
-        return StreamShape.REFERENCE;
-    }
-
-    public boolean isParallel() {
-        return isParallel;
-    }
-
-    public boolean isOrdered() {
-        return isOrdered;
-    }
-
-    public <T, U, S_IN extends BaseStream<T, S_IN>, S_OUT extends BaseStream<U, S_OUT>>
-    void run(TestData<T, S_IN> data, Consumer<U> b, Function<S_IN, S_OUT> m) {
-        try (S_IN source = getStream(data)) {
-            run(data, source, b, (Function<S_IN, Stream<U>>) m);
-        }
-    }
-
-    abstract <T, U, S_IN extends BaseStream<T, S_IN>>
-    void run(TestData<T, S_IN> data, S_IN source, Consumer<U> b, Function<S_IN, Stream<U>> m);
-
-}
--- a/jdk/test/java/util/stream/bootlib/java/util/stream/TestData.java	Thu Nov 19 18:18:05 2015 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,355 +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 java.util.stream;
-
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.PrimitiveIterator;
-import java.util.Spliterator;
-import java.util.Spliterators;
-import java.util.function.DoubleConsumer;
-import java.util.function.Function;
-import java.util.function.IntConsumer;
-import java.util.function.LongConsumer;
-import java.util.function.Supplier;
-import java.util.function.ToIntFunction;
-
-/** Describes a test data set for use in stream tests */
-public interface TestData<T, S extends BaseStream<T, S>>
-        extends Iterable<T> {
-
-    default int size() {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    default Iterator<T> iterator() {
-        return Spliterators.iterator(spliterator());
-    }
-
-    Spliterator<T> spliterator();
-
-    default boolean isOrdered() {
-        return spliterator().hasCharacteristics(Spliterator.ORDERED);
-    }
-
-    StreamShape getShape();
-
-    default <A extends Collection<? super T>> A into(A target) {
-        spliterator().forEachRemaining(target::add);
-        return target;
-    }
-
-    S stream();
-
-    S parallelStream();
-
-    public interface OfRef<T> extends TestData<T, Stream<T>> { }
-
-    public interface OfInt extends TestData<Integer, IntStream> { }
-
-    public interface OfLong extends TestData<Long, LongStream> { }
-
-    public interface OfDouble extends TestData<Double, DoubleStream> { }
-
-    // @@@ Temporary garbage class to avoid triggering bugs with lambdas in static methods in interfaces
-    public static class Factory {
-        public static <T> OfRef<T> ofArray(String name, T[] array) {
-            return new AbstractTestData.RefTestData<>(name, array, Arrays::stream, a -> Arrays.stream(a).parallel(),
-                                                      Arrays::spliterator, a -> a.length);
-        }
-
-        public static <T> OfRef<T> ofCollection(String name, Collection<T> collection) {
-            return new AbstractTestData.RefTestData<>(name, collection, Collection::stream, Collection::parallelStream,
-                                                      Collection::spliterator, Collection::size);
-        }
-
-        public static <T> OfRef<T> ofSpinedBuffer(String name, SpinedBuffer<T> buffer) {
-            return new AbstractTestData.RefTestData<>(name, buffer,
-                                                      b -> StreamSupport.stream(b.spliterator(), false),
-                                                      b -> StreamSupport.stream(b.spliterator(), true),
-                                                      SpinedBuffer::spliterator,
-                                                      b -> (int) b.count());
-        }
-
-        public static <T> OfRef<T> ofSupplier(String name, Supplier<Stream<T>> supplier) {
-            return new AbstractTestData.RefTestData<>(name, supplier,
-                                                      Supplier::get,
-                                                      s -> s.get().parallel(),
-                                                      s -> s.get().spliterator(),
-                                                      s -> (int) s.get().spliterator().getExactSizeIfKnown());
-        }
-
-        public static <T> OfRef<T> ofRefNode(String name, Node<T> node) {
-            return new AbstractTestData.RefTestData<>(name, node,
-                                                      n -> StreamSupport.stream(n::spliterator, Spliterator.SIZED | Spliterator.ORDERED, false),
-                                                      n -> StreamSupport.stream(n::spliterator, Spliterator.SIZED | Spliterator.ORDERED, true),
-                                                      Node::spliterator,
-                                                      n -> (int) n.count());
-        }
-
-        // int factories
-        public static <T> OfInt ofArray(String name, int[] array) {
-            return new AbstractTestData.IntTestData<>(name, array, Arrays::stream, a -> Arrays.stream(a).parallel(),
-                                                      Arrays::spliterator, a -> a.length);
-        }
-
-        public static OfInt ofSpinedBuffer(String name, SpinedBuffer.OfInt buffer) {
-            return new AbstractTestData.IntTestData<>(name, buffer,
-                                                      b -> StreamSupport.intStream(b.spliterator(), false),
-                                                      b -> StreamSupport.intStream(b.spliterator(), true),
-                                                      SpinedBuffer.OfInt::spliterator,
-                                                      b -> (int) b.count());
-        }
-
-        public static OfInt ofIntSupplier(String name, Supplier<IntStream> supplier) {
-            return new AbstractTestData.IntTestData<>(name, supplier,
-                                                      Supplier::get,
-                                                      s -> s.get().parallel(),
-                                                      s -> s.get().spliterator(),
-                                                      s -> (int) s.get().spliterator().getExactSizeIfKnown());
-        }
-
-        public static OfInt ofNode(String name, Node.OfInt node) {
-            int characteristics = Spliterator.SIZED | Spliterator.ORDERED;
-            return new AbstractTestData.IntTestData<>(name, node,
-                                                      n -> StreamSupport.intStream(n::spliterator, characteristics, false),
-                                                      n -> StreamSupport.intStream(n::spliterator, characteristics, true),
-                                                      Node.OfInt::spliterator,
-                                                      n -> (int) n.count());
-        }
-
-        // long factories
-        public static <T> OfLong ofArray(String name, long[] array) {
-            return new AbstractTestData.LongTestData<>(name, array, Arrays::stream, a -> Arrays.stream(a).parallel(),
-                                                       Arrays::spliterator, a -> a.length);
-        }
-
-        public static OfLong ofSpinedBuffer(String name, SpinedBuffer.OfLong buffer) {
-            return new AbstractTestData.LongTestData<>(name, buffer,
-                                                      b -> StreamSupport.longStream(b.spliterator(), false),
-                                                      b -> StreamSupport.longStream(b.spliterator(), true),
-                                                      SpinedBuffer.OfLong::spliterator,
-                                                      b -> (int) b.count());
-        }
-
-        public static OfLong ofLongSupplier(String name, Supplier<LongStream> supplier) {
-            return new AbstractTestData.LongTestData<>(name, supplier,
-                                                      Supplier::get,
-                                                      s -> s.get().parallel(),
-                                                      s -> s.get().spliterator(),
-                                                      s -> (int) s.get().spliterator().getExactSizeIfKnown());
-        }
-
-        public static OfLong ofNode(String name, Node.OfLong node) {
-            int characteristics = Spliterator.SIZED | Spliterator.ORDERED;
-            return new AbstractTestData.LongTestData<>(name, node,
-                                                      n -> StreamSupport.longStream(n::spliterator, characteristics, false),
-                                                      n -> StreamSupport.longStream(n::spliterator, characteristics, true),
-                                                      Node.OfLong::spliterator,
-                                                      n -> (int) n.count());
-        }
-
-        // double factories
-        public static <T> OfDouble ofArray(String name, double[] array) {
-            return new AbstractTestData.DoubleTestData<>(name, array, Arrays::stream, a -> Arrays.stream(a).parallel(),
-                                                         Arrays::spliterator, a -> a.length);
-        }
-
-        public static OfDouble ofSpinedBuffer(String name, SpinedBuffer.OfDouble buffer) {
-            return new AbstractTestData.DoubleTestData<>(name, buffer,
-                                                         b -> StreamSupport.doubleStream(b.spliterator(), false),
-                                                         b -> StreamSupport.doubleStream(b.spliterator(), true),
-                                                         SpinedBuffer.OfDouble::spliterator,
-                                                         b -> (int) b.count());
-        }
-
-        public static OfDouble ofDoubleSupplier(String name, Supplier<DoubleStream> supplier) {
-            return new AbstractTestData.DoubleTestData<>(name, supplier,
-                                                         Supplier::get,
-                                                         s -> s.get().parallel(),
-                                                         s -> s.get().spliterator(),
-                                                         s -> (int) s.get().spliterator().getExactSizeIfKnown());
-        }
-
-        public static OfDouble ofNode(String name, Node.OfDouble node) {
-            int characteristics = Spliterator.SIZED | Spliterator.ORDERED;
-            return new AbstractTestData.DoubleTestData<>(name, node,
-                                                         n -> StreamSupport.doubleStream(n::spliterator, characteristics, false),
-                                                         n -> StreamSupport.doubleStream(n::spliterator, characteristics, true),
-                                                         Node.OfDouble::spliterator,
-                                                         n -> (int) n.count());
-        }
-    }
-
-
-    abstract class AbstractTestData<T, S extends BaseStream<T, S>,
-            T_STATE,
-                                    T_SPLITR extends Spliterator<T>>
-            implements TestData<T, S> {
-        private final String name;
-        private final StreamShape shape;
-        protected final T_STATE state;
-        private final ToIntFunction<T_STATE> sizeFn;
-        private final Function<T_STATE, S> streamFn;
-        private final Function<T_STATE, S> parStreamFn;
-        private final Function<T_STATE, T_SPLITR> splitrFn;
-
-        AbstractTestData(String name,
-                         StreamShape shape,
-                         T_STATE state,
-                         Function<T_STATE, S> streamFn,
-                         Function<T_STATE, S> parStreamFn,
-                         Function<T_STATE, T_SPLITR> splitrFn,
-                         ToIntFunction<T_STATE> sizeFn) {
-            this.name = name;
-            this.shape = shape;
-            this.state = state;
-            this.streamFn = streamFn;
-            this.parStreamFn = parStreamFn;
-            this.splitrFn = splitrFn;
-            this.sizeFn = sizeFn;
-        }
-
-        @Override
-        public StreamShape getShape() {
-            return shape;
-        }
-
-        @Override
-        public String toString() {
-            return getClass().getSimpleName() + "[" + name + "]";
-        }
-
-        @Override
-        public int size() {
-            return sizeFn.applyAsInt(state);
-        }
-
-        @Override
-        public T_SPLITR spliterator() {
-            return splitrFn.apply(state);
-        }
-
-        @Override
-        public S stream() {
-            return streamFn.apply(state);
-        }
-
-        @Override
-        public S parallelStream() {
-            return parStreamFn.apply(state);
-        }
-
-        public static class RefTestData<T, I>
-                extends AbstractTestData<T, Stream<T>, I, Spliterator<T>>
-                implements TestData.OfRef<T> {
-
-            protected RefTestData(String name,
-                                  I state,
-                                  Function<I, Stream<T>> streamFn,
-                                  Function<I, Stream<T>> parStreamFn,
-                                  Function<I, Spliterator<T>> splitrFn,
-                                  ToIntFunction<I> sizeFn) {
-                super(name, StreamShape.REFERENCE, state, streamFn, parStreamFn, splitrFn, sizeFn);
-            }
-
-        }
-
-        static class IntTestData<I>
-                extends AbstractTestData<Integer, IntStream, I, Spliterator.OfInt>
-                implements TestData.OfInt {
-
-            protected IntTestData(String name,
-                                  I state,
-                                  Function<I, IntStream> streamFn,
-                                  Function<I, IntStream> parStreamFn,
-                                  Function<I, Spliterator.OfInt> splitrFn,
-                                  ToIntFunction<I> sizeFn) {
-                super(name, StreamShape.INT_VALUE, state, streamFn, parStreamFn, splitrFn, sizeFn);
-            }
-
-            @Override
-            public PrimitiveIterator.OfInt iterator() {
-                return Spliterators.iterator(spliterator());
-            }
-
-            @Override
-            public <A extends Collection<? super Integer>> A into(A target) {
-                spliterator().forEachRemaining((IntConsumer) target::add);
-                return target;
-            }
-        }
-
-        static class LongTestData<I>
-                extends AbstractTestData<Long, LongStream, I, Spliterator.OfLong>
-                implements TestData.OfLong {
-
-            protected LongTestData(String name,
-                                   I state,
-                                   Function<I, LongStream> streamFn,
-                                   Function<I, LongStream> parStreamFn,
-                                   Function<I, Spliterator.OfLong> splitrFn,
-                                   ToIntFunction<I> sizeFn) {
-                super(name, StreamShape.LONG_VALUE, state, streamFn, parStreamFn, splitrFn, sizeFn);
-            }
-
-            @Override
-            public PrimitiveIterator.OfLong iterator() {
-                return Spliterators.iterator(spliterator());
-            }
-
-            @Override
-            public <A extends Collection<? super Long>> A into(A target) {
-                spliterator().forEachRemaining((LongConsumer) target::add);
-                return target;
-            }
-        }
-
-        static class DoubleTestData<I>
-                extends AbstractTestData<Double, DoubleStream, I, Spliterator.OfDouble>
-                implements OfDouble {
-
-            protected DoubleTestData(String name,
-                                     I state,
-                                     Function<I, DoubleStream> streamFn,
-                                     Function<I, DoubleStream> parStreamFn,
-                                     Function<I, Spliterator.OfDouble> splitrFn,
-                                     ToIntFunction<I> sizeFn) {
-                super(name, StreamShape.DOUBLE_VALUE, state, streamFn, parStreamFn, splitrFn, sizeFn);
-            }
-
-            @Override
-            public PrimitiveIterator.OfDouble iterator() {
-                return Spliterators.iterator(spliterator());
-            }
-
-            @Override
-            public <A extends Collection<? super Double>> A into(A target) {
-                spliterator().forEachRemaining((DoubleConsumer) target::add);
-                return target;
-            }
-        }
-    }
-}
--- a/jdk/test/java/util/stream/bootlib/java/util/stream/TestFlagExpectedOp.java	Thu Nov 19 18:18:05 2015 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,125 +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 java.util.stream;
-
-import org.testng.Assert;
-
-import java.util.EnumSet;
-
-class TestFlagExpectedOp<T> extends FlagDeclaringOp<T> {
-
-    static class Builder<T> {
-        final int flags;
-        StreamShape shape = StreamShape.REFERENCE;
-
-        EnumSet<StreamOpFlag> known = EnumSet.noneOf(StreamOpFlag.class);
-        EnumSet<StreamOpFlag> preserve = EnumSet.noneOf(StreamOpFlag.class);
-        EnumSet<StreamOpFlag> notKnown = EnumSet.noneOf(StreamOpFlag.class);
-
-        Builder(int flags) {
-            this.flags = flags;
-        }
-
-        Builder<T> known(EnumSet<StreamOpFlag> known) {
-            this.known = known;
-            return this;
-        }
-
-        Builder<T> preserve(EnumSet<StreamOpFlag> preserve) {
-            this.preserve = preserve;
-            return this;
-        }
-
-        Builder<T> notKnown(EnumSet<StreamOpFlag> notKnown) {
-            this.notKnown = notKnown;
-            return this;
-        }
-
-        Builder<T> shape(StreamShape shape) {
-            this.shape = shape;
-            return this;
-        }
-
-        TestFlagExpectedOp<T> build() {
-            return new TestFlagExpectedOp<>(flags, known, preserve, notKnown, shape);
-        }
-    }
-
-    final EnumSet<StreamOpFlag> known;
-    final EnumSet<StreamOpFlag> preserve;
-    final EnumSet<StreamOpFlag> notKnown;
-    final StreamShape shape;
-
-    TestFlagExpectedOp(int flags,
-                       EnumSet<StreamOpFlag> known,
-                       EnumSet<StreamOpFlag> preserve,
-                       EnumSet<StreamOpFlag> notKnown) {
-        this(flags, known, preserve, notKnown, StreamShape.REFERENCE);
-    }
-
-    TestFlagExpectedOp(int flags,
-                       EnumSet<StreamOpFlag> known,
-                       EnumSet<StreamOpFlag> preserve,
-                       EnumSet<StreamOpFlag> notKnown,
-                       StreamShape shape) {
-        super(flags);
-        this.known = known;
-        this.preserve = preserve;
-        this.notKnown = notKnown;
-        this.shape = shape;
-    }
-
-    @Override
-    public StreamShape outputShape() {
-        return shape;
-    }
-
-    @Override
-    public StreamShape inputShape() {
-        return shape;
-    }
-
-    @Override
-    @SuppressWarnings({"rawtypes", "unchecked"})
-    public Sink<T> opWrapSink(int flags, boolean parallel, Sink upstream) {
-        assertFlags(flags);
-        return upstream;
-    }
-
-    private void assertFlags(int flags) {
-        for (StreamOpFlag f : known) {
-            Assert.assertTrue(f.isKnown(flags),
-                              String.format("Flag %s is not known, but should be known.", f.toString()));
-        }
-
-        for (StreamOpFlag f : preserve) {
-            Assert.assertTrue(f.isPreserved(flags),
-                              String.format("Flag %s is not preserved, but should be preserved.", f.toString()));
-        }
-
-        for (StreamOpFlag f : notKnown) {
-            Assert.assertFalse(f.isKnown(flags),
-                               String.format("Flag %s is known, but should be not known.", f.toString()));
-        }
-    }
-}
--- a/jdk/test/java/util/stream/bootlib/java/util/stream/ThowableHelper.java	Thu Nov 19 18:18:05 2015 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +0,0 @@
-/*
- * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package java.util.stream;
-
-import static org.testng.Assert.assertNotNull;
-import static org.testng.Assert.assertTrue;
-
-public final class ThowableHelper {
-
-    public static void checkException(Class<? extends Exception> ce, Runnable r) {
-        Exception caught = null;
-        try {
-            r.run();
-        } catch (Exception e) {
-            caught = e;
-        }
-
-        assertNotNull(caught);
-        assertTrue(ce.isInstance(caught));
-    }
-
-    public static void checkNPE(Runnable r) {
-        checkException(NullPointerException.class, r);
-    }
-
-    public static void checkISE(Runnable r) {
-        checkException(IllegalStateException.class, r);
-    }
-}
--- a/jdk/test/java/util/stream/boottest/TEST.properties	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/test/java/util/stream/boottest/TEST.properties	Mon Nov 23 14:37:04 2015 -0500
@@ -1,5 +1,5 @@
 # This file identifies root(s) of the test-ng hierarchy.
 
-TestNG.dirs = .
-bootclasspath.dirs = .
-lib.dirs = /java/util/stream/bootlib
+TestNG.dirs = java.base
+bootclasspath.dirs = java.base
+lib.dirs = /java/util/stream/bootlib/java.base
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/stream/boottest/java.base/java/util/stream/DoubleNodeTest.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 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 java.util.stream;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.PrimitiveIterator;
+import java.util.Spliterators;
+import java.util.function.Function;
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+@Test
+public class DoubleNodeTest extends OpTestCase {
+
+    @DataProvider(name = "nodes")
+    public Object[][] createSizes() {
+        List<Object[]> params = new ArrayList<>();
+
+        for (int size : Arrays.asList(0, 1, 4, 15, 16, 17, 127, 128, 129, 1000)) {
+            double[] array = new double[size];
+            for (int i = 0; i < array.length; i++) {
+                array[i] = i;
+            }
+
+            List<Node<Double>> nodes = new ArrayList<>();
+
+            nodes.add(Nodes.node(array));
+            nodes.add(degenerateTree(Spliterators.iterator(Arrays.spliterator(array))));
+            nodes.add(tree(toList(array), l -> Nodes.node(toDoubleArray(l))));
+            nodes.add(fill(array, Nodes.doubleBuilder(array.length)));
+            nodes.add(fill(array, Nodes.doubleBuilder()));
+
+            for (Node<Double> node : nodes) {
+                params.add(new Object[]{array, node});
+            }
+
+        }
+
+        return params.toArray(new Object[0][]);
+    }
+
+    private static void assertEqualsListDoubleArray(List<Double> list, double[] array) {
+        assertEquals(list.size(), array.length);
+        for (int i = 0; i < array.length; i++)
+            assertEquals(array[i], list.get(i));
+    }
+
+    private List<Double> toList(double[] a) {
+        List<Double> l = new ArrayList<>();
+        for (double i : a) {
+            l.add(i);
+        }
+
+        return l;
+    }
+
+    private double[] toDoubleArray(List<Double> l) {
+        double[] a = new double[l.size()];
+
+        int i = 0;
+        for (Double e : l) {
+            a[i++] = e;
+        }
+        return a;
+    }
+
+    private Node.OfDouble fill(double[] array, Node.Builder.OfDouble nb) {
+        nb.begin(array.length);
+        for (double i : array)
+            nb.accept(i);
+        nb.end();
+        return nb.build();
+    }
+
+    private Node.OfDouble degenerateTree(PrimitiveIterator.OfDouble it) {
+        if (!it.hasNext()) {
+            return Nodes.node(new double[0]);
+        }
+
+        double i = it.nextDouble();
+        if (it.hasNext()) {
+            return new Nodes.ConcNode.OfDouble(Nodes.node(new double[] {i}), degenerateTree(it));
+        }
+        else {
+            return Nodes.node(new double[] {i});
+        }
+    }
+
+    private Node.OfDouble tree(List<Double> l, Function<List<Double>, Node.OfDouble> m) {
+        if (l.size() < 3) {
+            return m.apply(l);
+        }
+        else {
+            return new Nodes.ConcNode.OfDouble(
+                    tree(l.subList(0, l.size() / 2), m),
+                    tree(l.subList(l.size() / 2, l.size()), m));
+        }
+    }
+
+    @Test(dataProvider = "nodes")
+    public void testAsArray(double[] array, Node.OfDouble n) {
+        assertEquals(n.asPrimitiveArray(), array);
+    }
+
+    @Test(dataProvider = "nodes")
+    public void testFlattenAsArray(double[] array, Node.OfDouble n) {
+        assertEquals(Nodes.flattenDouble(n).asPrimitiveArray(), array);
+    }
+
+    @Test(dataProvider = "nodes")
+    public void testCopyTo(double[] array, Node.OfDouble n) {
+        double[] copy = new double[(int) n.count()];
+        n.copyInto(copy, 0);
+
+        assertEquals(copy, array);
+    }
+
+    @Test(dataProvider = "nodes", groups = { "serialization-hostile" })
+    public void testForEach(double[] array, Node.OfDouble n) {
+        List<Double> l = new ArrayList<>((int) n.count());
+        n.forEach((double e) -> {
+            l.add(e);
+        });
+
+        assertEqualsListDoubleArray(l, array);
+    }
+
+    @Test(dataProvider = "nodes")
+    public void testStreams(double[] array, Node.OfDouble n) {
+        TestData.OfDouble data = TestData.Factory.ofNode("Node", n);
+
+        exerciseOps(data, s -> s);
+
+        exerciseTerminalOps(data, s -> s.toArray());
+    }
+
+    @Test(dataProvider = "nodes", groups={ "serialization-hostile" })
+    // throws SOE on serialization of DoubleConcNode[size=1000]
+    public void testSpliterator(double[] array, Node.OfDouble n) {
+        SpliteratorTestHelper.testDoubleSpliterator(n::spliterator);
+    }
+
+    @Test(dataProvider = "nodes")
+    public void testTruncate(double[] array, Node.OfDouble n) {
+        int[] nums = new int[] { 0, 1, array.length / 2, array.length - 1, array.length };
+        for (int start : nums)
+            for (int end : nums) {
+                if (start < 0 || end < 0 || end < start || end > array.length)
+                    continue;
+                Node.OfDouble slice = n.truncate(start, end, Double[]::new);
+                double[] asArray = slice.asPrimitiveArray();
+                for (int k = start; k < end; k++)
+                    assertEquals(array[k], asArray[k - start]);
+            }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/stream/boottest/java.base/java/util/stream/FlagOpTest.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,283 @@
+/*
+ * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package java.util.stream;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import java.util.ArrayList;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.function.Supplier;
+
+import static java.util.stream.LambdaTestHelpers.countTo;
+
+@Test
+public class FlagOpTest extends OpTestCase {
+
+    @Test(dataProvider = "StreamTestData<Integer>", dataProviderClass = StreamTestDataProvider.class)
+    public void testFlagsPassThrough(String name, TestData<Integer, Stream<Integer>> data) {
+
+        @SuppressWarnings({"unchecked", "rawtypes"})
+        TestFlagPassThroughOp<Integer>[] ops = new TestFlagPassThroughOp[3];
+        ops[0] = new TestFlagPassThroughOp<>();
+        ops[1] = new TestFlagPassThroughOp<>();
+        ops[2] = new TestFlagPassThroughOp<>();
+
+        ops[0].set(null, ops[1]);
+        ops[1].set(ops[0], ops[2]);
+        ops[2].set(ops[1], null);
+
+        withData(data).ops(ops).exercise();
+    }
+
+    static class TestFlagPassThroughOp<T> extends FlagDeclaringOp<T> {
+        TestFlagPassThroughOp<T> upstream;
+        TestFlagPassThroughOp<T> downstream;
+
+        TestFlagPassThroughOp() {
+            super(0);
+        }
+
+        void set(TestFlagPassThroughOp<T> upstream, TestFlagPassThroughOp<T> downstream)  {
+            this.upstream = upstream;
+            this.downstream = downstream;
+        }
+
+        int wrapFlags;
+
+        @Override
+        @SuppressWarnings({"unchecked", "rawtypes"})
+        public Sink<T> opWrapSink(int flags, boolean parallel, Sink sink) {
+            this.wrapFlags = flags;
+
+            if (downstream != null) {
+                assertTrue(flags == downstream.wrapFlags);
+            }
+
+            return sink;
+        }
+    }
+
+    public void testFlagsClearAllSet() {
+        int clearAllFlags = 0;
+        for (StreamOpFlag f : EnumSet.allOf(StreamOpFlag.class)) {
+            if (f.isStreamFlag()) {
+                clearAllFlags |= f.clear();
+            }
+        }
+
+        EnumSet<StreamOpFlag> known = EnumSet.noneOf(StreamOpFlag.class);
+        EnumSet<StreamOpFlag> notKnown = StreamOpFlagTestHelper.allStreamFlags();
+
+        List<FlagDeclaringOp<Integer>> ops = new ArrayList<>();
+        ops.add(new FlagDeclaringOp<>(clearAllFlags));
+        for (StreamOpFlag f : StreamOpFlagTestHelper.allStreamFlags()) {
+            if (f.canSet(StreamOpFlag.Type.OP)) {
+                ops.add(new TestFlagExpectedOp<>(f.set(),
+                                             known.clone(),
+                                             EnumSet.noneOf(StreamOpFlag.class),
+                                             notKnown.clone()));
+                known.add(f);
+                notKnown.remove(f);
+            }
+        }
+        ops.add(new TestFlagExpectedOp<>(0,
+                                         known.clone(),
+                                         EnumSet.noneOf(StreamOpFlag.class),
+                                         notKnown.clone()));
+
+        TestData<Integer, Stream<Integer>> data = TestData.Factory.ofArray("Array", countTo(10).toArray(new Integer[0]));
+        @SuppressWarnings("rawtypes")
+        FlagDeclaringOp[] opsArray = ops.toArray(new FlagDeclaringOp[ops.size()]);
+
+        withData(data).ops(opsArray).
+                without(StreamTestScenario.CLEAR_SIZED_SCENARIOS).
+                exercise();
+    }
+
+    public void testFlagsSetAllClear() {
+        EnumSet<StreamOpFlag> known = StreamOpFlagTestHelper.allStreamFlags();
+        int setAllFlags = 0;
+        for (StreamOpFlag f : EnumSet.allOf(StreamOpFlag.class)) {
+            if (f.isStreamFlag()) {
+                if (f.canSet(StreamOpFlag.Type.OP)) {
+                    setAllFlags |= f.set();
+                } else {
+                    known.remove(f);
+                }
+            }
+        }
+
+        EnumSet<StreamOpFlag> notKnown = EnumSet.noneOf(StreamOpFlag.class);
+
+        List<FlagDeclaringOp<Integer>> ops = new ArrayList<>();
+        ops.add(new FlagDeclaringOp<>(setAllFlags));
+        for (StreamOpFlag f : StreamOpFlagTestHelper.allStreamFlags()) {
+            ops.add(new TestFlagExpectedOp<>(f.clear(),
+                                             known.clone(),
+                                             EnumSet.noneOf(StreamOpFlag.class),
+                                             notKnown.clone()));
+            known.remove(f);
+            notKnown.add(f);
+        }
+        ops.add(new TestFlagExpectedOp<>(0,
+                                         known.clone(),
+                                         EnumSet.noneOf(StreamOpFlag.class),
+                                         notKnown.clone()));
+
+        TestData<Integer, Stream<Integer>> data = TestData.Factory.ofArray("Array", countTo(10).toArray(new Integer[0]));
+        @SuppressWarnings("rawtypes")
+        FlagDeclaringOp[] opsArray = ops.toArray(new FlagDeclaringOp[ops.size()]);
+
+
+        withData(data).ops(opsArray).
+                without(StreamTestScenario.CLEAR_SIZED_SCENARIOS).
+                exercise();
+    }
+
+    public void testFlagsParallelCollect() {
+        testFlagsSetSequence(CollectorOps::collector);
+    }
+
+    private void testFlagsSetSequence(Supplier<StatefulTestOp<Integer>> cf) {
+        EnumSet<StreamOpFlag> known = EnumSet.of(StreamOpFlag.ORDERED, StreamOpFlag.SIZED);
+        EnumSet<StreamOpFlag> preserve = EnumSet.of(StreamOpFlag.DISTINCT, StreamOpFlag.SORTED);
+
+        List<IntermediateTestOp<Integer, Integer>> ops = new ArrayList<>();
+        for (StreamOpFlag f : EnumSet.of(StreamOpFlag.DISTINCT, StreamOpFlag.SORTED)) {
+            ops.add(cf.get());
+            ops.add(new TestFlagExpectedOp<>(f.set(),
+                                             known.clone(),
+                                             preserve.clone(),
+                                             EnumSet.noneOf(StreamOpFlag.class)));
+            known.add(f);
+            preserve.remove(f);
+        }
+        ops.add(cf.get());
+        ops.add(new TestFlagExpectedOp<>(0,
+                                         known.clone(),
+                                         preserve.clone(),
+                                         EnumSet.noneOf(StreamOpFlag.class)));
+
+        TestData<Integer, Stream<Integer>> data = TestData.Factory.ofArray("Array", countTo(10).toArray(new Integer[0]));
+        @SuppressWarnings("rawtypes")
+        IntermediateTestOp[] opsArray = ops.toArray(new IntermediateTestOp[ops.size()]);
+
+        withData(data).ops(opsArray).
+                without(StreamTestScenario.CLEAR_SIZED_SCENARIOS).
+                exercise();
+    }
+
+
+    public void testFlagsClearParallelCollect() {
+        testFlagsClearSequence(CollectorOps::collector);
+    }
+
+    protected void testFlagsClearSequence(Supplier<StatefulTestOp<Integer>> cf) {
+        EnumSet<StreamOpFlag> known = EnumSet.of(StreamOpFlag.ORDERED, StreamOpFlag.SIZED);
+        EnumSet<StreamOpFlag> preserve = EnumSet.of(StreamOpFlag.DISTINCT, StreamOpFlag.SORTED);
+        EnumSet<StreamOpFlag> notKnown = EnumSet.noneOf(StreamOpFlag.class);
+
+        List<IntermediateTestOp<Integer, Integer>> ops = new ArrayList<>();
+        for (StreamOpFlag f : EnumSet.of(StreamOpFlag.ORDERED, StreamOpFlag.DISTINCT, StreamOpFlag.SORTED)) {
+            ops.add(cf.get());
+            ops.add(new TestFlagExpectedOp<>(f.clear(),
+                                             known.clone(),
+                                             preserve.clone(),
+                                             notKnown.clone()));
+            known.remove(f);
+            preserve.remove(f);
+            notKnown.add(f);
+        }
+        ops.add(cf.get());
+        ops.add(new TestFlagExpectedOp<>(0,
+                                         known.clone(),
+                                         preserve.clone(),
+                                         notKnown.clone()));
+
+        TestData<Integer, Stream<Integer>> data = TestData.Factory.ofArray("Array", countTo(10).toArray(new Integer[0]));
+        @SuppressWarnings("rawtypes")
+        IntermediateTestOp[] opsArray = ops.toArray(new IntermediateTestOp[ops.size()]);
+
+        withData(data).ops(opsArray).
+                without(StreamTestScenario.CLEAR_SIZED_SCENARIOS).
+                exercise();
+    }
+
+    public void testFlagsSizedOrderedParallelCollect() {
+        EnumSet<StreamOpFlag> parKnown = EnumSet.of(StreamOpFlag.SIZED);
+        EnumSet<StreamOpFlag> serKnown = parKnown.clone();
+
+        List<IntermediateTestOp<Integer, Integer>> ops = new ArrayList<>();
+        for (StreamOpFlag f : parKnown) {
+            ops.add(CollectorOps.collector());
+            ops.add(new ParSerTestFlagExpectedOp<>(f.clear(),
+                                             parKnown,
+                                             serKnown));
+            serKnown.remove(f);
+        }
+        ops.add(CollectorOps.collector());
+        ops.add(new ParSerTestFlagExpectedOp<>(0,
+                                         parKnown,
+                                         EnumSet.noneOf(StreamOpFlag.class)));
+
+        TestData<Integer, Stream<Integer>> data = TestData.Factory.ofArray("Array", countTo(10).toArray(new Integer[0]));
+        @SuppressWarnings("rawtypes")
+        IntermediateTestOp[] opsArray = ops.toArray(new IntermediateTestOp[ops.size()]);
+
+        withData(data).ops(opsArray).exercise();
+    }
+
+    static class ParSerTestFlagExpectedOp<T> extends FlagDeclaringOp<T> {
+        final EnumSet<StreamOpFlag> parKnown;
+        final EnumSet<StreamOpFlag> serKnown;
+
+        ParSerTestFlagExpectedOp(int flags, EnumSet<StreamOpFlag> known, EnumSet<StreamOpFlag> serKnown) {
+            super(flags);
+            this.parKnown = known;
+            this.serKnown = serKnown;
+        }
+
+        @Override
+        @SuppressWarnings({"unchecked", "rawtypes"})
+        public Sink<T> opWrapSink(int flags, boolean parallel, Sink upstream) {
+            assertFlags(flags, parallel);
+            return upstream;
+        }
+
+        protected void assertFlags(int flags, boolean parallel) {
+            if (parallel) {
+                for (StreamOpFlag f : parKnown) {
+                    Assert.assertTrue(f.isKnown(flags), String.format("Flag %s is not known, but should be known.", f.toString()));
+                }
+
+            } else {
+                for (StreamOpFlag f : serKnown) {
+                    Assert.assertTrue(f.isKnown(flags), String.format("Flag %s is not known, but should be known.", f.toString()));
+                }
+
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/stream/boottest/java.base/java/util/stream/IntNodeTest.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,177 @@
+/*
+ * 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 java.util.stream;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.PrimitiveIterator;
+import java.util.Spliterators;
+import java.util.function.Function;
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+@Test
+public class IntNodeTest extends OpTestCase {
+
+    @DataProvider(name = "nodes")
+    public Object[][] createSizes() {
+        List<Object[]> params = new ArrayList<>();
+
+        for (int size : Arrays.asList(0, 1, 4, 15, 16, 17, 127, 128, 129, 1000)) {
+            int[] array = new int[size];
+            for (int i = 0; i < array.length; i++) {
+                array[i] = i;
+            }
+
+            List<Node<Integer>> nodes = new ArrayList<>();
+
+            nodes.add(Nodes.node(array));
+            nodes.add(degenerateTree(Spliterators.iterator(Arrays.spliterator(array))));
+            nodes.add(tree(toList(array), l -> Nodes.node(toIntArray(l))));
+            nodes.add(fill(array, Nodes.intBuilder(array.length)));
+            nodes.add(fill(array, Nodes.intBuilder()));
+
+            for (Node<Integer> node : nodes) {
+                params.add(new Object[]{array, node});
+            }
+
+        }
+
+        return params.toArray(new Object[0][]);
+    }
+
+    private static void assertEqualsListIntArray(List<Integer> list, int[] array) {
+        assertEquals(list.size(), array.length);
+        for (int i = 0; i < array.length; i++)
+            assertEquals(array[i], (int) list.get(i));
+    }
+
+    private List<Integer> toList(int[] a) {
+        List<Integer> l = new ArrayList<>();
+        for (int i : a) {
+            l.add(i);
+        }
+
+        return l;
+    }
+
+    private int[] toIntArray(List<Integer> l) {
+        int[] a = new int[l.size()];
+
+        int i = 0;
+        for (Integer e : l) {
+            a[i++] = e;
+        }
+        return a;
+    }
+
+    private Node.OfInt fill(int[] array, Node.Builder.OfInt nb) {
+        nb.begin(array.length);
+        for (int i : array)
+            nb.accept(i);
+        nb.end();
+        return nb.build();
+    }
+
+    private Node.OfInt degenerateTree(PrimitiveIterator.OfInt it) {
+        if (!it.hasNext()) {
+            return Nodes.node(new int[0]);
+        }
+
+        int i = it.nextInt();
+        if (it.hasNext()) {
+            return new Nodes.ConcNode.OfInt(Nodes.node(new int[] {i}), degenerateTree(it));
+        }
+        else {
+            return Nodes.node(new int[] {i});
+        }
+    }
+
+    private Node.OfInt tree(List<Integer> l, Function<List<Integer>, Node.OfInt> m) {
+        if (l.size() < 3) {
+            return m.apply(l);
+        }
+        else {
+            return new Nodes.ConcNode.OfInt(
+                    tree(l.subList(0, l.size() / 2), m),
+                    tree(l.subList(l.size() / 2, l.size()), m));
+        }
+    }
+
+    @Test(dataProvider = "nodes")
+    public void testAsArray(int[] array, Node.OfInt n) {
+        assertEquals(n.asPrimitiveArray(), array);
+    }
+
+    @Test(dataProvider = "nodes")
+    public void testFlattenAsArray(int[] array, Node.OfInt n) {
+        assertEquals(Nodes.flattenInt(n).asPrimitiveArray(), array);
+    }
+
+    @Test(dataProvider = "nodes")
+    public void testCopyTo(int[] array, Node.OfInt n) {
+        int[] copy = new int[(int) n.count()];
+        n.copyInto(copy, 0);
+
+        assertEquals(copy, array);
+    }
+
+    @Test(dataProvider = "nodes", groups = { "serialization-hostile" })
+    public void testForEach(int[] array, Node.OfInt n) {
+        List<Integer> l = new ArrayList<>((int) n.count());
+        n.forEach((int e) -> {
+            l.add(e);
+        });
+
+        assertEqualsListIntArray(l, array);
+    }
+
+    @Test(dataProvider = "nodes")
+    public void testStreams(int[] array, Node.OfInt n) {
+        TestData.OfInt data = TestData.Factory.ofNode("Node", n);
+
+        exerciseOps(data, s -> s);
+        exerciseTerminalOps(data, s -> s.toArray());
+    }
+
+    @Test(dataProvider = "nodes")
+    public void testSpliterator(int[] array, Node.OfInt n) {
+        SpliteratorTestHelper.testIntSpliterator(n::spliterator);
+    }
+
+    @Test(dataProvider = "nodes")
+    public void testTruncate(int[] array, Node.OfInt n) {
+        int[] nums = new int[] { 0, 1, array.length / 2, array.length - 1, array.length };
+        for (int start : nums)
+            for (int end : nums) {
+                if (start < 0 || end < 0 || end < start || end > array.length)
+                    continue;
+                Node.OfInt slice = n.truncate(start, end, Integer[]::new);
+                int[] asArray = slice.asPrimitiveArray();
+                for (int k = start; k < end; k++)
+                    assertEquals(array[k], asArray[k - start]);
+            }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/stream/boottest/java.base/java/util/stream/LongNodeTest.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,178 @@
+/*
+ * Copyright (c) 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 java.util.stream;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.PrimitiveIterator;
+import java.util.Spliterators;
+import java.util.function.Function;
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+@Test
+public class LongNodeTest extends OpTestCase {
+
+    @DataProvider(name = "nodes")
+    public Object[][] createSizes() {
+        List<Object[]> params = new ArrayList<>();
+
+        for (int size : Arrays.asList(0, 1, 4, 15, 16, 17, 127, 128, 129, 1000)) {
+            long[] array = new long[size];
+            for (int i = 0; i < array.length; i++) {
+                array[i] = i;
+            }
+
+            List<Node<Long>> nodes = new ArrayList<>();
+
+            nodes.add(Nodes.node(array));
+            nodes.add(degenerateTree(Spliterators.iterator(Arrays.spliterator(array))));
+            nodes.add(tree(toList(array), l -> Nodes.node(toLongArray(l))));
+            nodes.add(fill(array, Nodes.longBuilder(array.length)));
+            nodes.add(fill(array, Nodes.longBuilder()));
+
+            for (Node<Long> node : nodes) {
+                params.add(new Object[]{array, node});
+            }
+
+        }
+
+        return params.toArray(new Object[0][]);
+    }
+
+    private static void assertEqualsListLongArray(List<Long> list, long[] array) {
+        assertEquals(list.size(), array.length);
+        for (int i = 0; i < array.length; i++)
+            assertEquals(array[i], (long) list.get(i));
+    }
+
+    private List<Long> toList(long[] a) {
+        List<Long> l = new ArrayList<>();
+        for (long i : a) {
+            l.add(i);
+        }
+
+        return l;
+    }
+
+    private long[] toLongArray(List<Long> l) {
+        long[] a = new long[l.size()];
+
+        int i = 0;
+        for (Long e : l) {
+            a[i++] = e;
+        }
+        return a;
+    }
+
+    private Node.OfLong fill(long[] array, Node.Builder.OfLong nb) {
+        nb.begin(array.length);
+        for (long i : array)
+            nb.accept(i);
+        nb.end();
+        return nb.build();
+    }
+
+    private Node.OfLong degenerateTree(PrimitiveIterator.OfLong it) {
+        if (!it.hasNext()) {
+            return Nodes.node(new long[0]);
+        }
+
+        long i = it.nextLong();
+        if (it.hasNext()) {
+            return new Nodes.ConcNode.OfLong(Nodes.node(new long[] {i}), degenerateTree(it));
+        }
+        else {
+            return Nodes.node(new long[] {i});
+        }
+    }
+
+    private Node.OfLong tree(List<Long> l, Function<List<Long>, Node.OfLong> m) {
+        if (l.size() < 3) {
+            return m.apply(l);
+        }
+        else {
+            return new Nodes.ConcNode.OfLong(
+                    tree(l.subList(0, l.size() / 2), m),
+                    tree(l.subList(l.size() / 2, l.size()), m));
+        }
+    }
+
+    @Test(dataProvider = "nodes")
+    public void testAsArray(long[] array, Node.OfLong n) {
+        assertEquals(n.asPrimitiveArray(), array);
+    }
+
+    @Test(dataProvider = "nodes")
+    public void testFlattenAsArray(long[] array, Node.OfLong n) {
+        assertEquals(Nodes.flattenLong(n).asPrimitiveArray(), array);
+    }
+
+    @Test(dataProvider = "nodes")
+    public void testCopyTo(long[] array, Node.OfLong n) {
+        long[] copy = new long[(int) n.count()];
+        n.copyInto(copy, 0);
+
+        assertEquals(copy, array);
+    }
+
+    @Test(dataProvider = "nodes", groups = { "serialization-hostile" })
+    public void testForEach(long[] array, Node.OfLong n) {
+        List<Long> l = new ArrayList<>((int) n.count());
+        n.forEach((long e) -> {
+            l.add(e);
+        });
+
+        assertEqualsListLongArray(l, array);
+    }
+
+    @Test(dataProvider = "nodes")
+    public void testStreams(long[] array, Node.OfLong n) {
+        TestData.OfLong data = TestData.Factory.ofNode("Node", n);
+
+        exerciseOps(data, s -> s);
+
+        exerciseTerminalOps(data, s -> s.toArray());
+    }
+
+    @Test(dataProvider = "nodes")
+    public void testSpliterator(long[] array, Node.OfLong n) {
+        SpliteratorTestHelper.testLongSpliterator(n::spliterator);
+    }
+
+    @Test(dataProvider = "nodes")
+    public void testTruncate(long[] array, Node.OfLong n) {
+        int[] nums = new int[] { 0, 1, array.length / 2, array.length - 1, array.length };
+        for (int start : nums)
+            for (int end : nums) {
+                if (start < 0 || end < 0 || end < start || end > array.length)
+                    continue;
+                Node.OfLong slice = n.truncate(start, end, Long[]::new);
+                long[] asArray = slice.asPrimitiveArray();
+                for (int k = start; k < end; k++)
+                    assertEquals(array[k], asArray[k - start]);
+            }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/stream/boottest/java.base/java/util/stream/NodeBuilderTest.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,236 @@
+/*
+ * 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 java.util.stream;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.function.DoubleConsumer;
+import java.util.function.Function;
+import java.util.function.IntConsumer;
+import java.util.function.LongConsumer;
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import static java.util.stream.LambdaTestHelpers.assertContents;
+import static java.util.stream.LambdaTestHelpers.countTo;
+import static org.testng.Assert.assertEquals;
+
+@Test
+public class NodeBuilderTest {
+
+    List<Integer> sizes = Arrays.asList(0, 1, 4, 16, 256,
+                                        1023, 1024, 1025,
+                                        2047, 2048, 2049,
+                                        1024 * 32 - 1, 1024 * 32, 1024 * 32 + 1);
+
+    @DataProvider(name = "Node.Builder")
+    public Object[][] createNodeBuilders() {
+        List<List<Integer>> ls = new ArrayList<>();
+        for (int size : sizes) {
+            ls.add(countTo(size));
+        }
+
+        List<Function<Integer, Node.Builder<Integer>>> ms = Arrays.asList(
+                s -> Nodes.builder(),
+                s -> Nodes.builder(s, LambdaTestHelpers.integerArrayGenerator)
+        );
+
+        Object[][] params = new Object[ls.size() * ms.size()][];
+        int i = 0;
+        for (List<Integer> l : ls) {
+            for (Function<Integer, Node.Builder<Integer>> m : ms) {
+                params[i++] = new Object[]{l, m};
+            }
+        }
+
+        return params;
+    }
+
+    @Test(dataProvider = "Node.Builder", groups = { "serialization-hostile" })
+    public void testIteration(List<Integer> l, Function<Integer, Node.Builder<Integer>> m) {
+        Node.Builder<Integer> nb = m.apply(l.size());
+        nb.begin(l.size());
+        for (Integer i : l) {
+            nb.accept(i);
+        }
+        nb.end();
+
+        Node<Integer> n = nb.build();
+        assertEquals(n.count(), l.size());
+
+        {
+            List<Integer> _l = new ArrayList<>();
+            n.forEach(_l::add);
+
+            assertContents(_l, l);
+        }
+    }
+
+    // Node.Builder.OfInt
+
+    @DataProvider(name = "Node.Builder<Integer>")
+    public Object[][] createIntNodeBuilders() {
+        List<List<Integer>> ls = new ArrayList<>();
+        for (int size : sizes) {
+            ls.add(countTo(size));
+        }
+
+        List<Function<Integer, Node.Builder<Integer>>> ms = Arrays.asList(
+                s -> Nodes.intBuilder(),
+                s -> Nodes.intBuilder(s)
+        );
+
+        Object[][] params = new Object[ls.size() * ms.size()][];
+        int i = 0;
+        for (List<Integer> l : ls) {
+            for (Function<Integer, Node.Builder<Integer>> m : ms) {
+                params[i++] = new Object[]{l, m};
+            }
+        }
+
+        return params;
+    }
+
+    @Test(dataProvider = "Node.Builder<Integer>", groups = { "serialization-hostile" })
+    public void testIntIteration(List<Integer> l, Function<Integer, Node.Builder.OfInt> m) {
+        Node.Builder.OfInt nb = m.apply(l.size());
+        nb.begin(l.size());
+        for (Integer i : l) {
+            nb.accept((int) i);
+        }
+        nb.end();
+
+        Node.OfInt n = nb.build();
+        assertEquals(n.count(), l.size());
+
+        {
+            List<Integer> _l = new ArrayList<>();
+            n.forEach((IntConsumer) _l::add);
+
+            assertContents(_l, l);
+        }
+
+    }
+
+    // Node.Builder.OfLong
+
+    @DataProvider(name = "Node.Builder<Long>")
+    public Object[][] createLongNodeBuilders() {
+        List<List<Long>> ls = new ArrayList<>();
+        for (int size : sizes) {
+            List<Long> l = new ArrayList<>();
+            for (long i = 0; i < size; i++) {
+                l.add(i);
+            }
+            ls.add(l);
+        }
+
+        List<Function<Integer, Node.Builder<Long>>> ms = Arrays.asList(
+                s -> Nodes.longBuilder(),
+                s -> Nodes.longBuilder(s)
+        );
+
+        Object[][] params = new Object[ls.size() * ms.size()][];
+        int i = 0;
+        for (List<Long> l : ls) {
+            for (Function<Integer, Node.Builder<Long>> m : ms) {
+                params[i++] = new Object[]{l, m};
+            }
+        }
+
+        return params;
+    }
+
+    @Test(dataProvider = "Node.Builder<Long>")
+    public void testLongIteration(List<Long> l, Function<Integer, Node.Builder.OfLong> m) {
+        Node.Builder.OfLong nb = m.apply(l.size());
+        nb.begin(l.size());
+        for (Long i : l) {
+            nb.accept((long) i);
+        }
+        nb.end();
+
+        Node.OfLong n = nb.build();
+        assertEquals(n.count(), l.size());
+
+        {
+            List<Long> _l = new ArrayList<>();
+            n.forEach((LongConsumer) _l::add);
+
+            assertContents(_l, l);
+        }
+
+    }
+
+    // Node.Builder.OfDouble
+
+    @DataProvider(name = "Node.Builder<Double>")
+    public Object[][] createDoubleNodeBuilders() {
+        List<List<Double>> ls = new ArrayList<>();
+        for (int size : sizes) {
+            List<Double> l = new ArrayList<>();
+            for (long i = 0; i < size; i++) {
+                l.add((double) i);
+            }
+            ls.add(l);
+        }
+
+        List<Function<Integer, Node.Builder<Double>>> ms = Arrays.asList(
+                s -> Nodes.doubleBuilder(),
+                s -> Nodes.doubleBuilder(s)
+        );
+
+        Object[][] params = new Object[ls.size() * ms.size()][];
+        int i = 0;
+        for (List<Double> l : ls) {
+            for (Function<Integer, Node.Builder<Double>> m : ms) {
+                params[i++] = new Object[]{l, m};
+            }
+        }
+
+        return params;
+    }
+
+    @Test(dataProvider = "Node.Builder<Double>")
+    public void testDoubleIteration(List<Double> l, Function<Integer, Node.Builder.OfDouble> m) {
+        Node.Builder.OfDouble nb = m.apply(l.size());
+        nb.begin(l.size());
+        for (Double i : l) {
+            nb.accept((double) i);
+        }
+        nb.end();
+
+        Node.OfDouble n = nb.build();
+        assertEquals(n.count(), l.size());
+
+        {
+            List<Double> _l = new ArrayList<>();
+            n.forEach((DoubleConsumer) _l::add);
+
+            assertContents(_l, l);
+        }
+
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/stream/boottest/java.base/java/util/stream/NodeTest.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,154 @@
+/*
+ * 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 java.util.stream;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.function.Function;
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+@Test
+public class NodeTest extends OpTestCase {
+
+    @DataProvider(name = "nodes")
+    public Object[][] createSizes() {
+        List<Object[]> params = new ArrayList<>();
+
+        for (int size : Arrays.asList(0, 1, 4, 15, 16, 17, 127, 128, 129, 1000)) {
+            Integer[] array = new Integer[size];
+            for (int i = 0; i < array.length; i++) {
+                array[i] = i;
+            }
+
+            List<Node<Integer>> nodes = new ArrayList<>();
+            nodes.add(Nodes.node(array));
+            nodes.add(Nodes.node(Arrays.asList(array)));
+            nodes.add(degenerateTree(Arrays.asList(array).iterator()));
+            nodes.add(tree(Arrays.asList(array), l -> Nodes.node(l.toArray(new Integer[l.size()]))));
+            nodes.add(tree(Arrays.asList(array), l -> Nodes.node(l)));
+            nodes.add(fill(array, Nodes.builder(array.length, LambdaTestHelpers.integerArrayGenerator)));
+            nodes.add(fill(array, Nodes.builder()));
+
+            for (int i = 0; i < nodes.size(); i++) {
+                params.add(new Object[]{array, nodes.get(i)});
+            }
+
+        }
+
+        return params.toArray(new Object[0][]);
+    }
+
+    Node<Integer> fill(Integer[] array, Node.Builder<Integer> nb) {
+        nb.begin(array.length);
+        for (Integer i : array) {
+            nb.accept(i);
+        }
+        nb.end();
+        return nb.build();
+    }
+
+    Node<Integer> degenerateTree(Iterator<Integer> it) {
+        if (!it.hasNext()) {
+            return Nodes.node(Collections.emptyList());
+        }
+
+        Integer i = it.next();
+        if (it.hasNext()) {
+            return new Nodes.ConcNode<Integer>(Nodes.node(new Integer[] {i}), degenerateTree(it));
+        }
+        else {
+            return Nodes.node(new Integer[]{i});
+        }
+    }
+
+    Node<Integer> tree(List<Integer> l, Function<List<Integer>, Node<Integer>> m) {
+        if (l.size() < 3) {
+            return m.apply(l);
+        }
+        else {
+            return new Nodes.ConcNode<>(
+                    tree(l.subList(0, l.size() / 2), m),
+                    tree(l.subList(l.size() / 2, l.size()), m ));
+        }
+    }
+
+    @Test(dataProvider = "nodes")
+    public void testAsArray(Integer[] array, Node<Integer> n) {
+        assertEquals(n.asArray(LambdaTestHelpers.integerArrayGenerator), array);
+    }
+
+    @Test(dataProvider = "nodes")
+    public void testFlattenAsArray(Integer[] array, Node<Integer> n) {
+        assertEquals(Nodes.flatten(n, LambdaTestHelpers.integerArrayGenerator)
+                          .asArray(LambdaTestHelpers.integerArrayGenerator), array);
+    }
+
+    @Test(dataProvider = "nodes")
+    public void testCopyTo(Integer[] array, Node<Integer> n) {
+        Integer[] copy = new Integer[(int) n.count()];
+        n.copyInto(copy, 0);
+
+        assertEquals(copy, array);
+    }
+
+    @Test(dataProvider = "nodes", groups = { "serialization-hostile" })
+    public void testForEach(Integer[] array, Node<Integer> n) {
+        List<Integer> l = new ArrayList<>((int) n.count());
+        n.forEach(e -> l.add(e));
+
+        assertEquals(l.toArray(), array);
+    }
+
+    @Test(dataProvider = "nodes")
+    public void testStreams(Integer[] array, Node<Integer> n) {
+        TestData<Integer, Stream<Integer>> data = TestData.Factory.ofRefNode("Node", n);
+
+        exerciseOps(data, s -> s);
+
+        exerciseTerminalOps(data, s -> s.toArray());
+    }
+
+    @Test(dataProvider = "nodes")
+    public void testSpliterator(Integer[] array, Node<Integer> n) {
+        SpliteratorTestHelper.testSpliterator(n::spliterator);
+    }
+
+    @Test(dataProvider = "nodes")
+    public void testTruncate(Integer[] array, Node<Integer> n) {
+        int[] nums = new int[] { 0, 1, array.length / 2, array.length - 1, array.length };
+        for (int start : nums)
+            for (int end : nums) {
+                if (start < 0 || end < 0 || end < start || end > array.length)
+                    continue;
+                Node<Integer> slice = n.truncate(start, end, Integer[]::new);
+                Integer[] asArray = slice.asArray(Integer[]::new);
+                for (int k = start; k < end; k++)
+                    assertEquals(array[k], asArray[k - start]);
+            }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/stream/boottest/java.base/java/util/stream/SliceSpliteratorTest.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,201 @@
+/*
+ * Copyright (c) 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 java.util.stream;
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Spliterator;
+
+import static java.util.stream.Collectors.toList;
+import static org.testng.Assert.assertEquals;
+
+/**
+ * @bug 8012987
+ */
+@Test
+public class SliceSpliteratorTest extends LoggingTestCase {
+
+    static class UnorderedContentAsserter<T> implements SpliteratorTestHelper.ContentAsserter<T> {
+        Collection<T> source;
+
+        UnorderedContentAsserter(Collection<T> source) {
+            this.source = source;
+        }
+
+        @Override
+        public void assertContents(Collection<T> actual, Collection<T> expected, boolean isOrdered) {
+            if (isOrdered) {
+                assertEquals(actual, expected);
+            }
+            else {
+                assertEquals(actual.size(), expected.size());
+                assertTrue(source.containsAll(actual));
+            }
+        }
+    }
+
+    interface SliceTester {
+        void test(int size, int skip, int limit);
+    }
+
+    @DataProvider(name = "sliceSpliteratorDataProvider")
+    public static Object[][] sliceSpliteratorDataProvider() {
+        List<Object[]> data = new ArrayList<>();
+
+        // SIZED/SUBSIZED slice spliterator
+
+        {
+            SliceTester r = (size, skip, limit) -> {
+                final Collection<Integer> source =  IntStream.range(0, size).boxed().collect(toList());
+
+                SpliteratorTestHelper.testSpliterator(() -> {
+                    Spliterator<Integer> s = Arrays.spliterator(source.stream().toArray(Integer[]::new));
+
+                    return new StreamSpliterators.SliceSpliterator.OfRef<>(s, skip, limit);
+                });
+            };
+            data.add(new Object[]{"StreamSpliterators.SliceSpliterator.OfRef", r});
+        }
+
+        {
+            SliceTester r = (size, skip, limit) -> {
+                final Collection<Integer> source =  IntStream.range(0, size).boxed().collect(toList());
+
+                SpliteratorTestHelper.testIntSpliterator(() -> {
+                    Spliterator.OfInt s = Arrays.spliterator(source.stream().mapToInt(i->i).toArray());
+
+                    return new StreamSpliterators.SliceSpliterator.OfInt(s, skip, limit);
+                });
+            };
+            data.add(new Object[]{"StreamSpliterators.SliceSpliterator.OfInt", r});
+        }
+
+        {
+            SliceTester r = (size, skip, limit) -> {
+                final Collection<Long> source =  LongStream.range(0, size).boxed().collect(toList());
+
+                SpliteratorTestHelper.testLongSpliterator(() -> {
+                    Spliterator.OfLong s = Arrays.spliterator(source.stream().mapToLong(i->i).toArray());
+
+                    return new StreamSpliterators.SliceSpliterator.OfLong(s, skip, limit);
+                });
+            };
+            data.add(new Object[]{"StreamSpliterators.SliceSpliterator.OfLong", r});
+        }
+
+        {
+            SliceTester r = (size, skip, limit) -> {
+                final Collection<Double> source =  LongStream.range(0, size).asDoubleStream().boxed().collect(toList());
+
+                SpliteratorTestHelper.testDoubleSpliterator(() -> {
+                    Spliterator.OfDouble s = Arrays.spliterator(source.stream().mapToDouble(i->i).toArray());
+
+                    return new StreamSpliterators.SliceSpliterator.OfDouble(s, skip, limit);
+                });
+            };
+            data.add(new Object[]{"StreamSpliterators.SliceSpliterator.OfLong", r});
+        }
+
+
+        // Unordered slice spliterator
+
+        {
+            SliceTester r = (size, skip, limit) -> {
+                final Collection<Integer> source =  IntStream.range(0, size).boxed().collect(toList());
+                final UnorderedContentAsserter<Integer> uca = new UnorderedContentAsserter<>(source);
+
+                SpliteratorTestHelper.testSpliterator(() -> {
+                    Spliterator<Integer> s = Arrays.spliterator(source.stream().toArray(Integer[]::new));
+
+                    return new StreamSpliterators.UnorderedSliceSpliterator.OfRef<>(s, skip, limit);
+                }, uca);
+            };
+            data.add(new Object[]{"StreamSpliterators.UnorderedSliceSpliterator.OfRef", r});
+        }
+
+        {
+            SliceTester r = (size, skip, limit) -> {
+                final Collection<Integer> source =  IntStream.range(0, size).boxed().collect(toList());
+                final UnorderedContentAsserter<Integer> uca = new UnorderedContentAsserter<>(source);
+
+                SpliteratorTestHelper.testIntSpliterator(() -> {
+                    Spliterator.OfInt s = Arrays.spliterator(source.stream().mapToInt(i->i).toArray());
+
+                    return new StreamSpliterators.UnorderedSliceSpliterator.OfInt(s, skip, limit);
+                }, uca);
+            };
+            data.add(new Object[]{"StreamSpliterators.UnorderedSliceSpliterator.OfInt", r});
+        }
+
+        {
+            SliceTester r = (size, skip, limit) -> {
+                final Collection<Long> source =  LongStream.range(0, size).boxed().collect(toList());
+                final UnorderedContentAsserter<Long> uca = new UnorderedContentAsserter<>(source);
+
+                SpliteratorTestHelper.testLongSpliterator(() -> {
+                    Spliterator.OfLong s = Arrays.spliterator(source.stream().mapToLong(i->i).toArray());
+
+                    return new StreamSpliterators.UnorderedSliceSpliterator.OfLong(s, skip, limit);
+                }, uca);
+            };
+            data.add(new Object[]{"StreamSpliterators.UnorderedSliceSpliterator.OfLong", r});
+        }
+
+        {
+            SliceTester r = (size, skip, limit) -> {
+                final Collection<Double> source =  LongStream.range(0, size).asDoubleStream().boxed().collect(toList());
+                final UnorderedContentAsserter<Double> uca = new UnorderedContentAsserter<>(source);
+
+                SpliteratorTestHelper.testDoubleSpliterator(() -> {
+                    Spliterator.OfDouble s = Arrays.spliterator(LongStream.range(0, SIZE).asDoubleStream().toArray());
+
+                    return new StreamSpliterators.UnorderedSliceSpliterator.OfDouble(s, skip, limit);
+                }, uca);
+            };
+            data.add(new Object[]{"StreamSpliterators.UnorderedSliceSpliterator.OfLong", r});
+        }
+
+        return data.toArray(new Object[0][]);
+    }
+
+    static final int SIZE = 256;
+
+    static final int STEP = 32;
+
+    @Test(dataProvider = "sliceSpliteratorDataProvider")
+    public void testSliceSpliterator(String description, SliceTester r) {
+        setContext("size", SIZE);
+        for (int skip = 0; skip < SIZE; skip += STEP) {
+            setContext("skip", skip);
+            for (int limit = 0; limit < SIZE; limit += STEP) {
+                setContext("limit", skip);
+                r.test(SIZE, skip, limit);
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/stream/boottest/java.base/java/util/stream/SpinedBufferTest.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,370 @@
+/*
+ * 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 java.util.stream;
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import java.util.*;
+import java.util.function.DoubleConsumer;
+import java.util.function.IntConsumer;
+import java.util.function.LongConsumer;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+
+@Test
+public class SpinedBufferTest {
+
+    // Create sizes around the boundary of spines
+    static List<Integer> sizes;
+    static {
+        try {
+            sizes = IntStream.range(0, 15)
+                             .map(i -> 1 << i)
+                             .flatMap(i -> Arrays.stream(new int[] { i-2, i-1, i, i+1, i+2 }))
+                             .filter(i -> i >= 0)
+                             .boxed()
+                             .distinct()
+                             .collect(Collectors.toList());
+        }
+        catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    private static final int TEST_SIZE = 5000;
+
+    // SpinedBuffer
+
+    @DataProvider(name = "SpinedBuffer")
+    public Object[][] createSpinedBuffer() {
+        List<Object[]> params = new ArrayList<>();
+
+        for (int size : sizes) {
+            int[] array = IntStream.range(0, size).toArray();
+
+            SpinedBuffer<Integer> sb = new SpinedBuffer<>();
+            Arrays.stream(array).boxed().forEach(sb);
+            params.add(new Object[]{array, sb});
+
+            sb = new SpinedBuffer<>(size / 2);
+            Arrays.stream(array).boxed().forEach(sb);
+            params.add(new Object[]{array, sb});
+
+            sb = new SpinedBuffer<>(size);
+            Arrays.stream(array).boxed().forEach(sb);
+            params.add(new Object[]{array, sb});
+
+            sb = new SpinedBuffer<>(size * 2);
+            Arrays.stream(array).boxed().forEach(sb);
+            params.add(new Object[]{array, sb});
+        }
+
+        return params.toArray(new Object[0][]);
+    }
+
+    @Test(dataProvider = "SpinedBuffer")
+    public void testSpliterator(int[] array, SpinedBuffer<Integer> sb) {
+        assertEquals(sb.count(), array.length);
+        assertEquals(sb.count(), sb.spliterator().getExactSizeIfKnown());
+
+        SpliteratorTestHelper.testSpliterator(sb::spliterator);
+    }
+
+    @Test(dataProvider = "SpinedBuffer", groups = { "serialization-hostile" })
+    public void testLastSplit(int[] array, SpinedBuffer<Integer> sb) {
+        Spliterator<Integer> spliterator = sb.spliterator();
+        Spliterator<Integer> split = spliterator.trySplit();
+        long splitSizes = (split == null) ? 0 : split.getExactSizeIfKnown();
+        long lastSplitSize = spliterator.getExactSizeIfKnown();
+        splitSizes += lastSplitSize;
+
+        assertEquals(splitSizes, array.length);
+
+        List<Integer> contentOfLastSplit = new ArrayList<>();
+        spliterator.forEachRemaining(contentOfLastSplit::add);
+
+        assertEquals(contentOfLastSplit.size(), lastSplitSize);
+
+        List<Integer> end = Arrays.stream(array)
+                .boxed()
+                .skip(array.length - lastSplitSize)
+                .collect(Collectors.toList());
+        assertEquals(contentOfLastSplit, end);
+    }
+
+    @Test(groups = { "serialization-hostile" })
+    public void testSpinedBuffer() {
+        List<Integer> list1 = new ArrayList<>();
+        List<Integer> list2 = new ArrayList<>();
+        SpinedBuffer<Integer> sb = new SpinedBuffer<>();
+        for (int i = 0; i < TEST_SIZE; i++) {
+            list1.add(i);
+            sb.accept(i);
+        }
+        Iterator<Integer> it = sb.iterator();
+        for (int i = 0; i < TEST_SIZE; i++)
+            list2.add(it.next());
+        assertFalse(it.hasNext());
+        assertEquals(list1, list2);
+
+        for (int i = 0; i < TEST_SIZE; i++)
+            assertEquals(sb.get(i), (Integer) i, Integer.toString(i));
+
+        list2.clear();
+        sb.forEach(list2::add);
+        assertEquals(list1, list2);
+        Integer[] array = sb.asArray(LambdaTestHelpers.integerArrayGenerator);
+        list2.clear();
+        for (Integer i : array)
+            list2.add(i);
+        assertEquals(list1, list2);
+    }
+
+    // IntSpinedBuffer
+
+    @DataProvider(name = "IntSpinedBuffer")
+    public Object[][] createIntSpinedBuffer() {
+        List<Object[]> params = new ArrayList<>();
+
+        for (int size : sizes) {
+            int[] array = IntStream.range(0, size).toArray();
+            SpinedBuffer.OfInt sb = new SpinedBuffer.OfInt();
+            Arrays.stream(array).forEach(sb);
+
+            params.add(new Object[]{array, sb});
+        }
+
+        return params.toArray(new Object[0][]);
+    }
+
+    @Test(dataProvider = "IntSpinedBuffer")
+    public void testIntSpliterator(int[] array, SpinedBuffer.OfInt sb) {
+        assertEquals(sb.count(), array.length);
+        assertEquals(sb.count(), sb.spliterator().getExactSizeIfKnown());
+
+        SpliteratorTestHelper.testIntSpliterator(sb::spliterator);
+    }
+
+    @Test(dataProvider = "IntSpinedBuffer", groups = { "serialization-hostile" })
+    public void testIntLastSplit(int[] array, SpinedBuffer.OfInt sb) {
+        Spliterator.OfInt spliterator = sb.spliterator();
+        Spliterator.OfInt split = spliterator.trySplit();
+        long splitSizes = (split == null) ? 0 : split.getExactSizeIfKnown();
+        long lastSplitSize = spliterator.getExactSizeIfKnown();
+        splitSizes += lastSplitSize;
+
+        assertEquals(splitSizes, array.length);
+
+        List<Integer> contentOfLastSplit = new ArrayList<>();
+        spliterator.forEachRemaining((IntConsumer) contentOfLastSplit::add);
+
+        assertEquals(contentOfLastSplit.size(), lastSplitSize);
+
+        List<Integer> end = Arrays.stream(array)
+                .boxed()
+                .skip(array.length - lastSplitSize)
+                .collect(Collectors.toList());
+        assertEquals(contentOfLastSplit, end);
+    }
+
+    @Test(groups = { "serialization-hostile" })
+    public void testIntSpinedBuffer() {
+        List<Integer> list1 = new ArrayList<>();
+        List<Integer> list2 = new ArrayList<>();
+        SpinedBuffer.OfInt sb = new SpinedBuffer.OfInt();
+        for (int i = 0; i < TEST_SIZE; i++) {
+            list1.add(i);
+            sb.accept(i);
+        }
+        PrimitiveIterator.OfInt it = sb.iterator();
+        for (int i = 0; i < TEST_SIZE; i++)
+            list2.add(it.nextInt());
+        assertFalse(it.hasNext());
+        assertEquals(list1, list2);
+
+        for (int i = 0; i < TEST_SIZE; i++)
+            assertEquals(sb.get(i), i, Integer.toString(i));
+
+        list2.clear();
+        sb.forEach((int i) -> list2.add(i));
+        assertEquals(list1, list2);
+        int[] array = sb.asPrimitiveArray();
+        list2.clear();
+        for (int i : array)
+            list2.add(i);
+        assertEquals(list1, list2);
+    }
+
+    // LongSpinedBuffer
+
+    @DataProvider(name = "LongSpinedBuffer")
+    public Object[][] createLongSpinedBuffer() {
+        List<Object[]> params = new ArrayList<>();
+
+        for (int size : sizes) {
+            long[] array = LongStream.range(0, size).toArray();
+            SpinedBuffer.OfLong sb = new SpinedBuffer.OfLong();
+            Arrays.stream(array).forEach(sb);
+
+            params.add(new Object[]{array, sb});
+        }
+
+        return params.toArray(new Object[0][]);
+    }
+
+    @Test(dataProvider = "LongSpinedBuffer")
+    public void testLongSpliterator(long[] array, SpinedBuffer.OfLong sb) {
+        assertEquals(sb.count(), array.length);
+        assertEquals(sb.count(), sb.spliterator().getExactSizeIfKnown());
+
+        SpliteratorTestHelper.testLongSpliterator(sb::spliterator);
+    }
+
+    @Test(dataProvider = "LongSpinedBuffer", groups = { "serialization-hostile" })
+    public void testLongLastSplit(long[] array, SpinedBuffer.OfLong sb) {
+        Spliterator.OfLong spliterator = sb.spliterator();
+        Spliterator.OfLong split = spliterator.trySplit();
+        long splitSizes = (split == null) ? 0 : split.getExactSizeIfKnown();
+        long lastSplitSize = spliterator.getExactSizeIfKnown();
+        splitSizes += lastSplitSize;
+
+        assertEquals(splitSizes, array.length);
+
+        List<Long> contentOfLastSplit = new ArrayList<>();
+        spliterator.forEachRemaining((LongConsumer) contentOfLastSplit::add);
+
+        assertEquals(contentOfLastSplit.size(), lastSplitSize);
+
+        List<Long> end = Arrays.stream(array)
+                .boxed()
+                .skip(array.length - lastSplitSize)
+                .collect(Collectors.toList());
+        assertEquals(contentOfLastSplit, end);
+    }
+
+    @Test(groups = { "serialization-hostile" })
+    public void testLongSpinedBuffer() {
+        List<Long> list1 = new ArrayList<>();
+        List<Long> list2 = new ArrayList<>();
+        SpinedBuffer.OfLong sb = new SpinedBuffer.OfLong();
+        for (long i = 0; i < TEST_SIZE; i++) {
+            list1.add(i);
+            sb.accept(i);
+        }
+        PrimitiveIterator.OfLong it = sb.iterator();
+        for (int i = 0; i < TEST_SIZE; i++)
+            list2.add(it.nextLong());
+        assertFalse(it.hasNext());
+        assertEquals(list1, list2);
+
+        for (int i = 0; i < TEST_SIZE; i++)
+            assertEquals(sb.get(i), i, Long.toString(i));
+
+        list2.clear();
+        sb.forEach((long i) -> list2.add(i));
+        assertEquals(list1, list2);
+        long[] array = sb.asPrimitiveArray();
+        list2.clear();
+        for (long i : array)
+            list2.add(i);
+        assertEquals(list1, list2);
+    }
+
+    // DoubleSpinedBuffer
+
+    @DataProvider(name = "DoubleSpinedBuffer")
+    public Object[][] createDoubleSpinedBuffer() {
+        List<Object[]> params = new ArrayList<>();
+
+        for (int size : sizes) {
+            // @@@ replace with double range when implemented
+            double[] array = LongStream.range(0, size).asDoubleStream().toArray();
+            SpinedBuffer.OfDouble sb = new SpinedBuffer.OfDouble();
+            Arrays.stream(array).forEach(sb);
+
+            params.add(new Object[]{array, sb});
+        }
+
+        return params.toArray(new Object[0][]);
+    }
+
+    @Test(dataProvider = "DoubleSpinedBuffer")
+    public void testDoubleSpliterator(double[] array, SpinedBuffer.OfDouble sb) {
+        assertEquals(sb.count(), array.length);
+        assertEquals(sb.count(), sb.spliterator().getExactSizeIfKnown());
+
+        SpliteratorTestHelper.testDoubleSpliterator(sb::spliterator);
+    }
+
+    @Test(dataProvider = "DoubleSpinedBuffer", groups = { "serialization-hostile" })
+    public void testLongLastSplit(double[] array, SpinedBuffer.OfDouble sb) {
+        Spliterator.OfDouble spliterator = sb.spliterator();
+        Spliterator.OfDouble split = spliterator.trySplit();
+        long splitSizes = (split == null) ? 0 : split.getExactSizeIfKnown();
+        long lastSplitSize = spliterator.getExactSizeIfKnown();
+        splitSizes += lastSplitSize;
+
+        assertEquals(splitSizes, array.length);
+
+        List<Double> contentOfLastSplit = new ArrayList<>();
+        spliterator.forEachRemaining((DoubleConsumer) contentOfLastSplit::add);
+
+        assertEquals(contentOfLastSplit.size(), lastSplitSize);
+
+        List<Double> end = Arrays.stream(array)
+                .boxed()
+                .skip(array.length - lastSplitSize)
+                .collect(Collectors.toList());
+        assertEquals(contentOfLastSplit, end);
+    }
+
+    @Test(groups = { "serialization-hostile" })
+    public void testDoubleSpinedBuffer() {
+        List<Double> list1 = new ArrayList<>();
+        List<Double> list2 = new ArrayList<>();
+        SpinedBuffer.OfDouble sb = new SpinedBuffer.OfDouble();
+        for (long i = 0; i < TEST_SIZE; i++) {
+            list1.add((double) i);
+            sb.accept((double) i);
+        }
+        PrimitiveIterator.OfDouble it = sb.iterator();
+        for (int i = 0; i < TEST_SIZE; i++)
+            list2.add(it.nextDouble());
+        assertFalse(it.hasNext());
+        assertEquals(list1, list2);
+
+        for (int i = 0; i < TEST_SIZE; i++)
+            assertEquals(sb.get(i), (double) i, Double.toString(i));
+
+        list2.clear();
+        sb.forEach((double i) -> list2.add(i));
+        assertEquals(list1, list2);
+        double[] array = sb.asPrimitiveArray();
+        list2.clear();
+        for (double i : array)
+            list2.add(i);
+        assertEquals(list1, list2);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/stream/boottest/java.base/java/util/stream/StreamFlagsTest.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,96 @@
+/*
+ * 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 java.util.stream;
+
+import org.testng.annotations.Test;
+
+import java.util.*;
+import java.util.stream.Stream;
+import java.util.stream.StreamOpFlag;
+import java.util.stream.Streams;
+
+import static java.util.stream.StreamOpFlag.*;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+/**
+ * StreamFlagsTest
+ *
+ * @author Brian Goetz
+ */
+@Test
+public class StreamFlagsTest {
+    Stream<String> arrayList = new ArrayList<String>().stream();
+    Stream<String> linkedList = new LinkedList<String>().stream();
+    Stream<String> hashSet = new HashSet<String>().stream();
+    Stream<String> treeSet = new TreeSet<String>().stream();
+    Stream<String> linkedHashSet = new LinkedHashSet<String>().stream();
+    Stream<String> repeat = Stream.generate(() -> "");
+
+    Stream<?>[] streams = { arrayList, linkedList, hashSet, treeSet, linkedHashSet, repeat };
+
+    private void assertFlags(int value, EnumSet<StreamOpFlag> setFlags, EnumSet<StreamOpFlag> clearFlags) {
+        for (StreamOpFlag flag : setFlags)
+            assertTrue(flag.isKnown(value));
+        for (StreamOpFlag flag : clearFlags)
+            assertTrue(!flag.isKnown(value));
+    }
+
+    public void testBaseStreams() {
+        Stream<String> arrayList = new ArrayList<String>().stream();
+        Stream<String> linkedList = new LinkedList<String>().stream();
+        Stream<String> hashSet = new HashSet<String>().stream();
+        Stream<String> treeSet = new TreeSet<String>().stream();
+        Stream<String> linkedHashSet = new LinkedHashSet<String>().stream();
+        Stream<String> repeat = Stream.generate(() -> "");
+
+        assertFlags(OpTestCase.getStreamFlags(arrayList),
+                    EnumSet.of(ORDERED, SIZED),
+                    EnumSet.of(DISTINCT, SORTED, SHORT_CIRCUIT));
+        assertFlags(OpTestCase.getStreamFlags(linkedList),
+                    EnumSet.of(ORDERED, SIZED),
+                    EnumSet.of(DISTINCT, SORTED, SHORT_CIRCUIT));
+        assertFlags(OpTestCase.getStreamFlags(hashSet),
+                    EnumSet.of(SIZED, DISTINCT),
+                    EnumSet.of(ORDERED, SORTED, SHORT_CIRCUIT));
+        assertFlags(OpTestCase.getStreamFlags(treeSet),
+                    EnumSet.of(ORDERED, SIZED, DISTINCT, SORTED),
+                    EnumSet.of(SHORT_CIRCUIT));
+        assertFlags(OpTestCase.getStreamFlags(linkedHashSet),
+                    EnumSet.of(ORDERED, DISTINCT, SIZED),
+                    EnumSet.of(SORTED, SHORT_CIRCUIT));
+        assertFlags(OpTestCase.getStreamFlags(repeat),
+                    EnumSet.noneOf(StreamOpFlag.class),
+                    EnumSet.of(DISTINCT, SORTED, SHORT_CIRCUIT));
+    }
+
+    public void testFilter() {
+        for (Stream<?> s : streams) {
+            int baseFlags = OpTestCase.getStreamFlags(s);
+            int filteredFlags = OpTestCase.getStreamFlags(s.filter((Object e) -> true));
+            int expectedFlags = baseFlags & ~SIZED.set();
+
+            assertEquals(filteredFlags, expectedFlags);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/stream/boottest/java.base/java/util/stream/StreamOpFlagsTest.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,381 @@
+/*
+ * 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 java.util.stream;
+
+import org.testng.annotations.Test;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.Spliterator;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.ToDoubleFunction;
+import java.util.function.ToIntFunction;
+import java.util.function.ToLongFunction;
+
+import static java.util.stream.Collectors.toList;
+import static java.util.stream.StreamOpFlag.*;
+import static org.testng.Assert.*;
+import static org.testng.Assert.assertEquals;
+
+@Test
+public class StreamOpFlagsTest {
+
+    public void testNullCombine() {
+        int sourceFlags = StreamOpFlag.IS_SIZED;
+
+        assertEquals(sourceFlags, toStreamFlags(combineOpFlags(sourceFlags, StreamOpFlag.INITIAL_OPS_VALUE)));
+    }
+
+    public void testInitialOpFlagsFromSourceFlags() {
+        List<StreamOpFlag> flags = new ArrayList<>(StreamOpFlagTestHelper.allStreamFlags());
+        for (int i = 0; i < (1 << flags.size()); i++)  {
+            int sourceFlags = 0;
+            for (int f = 0; f < flags.size(); f++) {
+                if ((i & (1 << f)) != 0)  {
+                    sourceFlags |= flags.get(f).set();
+                }
+            }
+
+            int opsFlags = combineOpFlags(sourceFlags, StreamOpFlag.INITIAL_OPS_VALUE);
+            assertEquals(opsFlags, (~(sourceFlags << 1)) & StreamOpFlag.INITIAL_OPS_VALUE);
+        }
+    }
+
+    public void testSameCombine() {
+        for (StreamOpFlag f : StreamOpFlagTestHelper.allStreamFlags()) {
+            int sourceFlags = f.set();
+            int opsFlags;
+
+            opsFlags = combineOpFlags(sourceFlags, StreamOpFlag.INITIAL_OPS_VALUE);
+            opsFlags = combineOpFlags(f.set(), opsFlags);
+            assertEquals(sourceFlags, toStreamFlags(opsFlags));
+        }
+    }
+
+    public void testOpClear() {
+        for (StreamOpFlag f : StreamOpFlagTestHelper.allStreamFlags()) {
+            // Clear when source not set
+            int sourceFlags = 0;
+            int opsFlags;
+
+            opsFlags = combineOpFlags(sourceFlags, StreamOpFlag.INITIAL_OPS_VALUE);
+            opsFlags = combineOpFlags(f.clear(), opsFlags);
+            assertEquals(sourceFlags, toStreamFlags(opsFlags));
+
+            // Clear when source set
+            sourceFlags = f.set();
+
+            opsFlags = combineOpFlags(sourceFlags, StreamOpFlag.INITIAL_OPS_VALUE);
+            opsFlags = combineOpFlags(f.clear(), opsFlags);
+            assertEquals(0, toStreamFlags(opsFlags));
+        }
+    }
+
+    public void testOpInject() {
+        for (StreamOpFlag f : StreamOpFlagTestHelper.allStreamFlags()) {
+            // Set when source not set
+            int sourceFlags = 0;
+            int opsFlags;
+
+            opsFlags = combineOpFlags(sourceFlags, StreamOpFlag.INITIAL_OPS_VALUE);
+            opsFlags = combineOpFlags(f.set(), opsFlags);
+            assertEquals(f.set(), toStreamFlags(opsFlags));
+
+            // Set when source set
+            sourceFlags = f.set();
+
+            opsFlags = combineOpFlags(sourceFlags, StreamOpFlag.INITIAL_OPS_VALUE);
+            opsFlags = combineOpFlags(f.set(), opsFlags);
+            assertEquals(sourceFlags, toStreamFlags(opsFlags));
+        }
+    }
+
+    public void testPairSet() {
+        List<Integer> sourceFlagsList
+                = StreamOpFlagTestHelper.allStreamFlags().stream().map(StreamOpFlag::set).collect(toList());
+        sourceFlagsList.add(0, 0);
+
+        for (int sourceFlags : sourceFlagsList) {
+            for (StreamOpFlag f1 : StreamOpFlagTestHelper.allStreamFlags()) {
+                for (StreamOpFlag f2 : StreamOpFlagTestHelper.allStreamFlags()) {
+                    int opsFlags;
+
+                    opsFlags = combineOpFlags(sourceFlags, StreamOpFlag.INITIAL_OPS_VALUE);
+                    opsFlags = combineOpFlags(f1.set(), opsFlags);
+                    opsFlags = combineOpFlags(f2.set(), opsFlags);
+                    assertEquals(sourceFlags | f1.set() | f2.set(), toStreamFlags(opsFlags));
+                }
+            }
+        }
+    }
+
+    public void testPairSetAndClear() {
+        List<Integer> sourceFlagsList
+                = StreamOpFlagTestHelper.allStreamFlags().stream().map(StreamOpFlag::set).collect(toList());
+        sourceFlagsList.add(0, 0);
+
+        for (int sourceFlags : sourceFlagsList) {
+            for (StreamOpFlag f1 : StreamOpFlagTestHelper.allStreamFlags())  {
+                for (StreamOpFlag f2 : StreamOpFlagTestHelper.allStreamFlags()) {
+                    int opsFlags;
+
+                    opsFlags = combineOpFlags(sourceFlags, StreamOpFlag.INITIAL_OPS_VALUE);
+                    opsFlags = combineOpFlags(f1.set(), opsFlags);
+                    opsFlags = combineOpFlags(f2.clear(), opsFlags);
+                    if (f1 == f2)
+                        assertEquals((f2.set() == sourceFlags) ? 0 : sourceFlags, toStreamFlags(opsFlags));
+                    else
+                        assertEquals((f2.set() == sourceFlags) ? f1.set() : sourceFlags | f1.set(), toStreamFlags(opsFlags));
+                }
+            }
+        }
+    }
+
+    public void testShortCircuit() {
+        int opsFlags = combineOpFlags(0, StreamOpFlag.INITIAL_OPS_VALUE);
+        assertFalse(StreamOpFlag.SHORT_CIRCUIT.isKnown(opsFlags));
+
+        opsFlags = combineOpFlags(StreamOpFlag.IS_SHORT_CIRCUIT, opsFlags);
+        assertTrue(StreamOpFlag.SHORT_CIRCUIT.isKnown(opsFlags));
+
+        opsFlags = combineOpFlags(0, opsFlags);
+        assertTrue(StreamOpFlag.SHORT_CIRCUIT.isKnown(opsFlags));
+    }
+
+    public void testApplySourceFlags() {
+        int sourceFlags = StreamOpFlag.IS_SIZED | StreamOpFlag.IS_DISTINCT;
+
+        List<Integer> ops = Arrays.asList(StreamOpFlag.NOT_SIZED, StreamOpFlag.IS_ORDERED | StreamOpFlag.IS_SORTED);
+
+        int opsFlags = StreamOpFlag.combineOpFlags(sourceFlags, StreamOpFlag.INITIAL_OPS_VALUE);
+        for (int opFlags : ops) {
+            opsFlags = combineOpFlags(opFlags, opsFlags);
+        }
+        assertFalse(StreamOpFlag.SIZED.isKnown(opsFlags));
+        assertTrue(StreamOpFlag.SIZED.isCleared(opsFlags));
+        assertFalse(StreamOpFlag.SIZED.isPreserved(opsFlags));
+        assertTrue(StreamOpFlag.DISTINCT.isKnown(opsFlags));
+        assertFalse(StreamOpFlag.DISTINCT.isCleared(opsFlags));
+        assertFalse(StreamOpFlag.DISTINCT.isPreserved(opsFlags));
+        assertTrue(StreamOpFlag.SORTED.isKnown(opsFlags));
+        assertFalse(StreamOpFlag.SORTED.isCleared(opsFlags));
+        assertFalse(StreamOpFlag.SORTED.isPreserved(opsFlags));
+        assertTrue(StreamOpFlag.ORDERED.isKnown(opsFlags));
+        assertFalse(StreamOpFlag.ORDERED.isCleared(opsFlags));
+        assertFalse(StreamOpFlag.ORDERED.isPreserved(opsFlags));
+
+        int streamFlags = toStreamFlags(opsFlags);
+        assertFalse(StreamOpFlag.SIZED.isKnown(streamFlags));
+        assertTrue(StreamOpFlag.DISTINCT.isKnown(streamFlags));
+        assertTrue(StreamOpFlag.SORTED.isKnown(streamFlags));
+        assertTrue(StreamOpFlag.ORDERED.isKnown(streamFlags));
+    }
+
+    public void testSpliteratorMask() {
+        assertSpliteratorMask(StreamOpFlag.DISTINCT.set(), StreamOpFlag.IS_DISTINCT);
+        assertSpliteratorMask(StreamOpFlag.DISTINCT.clear(), 0);
+
+        assertSpliteratorMask(StreamOpFlag.SORTED.set(), StreamOpFlag.IS_SORTED);
+        assertSpliteratorMask(StreamOpFlag.SORTED.clear(), 0);
+
+        assertSpliteratorMask(StreamOpFlag.ORDERED.set(), StreamOpFlag.IS_ORDERED);
+        assertSpliteratorMask(StreamOpFlag.ORDERED.clear(), 0);
+
+        assertSpliteratorMask(StreamOpFlag.SIZED.set(), StreamOpFlag.IS_SIZED);
+        assertSpliteratorMask(StreamOpFlag.SIZED.clear(), 0);
+
+        assertSpliteratorMask(StreamOpFlag.SHORT_CIRCUIT.set(), 0);
+        assertSpliteratorMask(StreamOpFlag.SHORT_CIRCUIT.clear(), 0);
+    }
+
+    private void assertSpliteratorMask(int actual, int expected) {
+        assertEquals(actual & StreamOpFlag.SPLITERATOR_CHARACTERISTICS_MASK, expected);
+    }
+
+    public void testStreamMask() {
+        assertStreamMask(StreamOpFlag.DISTINCT.set(), StreamOpFlag.IS_DISTINCT);
+        assertStreamMask(StreamOpFlag.DISTINCT.clear(), 0);
+
+        assertStreamMask(StreamOpFlag.SORTED.set(), StreamOpFlag.IS_SORTED);
+        assertStreamMask(StreamOpFlag.SORTED.clear(), 0);
+
+        assertStreamMask(StreamOpFlag.ORDERED.set(), StreamOpFlag.IS_ORDERED);
+        assertStreamMask(StreamOpFlag.ORDERED.clear(), 0);
+
+        assertStreamMask(StreamOpFlag.SIZED.set(), StreamOpFlag.IS_SIZED);
+        assertStreamMask(StreamOpFlag.SIZED.clear(), 0);
+
+        assertStreamMask(StreamOpFlag.SHORT_CIRCUIT.set(), 0);
+        assertStreamMask(StreamOpFlag.SHORT_CIRCUIT.clear(), 0);
+    }
+
+    private void assertStreamMask(int actual, int expected) {
+        assertEquals(actual & StreamOpFlag.STREAM_MASK, expected);
+    }
+
+    public void testOpMask() {
+        assertOpMask(StreamOpFlag.DISTINCT.set(), StreamOpFlag.IS_DISTINCT);
+        assertOpMask(StreamOpFlag.DISTINCT.clear(), StreamOpFlag.NOT_DISTINCT);
+
+        assertOpMask(StreamOpFlag.SORTED.set(), StreamOpFlag.IS_SORTED);
+        assertOpMask(StreamOpFlag.SORTED.clear(), StreamOpFlag.NOT_SORTED);
+
+        assertOpMask(StreamOpFlag.ORDERED.set(), StreamOpFlag.IS_ORDERED);
+        assertOpMask(StreamOpFlag.ORDERED.clear(), StreamOpFlag.NOT_ORDERED);
+
+        assertOpMask(StreamOpFlag.SIZED.set(), 0);
+        assertOpMask(StreamOpFlag.SIZED.clear(), StreamOpFlag.NOT_SIZED);
+
+        assertOpMask(StreamOpFlag.SHORT_CIRCUIT.set(), StreamOpFlag.IS_SHORT_CIRCUIT);
+        assertOpMask(StreamOpFlag.SHORT_CIRCUIT.clear(), 0);
+    }
+
+    private void assertOpMask(int actual, int expected) {
+        assertEquals(actual & StreamOpFlag.OP_MASK, expected);
+    }
+
+    public void testTerminalOpMask() {
+        assertTerminalOpMask(StreamOpFlag.DISTINCT.set(), 0);
+        assertTerminalOpMask(StreamOpFlag.DISTINCT.clear(), 0);
+
+        assertTerminalOpMask(StreamOpFlag.SORTED.set(), 0);
+        assertTerminalOpMask(StreamOpFlag.SORTED.clear(), 0);
+
+        assertTerminalOpMask(StreamOpFlag.ORDERED.set(), 0);
+        assertTerminalOpMask(StreamOpFlag.ORDERED.clear(), StreamOpFlag.NOT_ORDERED);
+
+        assertTerminalOpMask(StreamOpFlag.SIZED.set(), 0);
+        assertTerminalOpMask(StreamOpFlag.SIZED.clear(), 0);
+
+        assertTerminalOpMask(StreamOpFlag.SHORT_CIRCUIT.set(), StreamOpFlag.IS_SHORT_CIRCUIT);
+        assertTerminalOpMask(StreamOpFlag.SHORT_CIRCUIT.clear(), 0);
+    }
+
+    private void assertTerminalOpMask(int actual, int expected) {
+        assertEquals(actual & StreamOpFlag.TERMINAL_OP_MASK, expected);
+    }
+
+    public void testUpstreamTerminalOpMask() {
+        assertUpstreamTerminalOpMask(StreamOpFlag.DISTINCT.set(), 0);
+        assertUpstreamTerminalOpMask(StreamOpFlag.DISTINCT.clear(), 0);
+
+        assertUpstreamTerminalOpMask(StreamOpFlag.SORTED.set(), 0);
+        assertUpstreamTerminalOpMask(StreamOpFlag.SORTED.clear(), 0);
+
+        assertUpstreamTerminalOpMask(StreamOpFlag.ORDERED.set(), 0);
+        assertUpstreamTerminalOpMask(StreamOpFlag.ORDERED.clear(), StreamOpFlag.NOT_ORDERED);
+
+        assertUpstreamTerminalOpMask(StreamOpFlag.SIZED.set(), 0);
+        assertUpstreamTerminalOpMask(StreamOpFlag.SIZED.clear(), 0);
+
+        assertUpstreamTerminalOpMask(StreamOpFlag.SHORT_CIRCUIT.set(), 0);
+        assertUpstreamTerminalOpMask(StreamOpFlag.SHORT_CIRCUIT.clear(), 0);
+    }
+
+    private void assertUpstreamTerminalOpMask(int actual, int expected) {
+        assertEquals(actual & StreamOpFlag.UPSTREAM_TERMINAL_OP_MASK, expected);
+    }
+
+    public void testSpliteratorCharacteristics() {
+        assertEquals(Spliterator.DISTINCT, StreamOpFlag.IS_DISTINCT);
+        assertEquals(Spliterator.SORTED, StreamOpFlag.IS_SORTED);
+        assertEquals(Spliterator.ORDERED, StreamOpFlag.IS_ORDERED);
+        assertEquals(Spliterator.SIZED, StreamOpFlag.IS_SIZED);
+
+        List<Integer> others = Arrays.asList(Spliterator.NONNULL, Spliterator.IMMUTABLE,
+                                             Spliterator.CONCURRENT, Spliterator.SUBSIZED);
+        for (int c : others) {
+            assertNotEquals(c, StreamOpFlag.IS_SHORT_CIRCUIT);
+        }
+    }
+
+    public void testSpliteratorCharacteristicsMask() {
+        assertSpliteratorCharacteristicsMask(StreamOpFlag.DISTINCT.set(), StreamOpFlag.IS_DISTINCT);
+        assertSpliteratorCharacteristicsMask(StreamOpFlag.DISTINCT.clear(), 0);
+
+        assertSpliteratorCharacteristicsMask(StreamOpFlag.SORTED.set(), StreamOpFlag.IS_SORTED);
+        assertSpliteratorCharacteristicsMask(StreamOpFlag.SORTED.clear(), 0);
+
+        assertSpliteratorCharacteristicsMask(StreamOpFlag.ORDERED.set(), StreamOpFlag.IS_ORDERED);
+        assertSpliteratorCharacteristicsMask(StreamOpFlag.ORDERED.clear(), 0);
+
+        assertSpliteratorCharacteristicsMask(StreamOpFlag.SIZED.set(), StreamOpFlag.IS_SIZED);
+        assertSpliteratorCharacteristicsMask(StreamOpFlag.SIZED.clear(), 0);
+
+        assertSpliteratorCharacteristicsMask(StreamOpFlag.SHORT_CIRCUIT.set(), 0);
+        assertSpliteratorCharacteristicsMask(StreamOpFlag.SHORT_CIRCUIT.clear(), 0);
+    }
+
+    private void assertSpliteratorCharacteristicsMask(int actual, int expected) {
+        assertEquals(StreamOpFlag.fromCharacteristics(actual), expected);
+    }
+
+    public void testSpliteratorSorted() {
+        class SortedEmptySpliterator implements Spliterator<Object> {
+            final Comparator<Object> c;
+
+            SortedEmptySpliterator(Comparator<Object> c) {
+                this.c = c;
+            }
+
+            @Override
+            public Spliterator<Object> trySplit() {
+                return null;
+            }
+
+            @Override
+            public boolean tryAdvance(Consumer<? super Object> action) {
+                return false;
+            }
+
+            @Override
+            public long estimateSize() {
+                return Long.MAX_VALUE;
+            }
+
+            @Override
+            public int characteristics() {
+                return Spliterator.SORTED;
+            }
+
+            @Override
+            public Comparator<? super Object> getComparator() {
+                return c;
+            }
+        };
+
+        {
+            int flags = StreamOpFlag.fromCharacteristics(new SortedEmptySpliterator(null));
+            assertEquals(flags, StreamOpFlag.IS_SORTED);
+        }
+
+        {
+            int flags = StreamOpFlag.fromCharacteristics(new SortedEmptySpliterator((a, b) -> 0));
+            assertEquals(flags, 0);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/util/stream/boottest/java.base/java/util/stream/StreamReuseTest.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,441 @@
+/*
+ * 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 java.util.stream;
+
+import org.testng.annotations.Test;
+
+import java.util.function.Function;
+
+import static org.testng.Assert.fail;
+
+/**
+ * StreamReuseTest
+ *
+ * @author Brian Goetz
+ */
+@Test
+public class StreamReuseTest {
+
+    private <T, U, E, S extends BaseStream<E, S>, D extends TestData<E, S>> void assertSecondFails(
+            D data,
+            Function<S, T> first,
+            Function<S, U> second,
+            Class<? extends Throwable> exception,
+            String text) {
+        S stream = data.stream();
+        T fr = first.apply(stream);
+        try {
+            U sr = second.apply(stream);
+            fail(text + " (seq)");
+        }
+        catch (Throwable e) {
+            if (exception.isAssignableFrom(e.getClass())) {
+                // Expected
+            }
+            else if (e instanceof Error)
+                throw (Error) e;
+            else if (e instanceof RuntimeException)
+                throw (RuntimeException) e;
+            else
+                throw new AssertionError("Unexpected exception " + e.getClass(), e);
+        }
+
+        stream = data.parallelStream();
+        fr = first.apply(stream);
+        try {
+            U sr = second.apply(stream);
+            fail(text + " (par)");
+        }
+        catch (Throwable e) {
+            if (exception.isAssignableFrom(e.getClass())) {
+                // Expected
+            }
+            else if (e instanceof Error)
+                throw (Error) e;
+            else if (e instanceof RuntimeException)
+                throw (RuntimeException) e;
+            else
+                throw new AssertionError("Unexpected exception " + e.getClass(), e);
+        }
+    }
+
+    // Stream
+
+    @Test(dataProvider = "StreamTestData<Integer>", dataProviderClass = StreamTestDataProvider.class)
+    public void testTwoStreams(String name, TestData<Integer, Stream<Integer>> data) {
+        assertSecondFails(data,
+                          (Stream<Integer> s) -> s.map(i -> i), (Stream<Integer> s) -> s.map(i -> i),
+                          IllegalStateException.class,
+                          "Stream map / map succeeded erroneously");
+        assertSecondFails(data,
+                          Stream::distinct, (Stream<Integer> s) -> s.map(i -> i),
+                          IllegalStateException.class,
+                          "Stream distinct / map succeeded erroneously");
+        assertSecondFails(data,
+                          (Stream<Integer> s) -> s.map(i -> i), Stream::distinct,
+                          IllegalStateException.class,
+                          "Stream map / distinct succeeded erroneously");
+        assertSecondFails(data,
+                          Stream::distinct, Stream::distinct,
+                          IllegalStateException.class,
+                          "Stream distinct / distinct succeeded erroneously");
+    }
+
+    @Test(dataProvider = "StreamTestData<Integer>", dataProviderClass = StreamTestDataProvider.class)
+    public void testTwoTerminals(String name, TestData<Integer, Stream<Integer>> data) {
+        assertSecondFails(data,
+                          Stream::findFirst, Stream::findFirst,
+                          IllegalStateException.class,
+                          "Stream findFirst / findFirst succeeded erroneously");
+    }
+
+    @Test(dataProvider = "StreamTestData<Integer>", dataProviderClass = StreamTestDataProvider.class)
+    public void testTerminalStream(String name, TestData<Integer, Stream<Integer>> data) {
+        assertSecondFails(data,
+                          Stream::findFirst, (Stream<Integer> s) -> s.map(i -> i),
+                          IllegalStateException.class,
+                          "Stream findFirst / map succeeded erroneously");
+        assertSecondFails(data,
+                          (Stream<Integer> s) -> s.map(i -> i), Stream::findFirst,
+                          IllegalStateException.class,
+                          "Stream map / findFirst succeeded erroneously");
+        assertSecondFails(data,
+                          Stream::findFirst, Stream::distinct,
+                          IllegalStateException.class,
+                          "Stream findFirst / distinct succeeded erroneously");
+        assertSecondFails(data,
+                          Stream::distinct, Stream::findFirst,
+                          IllegalStateException.class,
+                          "Stream distinct / findFirst succeeded erroneously");
+    }
+
+    @Test(dataProvider = "StreamTestData<Integer>", dataProviderClass = StreamTestDataProvider.class)
+    public void testTwoIterators(String name, TestData<Integer, Stream<Integer>> data) {
+        assertSecondFails(data,
+                          Stream::iterator, Stream::iterator,
+                          IllegalStateException.class,
+                          "Stream iterator / iterator succeeded erroneously");
+    }
+
+    @Test(dataProvider = "StreamTestData<Integer>", dataProviderClass = StreamTestDataProvider.class)
+    public void testTerminalIterator(String name, TestData<Integer, Stream<Integer>> data) {
+        assertSecondFails(data,
+                          Stream::iterator, Stream::findFirst,
+                          IllegalStateException.class,
+                          "Stream iterator / findFirst succeeded erroneously");
+        assertSecondFails(data,
+                          Stream::findFirst, Stream::iterator,
+                          IllegalStateException.class,
+                          "Stream findFirst / iterator succeeded erroneously");
+    }
+
+    @Test(dataProvider = "StreamTestData<Integer>", dataProviderClass = StreamTestDataProvider.class)
+    public void testStreamIterator(String name, TestData<Integer, Stream<Integer>> data) {
+        assertSecondFails(data,
+                          Stream::iterator, (Stream<Integer> s) -> s.map(i -> i),
+                          IllegalStateException.class,
+                          "Stream iterator / map succeeded erroneously");
+        assertSecondFails(data,
+                          (Stream<Integer> s) -> s.map(i -> i), Stream::iterator,
+                          IllegalStateException.class,
+                          "Stream map / iterator succeeded erroneously");
+        assertSecondFails(data,
+                          Stream::iterator, Stream::distinct,
+                          IllegalStateException.class,
+                          "Stream iterator / distinct succeeded erroneously");
+        assertSecondFails(data,
+                          Stream::distinct, Stream::iterator,
+                          IllegalStateException.class,
+                          "Stream distinct / iterator succeeded erroneously");
+    }
+
+    // IntStream
+
+    @Test(dataProvider = "IntStreamTestData", dataProviderClass = IntStreamTestDataProvider.class)
+    public void testTwoStreams(String name, TestData.OfInt data) {
+        assertSecondFails(data,
+                          (IntStream s) -> s.mapToObj(i -> i), (IntStream s) -> s.mapToObj(i -> i),
+                          IllegalStateException.class,
+                          "IntStream map / map succeeded erroneously");
+        assertSecondFails(data,
+                          IntStream::distinct, (IntStream s) -> s.mapToObj(i -> i),
+                          IllegalStateException.class,
+                          "IntStream distinct / map succeeded erroneously");
+        assertSecondFails(data,
+                          (IntStream s) -> s.mapToObj(i -> i), IntStream::distinct,
+                          IllegalStateException.class,
+                          "IntStream map / distinct succeeded erroneously");
+        assertSecondFails(data,
+                          IntStream::distinct, IntStream::distinct,
+                          IllegalStateException.class,
+                          "IntStream distinct / distinct succeeded erroneously");
+    }
+
+    @Test(dataProvider = "IntStreamTestData", dataProviderClass = IntStreamTestDataProvider.class)
+    public void testTwoTerminals(String name, TestData.OfInt data) {
+        assertSecondFails(data,
+                          IntStream::sum, IntStream::sum,
+                          IllegalStateException.class,
+                          "IntStream sum / sum succeeded erroneously");
+    }
+
+    @Test(dataProvider = "IntStreamTestData", dataProviderClass = IntStreamTestDataProvider.class)
+    public void testTerminalStream(String name, TestData.OfInt data) {
+        assertSecondFails(data,
+                          IntStream::sum, (IntStream s) -> s.mapToObj(i -> i),
+                          IllegalStateException.class,
+                          "IntStream sum / map succeeded erroneously");
+        assertSecondFails(data,
+                          (IntStream s) -> s.mapToObj(i -> i), IntStream::sum,
+                          IllegalStateException.class,
+                          "IntStream map / sum succeeded erroneously");
+        assertSecondFails(data,
+                          IntStream::sum, IntStream::distinct,
+                          IllegalStateException.class,
+                          "IntStream sum / distinct succeeded erroneously");
+        assertSecondFails(data,
+                          IntStream::distinct, IntStream::sum,
+                          IllegalStateException.class,
+                          "IntStream distinct / sum succeeded erroneously");
+    }
+
+    @Test(dataProvider = "IntStreamTestData", dataProviderClass = IntStreamTestDataProvider.class)
+    public void testTwoIterators(String name, TestData.OfInt data) {
+        assertSecondFails(data,
+                          IntStream::iterator, IntStream::iterator,
+                          IllegalStateException.class,
+                          "IntStream iterator / iterator succeeded erroneously");
+    }
+
+    @Test(dataProvider = "IntStreamTestData", dataProviderClass = IntStreamTestDataProvider.class)
+    public void testTerminalIterator(String name, TestData.OfInt data) {
+        assertSecondFails(data,
+                          IntStream::iterator, IntStream::sum,
+                          IllegalStateException.class,
+                          "IntStream iterator / sum succeeded erroneously");
+        assertSecondFails(data,
+                          IntStream::sum, IntStream::iterator,
+                          IllegalStateException.class,
+                          "Stream sum / iterator succeeded erroneously");
+    }
+
+    @Test(dataProvider = "IntStreamTestData", dataProviderClass = IntStreamTestDataProvider.class)
+    public void testStreamIterator(String name, TestData.OfInt data) {
+        assertSecondFails(data,
+                          IntStream::iterator, (IntStream s) -> s.mapToObj(i -> i),
+                          IllegalStateException.class,
+                          "IntStream iterator / map succeeded erroneously");
+        assertSecondFails(data,
+                          (IntStream s) -> s.mapToObj(i -> i), IntStream::iterator,
+                          IllegalStateException.class,
+                          "IntStream map / iterator succeeded erroneously");
+        assertSecondFails(data,
+                          IntStream::iterator, IntStream::distinct,
+                          IllegalStateException.class,
+                          "IntStream iterator / distinct succeeded erroneously");
+        assertSecondFails(data,
+                          IntStream::distinct, IntStream::iterator,
+                          IllegalStateException.class,
+                          "IntStream distinct / iterator succeeded erroneously");
+    }
+
+    // LongStream
+
+    @Test(dataProvider = "LongStreamTestData", dataProviderClass = LongStreamTestDataProvider.class)
+    public void testTwoStreams(String name, TestData.OfLong data) {
+        assertSecondFails(data,
+                          (LongStream s) -> s.mapToObj(i -> i), (LongStream s) -> s.mapToObj(i -> i),
+                          IllegalStateException.class,
+                          "LongStream map / map succeeded erroneously");
+        assertSecondFails(data,
+                          LongStream::distinct, (LongStream s) -> s.mapToObj(i -> i),
+                          IllegalStateException.class,
+                          "LongStream distinct / map succeeded erroneously");
+        assertSecondFails(data,
+                          (LongStream s) -> s.mapToObj(i -> i), LongStream::distinct,
+                          IllegalStateException.class,
+                          "LongStream map / distinct succeeded erroneously");
+        assertSecondFails(data,
+                          LongStream::distinct, LongStream::distinct,
+                          IllegalStateException.class,
+                          "LongStream distinct / distinct succeeded erroneously");
+    }
+
+    @Test(dataProvider = "LongStreamTestData", dataProviderClass = LongStreamTestDataProvider.class)
+    public void testTwoTerminals(String name, TestData.OfLong data) {
+        assertSecondFails(data,
+                          LongStream::sum, LongStream::sum,
+                          IllegalStateException.class,
+                          "LongStream sum / sum succeeded erroneously");
+    }
+
+    @Test(dataProvider = "LongStreamTestData", dataProviderClass = LongStreamTestDataProvider.class)
+    public void testTerminalStream(String name, TestData.OfLong data) {
+        assertSecondFails(data,
+                          LongStream::sum, (LongStream s) -> s.mapToObj(i -> i),
+                          IllegalStateException.class,
+                          "LongStream sum / map succeeded erroneously");
+        assertSecondFails(data,
+                          (LongStream s) -> s.mapToObj(i -> i), LongStream::sum,
+                          IllegalStateException.class,
+                          "LongStream map / sum succeeded erroneously");
+        assertSecondFails(data,
+                          LongStream::sum, LongStream::distinct,
+                          IllegalStateException.class,
+                          "LongStream sum / distinct succeeded erroneously");
+        assertSecondFails(data,
+                          LongStream::distinct, LongStream::sum,
+                          IllegalStateException.class,
+                          "LongStream distinct / sum succeeded erroneously");
+    }
+
+    @Test(dataProvider = "LongStreamTestData", dataProviderClass = LongStreamTestDataProvider.class)
+    public void testTwoIterators(String name, TestData.OfLong data) {
+        assertSecondFails(data,
+                          LongStream::iterator, LongStream::iterator,
+                          IllegalStateException.class,
+                          "LongStream iterator / iterator succeeded erroneously");
+    }
+
+    @Test(dataProvider = "LongStreamTestData", dataProviderClass = LongStreamTestDataProvider.class)
+    public void testTerminalIterator(String name, TestData.OfLong data) {
+        assertSecondFails(data,
+                          LongStream::iterator, LongStream::sum,
+                          IllegalStateException.class,
+                          "LongStream iterator / sum succeeded erroneously");
+        assertSecondFails(data,
+                          LongStream::sum, LongStream::iterator,
+                          IllegalStateException.class,
+                          "Stream sum / iterator succeeded erroneously");
+    }
+
+    @Test(dataProvider = "LongStreamTestData", dataProviderClass = LongStreamTestDataProvider.class)
+    public void testStreamIterator(String name, TestData.OfLong data) {
+        assertSecondFails(data,
+                          LongStream::iterator, (LongStream s) -> s.mapToObj(i -> i),
+                          IllegalStateException.class,
+                          "LongStream iterator / map succeeded erroneously");
+        assertSecondFails(data,
+                          (LongStream s) -> s.mapToObj(i -> i), LongStream::iterator,
+                          IllegalStateException.class,
+                          "LongStream map / iterator succeeded erroneously");
+        assertSecondFails(data,
+                          LongStream::iterator, LongStream::distinct,
+                          IllegalStateException.class,
+                          "LongStream iterator / distinct succeeded erroneously");
+        assertSecondFails(data,
+                          LongStream::distinct, LongStream::iterator,
+                          IllegalStateException.class,
+                          "LongStream distinct / iterator succeeded erroneously");
+    }
+
+    // DoubleStream
+
+    @Test(dataProvider = "DoubleStreamTestData", dataProviderClass = DoubleStreamTestDataProvider.class)
+    public void testTwoStreams(String name, TestData.OfDouble data) {
+        assertSecondFails(data,
+                          (DoubleStream s) -> s.mapToObj(i -> i), (DoubleStream s) -> s.mapToObj(i -> i),
+                          IllegalStateException.class,
+                          "DoubleStream map / map succeeded erroneously");
+        assertSecondFails(data,
+                          DoubleStream::distinct, (DoubleStream s) -> s.mapToObj(i -> i),
+                          IllegalStateException.class,
+                          "DoubleStream distinct / map succeeded erroneously");
+        assertSecondFails(data,
+                          (DoubleStream s) -> s.mapToObj(i -> i), DoubleStream::distinct,
+                          IllegalStateException.class,
+                          "DoubleStream map / distinct succeeded erroneously");
+        assertSecondFails(data,
+                          DoubleStream::distinct, DoubleStream::distinct,
+                          IllegalStateException.class,
+                          "DoubleStream distinct / distinct succeeded erroneously");
+    }
+
+    @Test(dataProvider = "DoubleStreamTestData", dataProviderClass = DoubleStreamTestDataProvider.class)
+    public void testTwoTerminals(String name, TestData.OfDouble data) {
+        assertSecondFails(data,
+                          DoubleStream::sum, DoubleStream::sum,
+                          IllegalStateException.class,
+                          "DoubleStream sum / sum succeeded erroneously");
+    }
+
+    @Test(dataProvider = "DoubleStreamTestData", dataProviderClass = DoubleStreamTestDataProvider.class)
+    public void testTerminalStream(String name, TestData.OfDouble data) {
+        assertSecondFails(data,
+                          DoubleStream::sum, (DoubleStream s) -> s.mapToObj(i -> i),
+                          IllegalStateException.class,
+                          "DoubleStream sum / map succeeded erroneously");
+        assertSecondFails(data,
+                          (DoubleStream s) -> s.mapToObj(i -> i), DoubleStream::sum,
+                          IllegalStateException.class,
+                          "DoubleStream map / sum succeeded erroneously");
+        assertSecondFails(data,
+                          DoubleStream::sum, DoubleStream::distinct,
+                          IllegalStateException.class,
+                          "DoubleStream sum / distinct succeeded erroneously");
+        assertSecondFails(data,
+                          DoubleStream::distinct, DoubleStream::sum,
+                          IllegalStateException.class,
+                          "DoubleStream distinct / sum succeeded erroneously");
+    }
+
+    @Test(dataProvider = "DoubleStreamTestData", dataProviderClass = DoubleStreamTestDataProvider.class)
+    public void testTwoIterators(String name, TestData.OfDouble data) {
+        assertSecondFails(data,
+                          DoubleStream::iterator, DoubleStream::iterator,
+                          IllegalStateException.class,
+                          "DoubleStream iterator / iterator succeeded erroneously");
+    }
+
+    @Test(dataProvider = "DoubleStreamTestData", dataProviderClass = DoubleStreamTestDataProvider.class)
+    public void testTerminalIterator(String name, TestData.OfDouble data) {
+        assertSecondFails(data,
+                          DoubleStream::iterator, DoubleStream::sum,
+                          IllegalStateException.class,
+                          "DoubleStream iterator / sum succeeded erroneously");
+        assertSecondFails(data,
+                          DoubleStream::sum, DoubleStream::iterator,
+                          IllegalStateException.class,
+                          "Stream sum / iterator succeeded erroneously");
+    }
+
+    @Test(dataProvider = "DoubleStreamTestData", dataProviderClass = DoubleStreamTestDataProvider.class)
+    public void testStreamIterator(String name, TestData.OfDouble data) {
+        assertSecondFails(data,
+                          DoubleStream::iterator, (DoubleStream s) -> s.mapToObj(i -> i),
+                          IllegalStateException.class,
+                          "DoubleStream iterator / map succeeded erroneously");
+        assertSecondFails(data,
+                          (DoubleStream s) -> s.mapToObj(i -> i), DoubleStream::iterator,
+                          IllegalStateException.class,
+                          "DoubleStream map / iterator succeeded erroneously");
+        assertSecondFails(data,
+                          DoubleStream::iterator, DoubleStream::distinct,
+                          IllegalStateException.class,
+                          "DoubleStream iterator / distinct succeeded erroneously");
+        assertSecondFails(data,
+                          DoubleStream::distinct, DoubleStream::iterator,
+                          IllegalStateException.class,
+                          "DoubleStream distinct / iterator succeeded erroneously");
+    }
+}
--- a/jdk/test/java/util/stream/boottest/java/util/stream/DoubleNodeTest.java	Thu Nov 19 18:18:05 2015 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,179 +0,0 @@
-/*
- * Copyright (c) 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 java.util.stream;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.PrimitiveIterator;
-import java.util.Spliterators;
-import java.util.function.Function;
-
-import org.testng.annotations.DataProvider;
-import org.testng.annotations.Test;
-
-@Test
-public class DoubleNodeTest extends OpTestCase {
-
-    @DataProvider(name = "nodes")
-    public Object[][] createSizes() {
-        List<Object[]> params = new ArrayList<>();
-
-        for (int size : Arrays.asList(0, 1, 4, 15, 16, 17, 127, 128, 129, 1000)) {
-            double[] array = new double[size];
-            for (int i = 0; i < array.length; i++) {
-                array[i] = i;
-            }
-
-            List<Node<Double>> nodes = new ArrayList<>();
-
-            nodes.add(Nodes.node(array));
-            nodes.add(degenerateTree(Spliterators.iterator(Arrays.spliterator(array))));
-            nodes.add(tree(toList(array), l -> Nodes.node(toDoubleArray(l))));
-            nodes.add(fill(array, Nodes.doubleBuilder(array.length)));
-            nodes.add(fill(array, Nodes.doubleBuilder()));
-
-            for (Node<Double> node : nodes) {
-                params.add(new Object[]{array, node});
-            }
-
-        }
-
-        return params.toArray(new Object[0][]);
-    }
-
-    private static void assertEqualsListDoubleArray(List<Double> list, double[] array) {
-        assertEquals(list.size(), array.length);
-        for (int i = 0; i < array.length; i++)
-            assertEquals(array[i], list.get(i));
-    }
-
-    private List<Double> toList(double[] a) {
-        List<Double> l = new ArrayList<>();
-        for (double i : a) {
-            l.add(i);
-        }
-
-        return l;
-    }
-
-    private double[] toDoubleArray(List<Double> l) {
-        double[] a = new double[l.size()];
-
-        int i = 0;
-        for (Double e : l) {
-            a[i++] = e;
-        }
-        return a;
-    }
-
-    private Node.OfDouble fill(double[] array, Node.Builder.OfDouble nb) {
-        nb.begin(array.length);
-        for (double i : array)
-            nb.accept(i);
-        nb.end();
-        return nb.build();
-    }
-
-    private Node.OfDouble degenerateTree(PrimitiveIterator.OfDouble it) {
-        if (!it.hasNext()) {
-            return Nodes.node(new double[0]);
-        }
-
-        double i = it.nextDouble();
-        if (it.hasNext()) {
-            return new Nodes.ConcNode.OfDouble(Nodes.node(new double[] {i}), degenerateTree(it));
-        }
-        else {
-            return Nodes.node(new double[] {i});
-        }
-    }
-
-    private Node.OfDouble tree(List<Double> l, Function<List<Double>, Node.OfDouble> m) {
-        if (l.size() < 3) {
-            return m.apply(l);
-        }
-        else {
-            return new Nodes.ConcNode.OfDouble(
-                    tree(l.subList(0, l.size() / 2), m),
-                    tree(l.subList(l.size() / 2, l.size()), m));
-        }
-    }
-
-    @Test(dataProvider = "nodes")
-    public void testAsArray(double[] array, Node.OfDouble n) {
-        assertEquals(n.asPrimitiveArray(), array);
-    }
-
-    @Test(dataProvider = "nodes")
-    public void testFlattenAsArray(double[] array, Node.OfDouble n) {
-        assertEquals(Nodes.flattenDouble(n).asPrimitiveArray(), array);
-    }
-
-    @Test(dataProvider = "nodes")
-    public void testCopyTo(double[] array, Node.OfDouble n) {
-        double[] copy = new double[(int) n.count()];
-        n.copyInto(copy, 0);
-
-        assertEquals(copy, array);
-    }
-
-    @Test(dataProvider = "nodes", groups = { "serialization-hostile" })
-    public void testForEach(double[] array, Node.OfDouble n) {
-        List<Double> l = new ArrayList<>((int) n.count());
-        n.forEach((double e) -> {
-            l.add(e);
-        });
-
-        assertEqualsListDoubleArray(l, array);
-    }
-
-    @Test(dataProvider = "nodes")
-    public void testStreams(double[] array, Node.OfDouble n) {
-        TestData.OfDouble data = TestData.Factory.ofNode("Node", n);
-
-        exerciseOps(data, s -> s);
-
-        exerciseTerminalOps(data, s -> s.toArray());
-    }
-
-    @Test(dataProvider = "nodes", groups={ "serialization-hostile" })
-    // throws SOE on serialization of DoubleConcNode[size=1000]
-    public void testSpliterator(double[] array, Node.OfDouble n) {
-        SpliteratorTestHelper.testDoubleSpliterator(n::spliterator);
-    }
-
-    @Test(dataProvider = "nodes")
-    public void testTruncate(double[] array, Node.OfDouble n) {
-        int[] nums = new int[] { 0, 1, array.length / 2, array.length - 1, array.length };
-        for (int start : nums)
-            for (int end : nums) {
-                if (start < 0 || end < 0 || end < start || end > array.length)
-                    continue;
-                Node.OfDouble slice = n.truncate(start, end, Double[]::new);
-                double[] asArray = slice.asPrimitiveArray();
-                for (int k = start; k < end; k++)
-                    assertEquals(array[k], asArray[k - start]);
-            }
-    }
-}
--- a/jdk/test/java/util/stream/boottest/java/util/stream/FlagOpTest.java	Thu Nov 19 18:18:05 2015 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,283 +0,0 @@
-/*
- * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package java.util.stream;
-
-import org.testng.Assert;
-import org.testng.annotations.Test;
-
-import java.util.ArrayList;
-import java.util.EnumSet;
-import java.util.List;
-import java.util.function.Supplier;
-
-import static java.util.stream.LambdaTestHelpers.countTo;
-
-@Test
-public class FlagOpTest extends OpTestCase {
-
-    @Test(dataProvider = "StreamTestData<Integer>", dataProviderClass = StreamTestDataProvider.class)
-    public void testFlagsPassThrough(String name, TestData<Integer, Stream<Integer>> data) {
-
-        @SuppressWarnings({"unchecked", "rawtypes"})
-        TestFlagPassThroughOp<Integer>[] ops = new TestFlagPassThroughOp[3];
-        ops[0] = new TestFlagPassThroughOp<>();
-        ops[1] = new TestFlagPassThroughOp<>();
-        ops[2] = new TestFlagPassThroughOp<>();
-
-        ops[0].set(null, ops[1]);
-        ops[1].set(ops[0], ops[2]);
-        ops[2].set(ops[1], null);
-
-        withData(data).ops(ops).exercise();
-    }
-
-    static class TestFlagPassThroughOp<T> extends FlagDeclaringOp<T> {
-        TestFlagPassThroughOp<T> upstream;
-        TestFlagPassThroughOp<T> downstream;
-
-        TestFlagPassThroughOp() {
-            super(0);
-        }
-
-        void set(TestFlagPassThroughOp<T> upstream, TestFlagPassThroughOp<T> downstream)  {
-            this.upstream = upstream;
-            this.downstream = downstream;
-        }
-
-        int wrapFlags;
-
-        @Override
-        @SuppressWarnings({"unchecked", "rawtypes"})
-        public Sink<T> opWrapSink(int flags, boolean parallel, Sink sink) {
-            this.wrapFlags = flags;
-
-            if (downstream != null) {
-                assertTrue(flags == downstream.wrapFlags);
-            }
-
-            return sink;
-        }
-    }
-
-    public void testFlagsClearAllSet() {
-        int clearAllFlags = 0;
-        for (StreamOpFlag f : EnumSet.allOf(StreamOpFlag.class)) {
-            if (f.isStreamFlag()) {
-                clearAllFlags |= f.clear();
-            }
-        }
-
-        EnumSet<StreamOpFlag> known = EnumSet.noneOf(StreamOpFlag.class);
-        EnumSet<StreamOpFlag> notKnown = StreamOpFlagTestHelper.allStreamFlags();
-
-        List<FlagDeclaringOp<Integer>> ops = new ArrayList<>();
-        ops.add(new FlagDeclaringOp<>(clearAllFlags));
-        for (StreamOpFlag f : StreamOpFlagTestHelper.allStreamFlags()) {
-            if (f.canSet(StreamOpFlag.Type.OP)) {
-                ops.add(new TestFlagExpectedOp<>(f.set(),
-                                             known.clone(),
-                                             EnumSet.noneOf(StreamOpFlag.class),
-                                             notKnown.clone()));
-                known.add(f);
-                notKnown.remove(f);
-            }
-        }
-        ops.add(new TestFlagExpectedOp<>(0,
-                                         known.clone(),
-                                         EnumSet.noneOf(StreamOpFlag.class),
-                                         notKnown.clone()));
-
-        TestData<Integer, Stream<Integer>> data = TestData.Factory.ofArray("Array", countTo(10).toArray(new Integer[0]));
-        @SuppressWarnings("rawtypes")
-        FlagDeclaringOp[] opsArray = ops.toArray(new FlagDeclaringOp[ops.size()]);
-
-        withData(data).ops(opsArray).
-                without(StreamTestScenario.CLEAR_SIZED_SCENARIOS).
-                exercise();
-    }
-
-    public void testFlagsSetAllClear() {
-        EnumSet<StreamOpFlag> known = StreamOpFlagTestHelper.allStreamFlags();
-        int setAllFlags = 0;
-        for (StreamOpFlag f : EnumSet.allOf(StreamOpFlag.class)) {
-            if (f.isStreamFlag()) {
-                if (f.canSet(StreamOpFlag.Type.OP)) {
-                    setAllFlags |= f.set();
-                } else {
-                    known.remove(f);
-                }
-            }
-        }
-
-        EnumSet<StreamOpFlag> notKnown = EnumSet.noneOf(StreamOpFlag.class);
-
-        List<FlagDeclaringOp<Integer>> ops = new ArrayList<>();
-        ops.add(new FlagDeclaringOp<>(setAllFlags));
-        for (StreamOpFlag f : StreamOpFlagTestHelper.allStreamFlags()) {
-            ops.add(new TestFlagExpectedOp<>(f.clear(),
-                                             known.clone(),
-                                             EnumSet.noneOf(StreamOpFlag.class),
-                                             notKnown.clone()));
-            known.remove(f);
-            notKnown.add(f);
-        }
-        ops.add(new TestFlagExpectedOp<>(0,
-                                         known.clone(),
-                                         EnumSet.noneOf(StreamOpFlag.class),
-                                         notKnown.clone()));
-
-        TestData<Integer, Stream<Integer>> data = TestData.Factory.ofArray("Array", countTo(10).toArray(new Integer[0]));
-        @SuppressWarnings("rawtypes")
-        FlagDeclaringOp[] opsArray = ops.toArray(new FlagDeclaringOp[ops.size()]);
-
-
-        withData(data).ops(opsArray).
-                without(StreamTestScenario.CLEAR_SIZED_SCENARIOS).
-                exercise();
-    }
-
-    public void testFlagsParallelCollect() {
-        testFlagsSetSequence(CollectorOps::collector);
-    }
-
-    private void testFlagsSetSequence(Supplier<StatefulTestOp<Integer>> cf) {
-        EnumSet<StreamOpFlag> known = EnumSet.of(StreamOpFlag.ORDERED, StreamOpFlag.SIZED);
-        EnumSet<StreamOpFlag> preserve = EnumSet.of(StreamOpFlag.DISTINCT, StreamOpFlag.SORTED);
-
-        List<IntermediateTestOp<Integer, Integer>> ops = new ArrayList<>();
-        for (StreamOpFlag f : EnumSet.of(StreamOpFlag.DISTINCT, StreamOpFlag.SORTED)) {
-            ops.add(cf.get());
-            ops.add(new TestFlagExpectedOp<>(f.set(),
-                                             known.clone(),
-                                             preserve.clone(),
-                                             EnumSet.noneOf(StreamOpFlag.class)));
-            known.add(f);
-            preserve.remove(f);
-        }
-        ops.add(cf.get());
-        ops.add(new TestFlagExpectedOp<>(0,
-                                         known.clone(),
-                                         preserve.clone(),
-                                         EnumSet.noneOf(StreamOpFlag.class)));
-
-        TestData<Integer, Stream<Integer>> data = TestData.Factory.ofArray("Array", countTo(10).toArray(new Integer[0]));
-        @SuppressWarnings("rawtypes")
-        IntermediateTestOp[] opsArray = ops.toArray(new IntermediateTestOp[ops.size()]);
-
-        withData(data).ops(opsArray).
-                without(StreamTestScenario.CLEAR_SIZED_SCENARIOS).
-                exercise();
-    }
-
-
-    public void testFlagsClearParallelCollect() {
-        testFlagsClearSequence(CollectorOps::collector);
-    }
-
-    protected void testFlagsClearSequence(Supplier<StatefulTestOp<Integer>> cf) {
-        EnumSet<StreamOpFlag> known = EnumSet.of(StreamOpFlag.ORDERED, StreamOpFlag.SIZED);
-        EnumSet<StreamOpFlag> preserve = EnumSet.of(StreamOpFlag.DISTINCT, StreamOpFlag.SORTED);
-        EnumSet<StreamOpFlag> notKnown = EnumSet.noneOf(StreamOpFlag.class);
-
-        List<IntermediateTestOp<Integer, Integer>> ops = new ArrayList<>();
-        for (StreamOpFlag f : EnumSet.of(StreamOpFlag.ORDERED, StreamOpFlag.DISTINCT, StreamOpFlag.SORTED)) {
-            ops.add(cf.get());
-            ops.add(new TestFlagExpectedOp<>(f.clear(),
-                                             known.clone(),
-                                             preserve.clone(),
-                                             notKnown.clone()));
-            known.remove(f);
-            preserve.remove(f);
-            notKnown.add(f);
-        }
-        ops.add(cf.get());
-        ops.add(new TestFlagExpectedOp<>(0,
-                                         known.clone(),
-                                         preserve.clone(),
-                                         notKnown.clone()));
-
-        TestData<Integer, Stream<Integer>> data = TestData.Factory.ofArray("Array", countTo(10).toArray(new Integer[0]));
-        @SuppressWarnings("rawtypes")
-        IntermediateTestOp[] opsArray = ops.toArray(new IntermediateTestOp[ops.size()]);
-
-        withData(data).ops(opsArray).
-                without(StreamTestScenario.CLEAR_SIZED_SCENARIOS).
-                exercise();
-    }
-
-    public void testFlagsSizedOrderedParallelCollect() {
-        EnumSet<StreamOpFlag> parKnown = EnumSet.of(StreamOpFlag.SIZED);
-        EnumSet<StreamOpFlag> serKnown = parKnown.clone();
-
-        List<IntermediateTestOp<Integer, Integer>> ops = new ArrayList<>();
-        for (StreamOpFlag f : parKnown) {
-            ops.add(CollectorOps.collector());
-            ops.add(new ParSerTestFlagExpectedOp<>(f.clear(),
-                                             parKnown,
-                                             serKnown));
-            serKnown.remove(f);
-        }
-        ops.add(CollectorOps.collector());
-        ops.add(new ParSerTestFlagExpectedOp<>(0,
-                                         parKnown,
-                                         EnumSet.noneOf(StreamOpFlag.class)));
-
-        TestData<Integer, Stream<Integer>> data = TestData.Factory.ofArray("Array", countTo(10).toArray(new Integer[0]));
-        @SuppressWarnings("rawtypes")
-        IntermediateTestOp[] opsArray = ops.toArray(new IntermediateTestOp[ops.size()]);
-
-        withData(data).ops(opsArray).exercise();
-    }
-
-    static class ParSerTestFlagExpectedOp<T> extends FlagDeclaringOp<T> {
-        final EnumSet<StreamOpFlag> parKnown;
-        final EnumSet<StreamOpFlag> serKnown;
-
-        ParSerTestFlagExpectedOp(int flags, EnumSet<StreamOpFlag> known, EnumSet<StreamOpFlag> serKnown) {
-            super(flags);
-            this.parKnown = known;
-            this.serKnown = serKnown;
-        }
-
-        @Override
-        @SuppressWarnings({"unchecked", "rawtypes"})
-        public Sink<T> opWrapSink(int flags, boolean parallel, Sink upstream) {
-            assertFlags(flags, parallel);
-            return upstream;
-        }
-
-        protected void assertFlags(int flags, boolean parallel) {
-            if (parallel) {
-                for (StreamOpFlag f : parKnown) {
-                    Assert.assertTrue(f.isKnown(flags), String.format("Flag %s is not known, but should be known.", f.toString()));
-                }
-
-            } else {
-                for (StreamOpFlag f : serKnown) {
-                    Assert.assertTrue(f.isKnown(flags), String.format("Flag %s is not known, but should be known.", f.toString()));
-                }
-
-            }
-        }
-    }
-}
--- a/jdk/test/java/util/stream/boottest/java/util/stream/IntNodeTest.java	Thu Nov 19 18:18:05 2015 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,177 +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 java.util.stream;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.PrimitiveIterator;
-import java.util.Spliterators;
-import java.util.function.Function;
-
-import org.testng.annotations.DataProvider;
-import org.testng.annotations.Test;
-
-@Test
-public class IntNodeTest extends OpTestCase {
-
-    @DataProvider(name = "nodes")
-    public Object[][] createSizes() {
-        List<Object[]> params = new ArrayList<>();
-
-        for (int size : Arrays.asList(0, 1, 4, 15, 16, 17, 127, 128, 129, 1000)) {
-            int[] array = new int[size];
-            for (int i = 0; i < array.length; i++) {
-                array[i] = i;
-            }
-
-            List<Node<Integer>> nodes = new ArrayList<>();
-
-            nodes.add(Nodes.node(array));
-            nodes.add(degenerateTree(Spliterators.iterator(Arrays.spliterator(array))));
-            nodes.add(tree(toList(array), l -> Nodes.node(toIntArray(l))));
-            nodes.add(fill(array, Nodes.intBuilder(array.length)));
-            nodes.add(fill(array, Nodes.intBuilder()));
-
-            for (Node<Integer> node : nodes) {
-                params.add(new Object[]{array, node});
-            }
-
-        }
-
-        return params.toArray(new Object[0][]);
-    }
-
-    private static void assertEqualsListIntArray(List<Integer> list, int[] array) {
-        assertEquals(list.size(), array.length);
-        for (int i = 0; i < array.length; i++)
-            assertEquals(array[i], (int) list.get(i));
-    }
-
-    private List<Integer> toList(int[] a) {
-        List<Integer> l = new ArrayList<>();
-        for (int i : a) {
-            l.add(i);
-        }
-
-        return l;
-    }
-
-    private int[] toIntArray(List<Integer> l) {
-        int[] a = new int[l.size()];
-
-        int i = 0;
-        for (Integer e : l) {
-            a[i++] = e;
-        }
-        return a;
-    }
-
-    private Node.OfInt fill(int[] array, Node.Builder.OfInt nb) {
-        nb.begin(array.length);
-        for (int i : array)
-            nb.accept(i);
-        nb.end();
-        return nb.build();
-    }
-
-    private Node.OfInt degenerateTree(PrimitiveIterator.OfInt it) {
-        if (!it.hasNext()) {
-            return Nodes.node(new int[0]);
-        }
-
-        int i = it.nextInt();
-        if (it.hasNext()) {
-            return new Nodes.ConcNode.OfInt(Nodes.node(new int[] {i}), degenerateTree(it));
-        }
-        else {
-            return Nodes.node(new int[] {i});
-        }
-    }
-
-    private Node.OfInt tree(List<Integer> l, Function<List<Integer>, Node.OfInt> m) {
-        if (l.size() < 3) {
-            return m.apply(l);
-        }
-        else {
-            return new Nodes.ConcNode.OfInt(
-                    tree(l.subList(0, l.size() / 2), m),
-                    tree(l.subList(l.size() / 2, l.size()), m));
-        }
-    }
-
-    @Test(dataProvider = "nodes")
-    public void testAsArray(int[] array, Node.OfInt n) {
-        assertEquals(n.asPrimitiveArray(), array);
-    }
-
-    @Test(dataProvider = "nodes")
-    public void testFlattenAsArray(int[] array, Node.OfInt n) {
-        assertEquals(Nodes.flattenInt(n).asPrimitiveArray(), array);
-    }
-
-    @Test(dataProvider = "nodes")
-    public void testCopyTo(int[] array, Node.OfInt n) {
-        int[] copy = new int[(int) n.count()];
-        n.copyInto(copy, 0);
-
-        assertEquals(copy, array);
-    }
-
-    @Test(dataProvider = "nodes", groups = { "serialization-hostile" })
-    public void testForEach(int[] array, Node.OfInt n) {
-        List<Integer> l = new ArrayList<>((int) n.count());
-        n.forEach((int e) -> {
-            l.add(e);
-        });
-
-        assertEqualsListIntArray(l, array);
-    }
-
-    @Test(dataProvider = "nodes")
-    public void testStreams(int[] array, Node.OfInt n) {
-        TestData.OfInt data = TestData.Factory.ofNode("Node", n);
-
-        exerciseOps(data, s -> s);
-        exerciseTerminalOps(data, s -> s.toArray());
-    }
-
-    @Test(dataProvider = "nodes")
-    public void testSpliterator(int[] array, Node.OfInt n) {
-        SpliteratorTestHelper.testIntSpliterator(n::spliterator);
-    }
-
-    @Test(dataProvider = "nodes")
-    public void testTruncate(int[] array, Node.OfInt n) {
-        int[] nums = new int[] { 0, 1, array.length / 2, array.length - 1, array.length };
-        for (int start : nums)
-            for (int end : nums) {
-                if (start < 0 || end < 0 || end < start || end > array.length)
-                    continue;
-                Node.OfInt slice = n.truncate(start, end, Integer[]::new);
-                int[] asArray = slice.asPrimitiveArray();
-                for (int k = start; k < end; k++)
-                    assertEquals(array[k], asArray[k - start]);
-            }
-    }
-}
--- a/jdk/test/java/util/stream/boottest/java/util/stream/LongNodeTest.java	Thu Nov 19 18:18:05 2015 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,178 +0,0 @@
-/*
- * Copyright (c) 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 java.util.stream;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.PrimitiveIterator;
-import java.util.Spliterators;
-import java.util.function.Function;
-
-import org.testng.annotations.DataProvider;
-import org.testng.annotations.Test;
-
-@Test
-public class LongNodeTest extends OpTestCase {
-
-    @DataProvider(name = "nodes")
-    public Object[][] createSizes() {
-        List<Object[]> params = new ArrayList<>();
-
-        for (int size : Arrays.asList(0, 1, 4, 15, 16, 17, 127, 128, 129, 1000)) {
-            long[] array = new long[size];
-            for (int i = 0; i < array.length; i++) {
-                array[i] = i;
-            }
-
-            List<Node<Long>> nodes = new ArrayList<>();
-
-            nodes.add(Nodes.node(array));
-            nodes.add(degenerateTree(Spliterators.iterator(Arrays.spliterator(array))));
-            nodes.add(tree(toList(array), l -> Nodes.node(toLongArray(l))));
-            nodes.add(fill(array, Nodes.longBuilder(array.length)));
-            nodes.add(fill(array, Nodes.longBuilder()));
-
-            for (Node<Long> node : nodes) {
-                params.add(new Object[]{array, node});
-            }
-
-        }
-
-        return params.toArray(new Object[0][]);
-    }
-
-    private static void assertEqualsListLongArray(List<Long> list, long[] array) {
-        assertEquals(list.size(), array.length);
-        for (int i = 0; i < array.length; i++)
-            assertEquals(array[i], (long) list.get(i));
-    }
-
-    private List<Long> toList(long[] a) {
-        List<Long> l = new ArrayList<>();
-        for (long i : a) {
-            l.add(i);
-        }
-
-        return l;
-    }
-
-    private long[] toLongArray(List<Long> l) {
-        long[] a = new long[l.size()];
-
-        int i = 0;
-        for (Long e : l) {
-            a[i++] = e;
-        }
-        return a;
-    }
-
-    private Node.OfLong fill(long[] array, Node.Builder.OfLong nb) {
-        nb.begin(array.length);
-        for (long i : array)
-            nb.accept(i);
-        nb.end();
-        return nb.build();
-    }
-
-    private Node.OfLong degenerateTree(PrimitiveIterator.OfLong it) {
-        if (!it.hasNext()) {
-            return Nodes.node(new long[0]);
-        }
-
-        long i = it.nextLong();
-        if (it.hasNext()) {
-            return new Nodes.ConcNode.OfLong(Nodes.node(new long[] {i}), degenerateTree(it));
-        }
-        else {
-            return Nodes.node(new long[] {i});
-        }
-    }
-
-    private Node.OfLong tree(List<Long> l, Function<List<Long>, Node.OfLong> m) {
-        if (l.size() < 3) {
-            return m.apply(l);
-        }
-        else {
-            return new Nodes.ConcNode.OfLong(
-                    tree(l.subList(0, l.size() / 2), m),
-                    tree(l.subList(l.size() / 2, l.size()), m));
-        }
-    }
-
-    @Test(dataProvider = "nodes")
-    public void testAsArray(long[] array, Node.OfLong n) {
-        assertEquals(n.asPrimitiveArray(), array);
-    }
-
-    @Test(dataProvider = "nodes")
-    public void testFlattenAsArray(long[] array, Node.OfLong n) {
-        assertEquals(Nodes.flattenLong(n).asPrimitiveArray(), array);
-    }
-
-    @Test(dataProvider = "nodes")
-    public void testCopyTo(long[] array, Node.OfLong n) {
-        long[] copy = new long[(int) n.count()];
-        n.copyInto(copy, 0);
-
-        assertEquals(copy, array);
-    }
-
-    @Test(dataProvider = "nodes", groups = { "serialization-hostile" })
-    public void testForEach(long[] array, Node.OfLong n) {
-        List<Long> l = new ArrayList<>((int) n.count());
-        n.forEach((long e) -> {
-            l.add(e);
-        });
-
-        assertEqualsListLongArray(l, array);
-    }
-
-    @Test(dataProvider = "nodes")
-    public void testStreams(long[] array, Node.OfLong n) {
-        TestData.OfLong data = TestData.Factory.ofNode("Node", n);
-
-        exerciseOps(data, s -> s);
-
-        exerciseTerminalOps(data, s -> s.toArray());
-    }
-
-    @Test(dataProvider = "nodes")
-    public void testSpliterator(long[] array, Node.OfLong n) {
-        SpliteratorTestHelper.testLongSpliterator(n::spliterator);
-    }
-
-    @Test(dataProvider = "nodes")
-    public void testTruncate(long[] array, Node.OfLong n) {
-        int[] nums = new int[] { 0, 1, array.length / 2, array.length - 1, array.length };
-        for (int start : nums)
-            for (int end : nums) {
-                if (start < 0 || end < 0 || end < start || end > array.length)
-                    continue;
-                Node.OfLong slice = n.truncate(start, end, Long[]::new);
-                long[] asArray = slice.asPrimitiveArray();
-                for (int k = start; k < end; k++)
-                    assertEquals(array[k], asArray[k - start]);
-            }
-    }
-}
--- a/jdk/test/java/util/stream/boottest/java/util/stream/NodeBuilderTest.java	Thu Nov 19 18:18:05 2015 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,236 +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 java.util.stream;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.function.DoubleConsumer;
-import java.util.function.Function;
-import java.util.function.IntConsumer;
-import java.util.function.LongConsumer;
-
-import org.testng.annotations.DataProvider;
-import org.testng.annotations.Test;
-
-import static java.util.stream.LambdaTestHelpers.assertContents;
-import static java.util.stream.LambdaTestHelpers.countTo;
-import static org.testng.Assert.assertEquals;
-
-@Test
-public class NodeBuilderTest {
-
-    List<Integer> sizes = Arrays.asList(0, 1, 4, 16, 256,
-                                        1023, 1024, 1025,
-                                        2047, 2048, 2049,
-                                        1024 * 32 - 1, 1024 * 32, 1024 * 32 + 1);
-
-    @DataProvider(name = "Node.Builder")
-    public Object[][] createNodeBuilders() {
-        List<List<Integer>> ls = new ArrayList<>();
-        for (int size : sizes) {
-            ls.add(countTo(size));
-        }
-
-        List<Function<Integer, Node.Builder<Integer>>> ms = Arrays.asList(
-                s -> Nodes.builder(),
-                s -> Nodes.builder(s, LambdaTestHelpers.integerArrayGenerator)
-        );
-
-        Object[][] params = new Object[ls.size() * ms.size()][];
-        int i = 0;
-        for (List<Integer> l : ls) {
-            for (Function<Integer, Node.Builder<Integer>> m : ms) {
-                params[i++] = new Object[]{l, m};
-            }
-        }
-
-        return params;
-    }
-
-    @Test(dataProvider = "Node.Builder", groups = { "serialization-hostile" })
-    public void testIteration(List<Integer> l, Function<Integer, Node.Builder<Integer>> m) {
-        Node.Builder<Integer> nb = m.apply(l.size());
-        nb.begin(l.size());
-        for (Integer i : l) {
-            nb.accept(i);
-        }
-        nb.end();
-
-        Node<Integer> n = nb.build();
-        assertEquals(n.count(), l.size());
-
-        {
-            List<Integer> _l = new ArrayList<>();
-            n.forEach(_l::add);
-
-            assertContents(_l, l);
-        }
-    }
-
-    // Node.Builder.OfInt
-
-    @DataProvider(name = "Node.Builder<Integer>")
-    public Object[][] createIntNodeBuilders() {
-        List<List<Integer>> ls = new ArrayList<>();
-        for (int size : sizes) {
-            ls.add(countTo(size));
-        }
-
-        List<Function<Integer, Node.Builder<Integer>>> ms = Arrays.asList(
-                s -> Nodes.intBuilder(),
-                s -> Nodes.intBuilder(s)
-        );
-
-        Object[][] params = new Object[ls.size() * ms.size()][];
-        int i = 0;
-        for (List<Integer> l : ls) {
-            for (Function<Integer, Node.Builder<Integer>> m : ms) {
-                params[i++] = new Object[]{l, m};
-            }
-        }
-
-        return params;
-    }
-
-    @Test(dataProvider = "Node.Builder<Integer>", groups = { "serialization-hostile" })
-    public void testIntIteration(List<Integer> l, Function<Integer, Node.Builder.OfInt> m) {
-        Node.Builder.OfInt nb = m.apply(l.size());
-        nb.begin(l.size());
-        for (Integer i : l) {
-            nb.accept((int) i);
-        }
-        nb.end();
-
-        Node.OfInt n = nb.build();
-        assertEquals(n.count(), l.size());
-
-        {
-            List<Integer> _l = new ArrayList<>();
-            n.forEach((IntConsumer) _l::add);
-
-            assertContents(_l, l);
-        }
-
-    }
-
-    // Node.Builder.OfLong
-
-    @DataProvider(name = "Node.Builder<Long>")
-    public Object[][] createLongNodeBuilders() {
-        List<List<Long>> ls = new ArrayList<>();
-        for (int size : sizes) {
-            List<Long> l = new ArrayList<>();
-            for (long i = 0; i < size; i++) {
-                l.add(i);
-            }
-            ls.add(l);
-        }
-
-        List<Function<Integer, Node.Builder<Long>>> ms = Arrays.asList(
-                s -> Nodes.longBuilder(),
-                s -> Nodes.longBuilder(s)
-        );
-
-        Object[][] params = new Object[ls.size() * ms.size()][];
-        int i = 0;
-        for (List<Long> l : ls) {
-            for (Function<Integer, Node.Builder<Long>> m : ms) {
-                params[i++] = new Object[]{l, m};
-            }
-        }
-
-        return params;
-    }
-
-    @Test(dataProvider = "Node.Builder<Long>")
-    public void testLongIteration(List<Long> l, Function<Integer, Node.Builder.OfLong> m) {
-        Node.Builder.OfLong nb = m.apply(l.size());
-        nb.begin(l.size());
-        for (Long i : l) {
-            nb.accept((long) i);
-        }
-        nb.end();
-
-        Node.OfLong n = nb.build();
-        assertEquals(n.count(), l.size());
-
-        {
-            List<Long> _l = new ArrayList<>();
-            n.forEach((LongConsumer) _l::add);
-
-            assertContents(_l, l);
-        }
-
-    }
-
-    // Node.Builder.OfDouble
-
-    @DataProvider(name = "Node.Builder<Double>")
-    public Object[][] createDoubleNodeBuilders() {
-        List<List<Double>> ls = new ArrayList<>();
-        for (int size : sizes) {
-            List<Double> l = new ArrayList<>();
-            for (long i = 0; i < size; i++) {
-                l.add((double) i);
-            }
-            ls.add(l);
-        }
-
-        List<Function<Integer, Node.Builder<Double>>> ms = Arrays.asList(
-                s -> Nodes.doubleBuilder(),
-                s -> Nodes.doubleBuilder(s)
-        );
-
-        Object[][] params = new Object[ls.size() * ms.size()][];
-        int i = 0;
-        for (List<Double> l : ls) {
-            for (Function<Integer, Node.Builder<Double>> m : ms) {
-                params[i++] = new Object[]{l, m};
-            }
-        }
-
-        return params;
-    }
-
-    @Test(dataProvider = "Node.Builder<Double>")
-    public void testDoubleIteration(List<Double> l, Function<Integer, Node.Builder.OfDouble> m) {
-        Node.Builder.OfDouble nb = m.apply(l.size());
-        nb.begin(l.size());
-        for (Double i : l) {
-            nb.accept((double) i);
-        }
-        nb.end();
-
-        Node.OfDouble n = nb.build();
-        assertEquals(n.count(), l.size());
-
-        {
-            List<Double> _l = new ArrayList<>();
-            n.forEach((DoubleConsumer) _l::add);
-
-            assertContents(_l, l);
-        }
-
-    }
-}
--- a/jdk/test/java/util/stream/boottest/java/util/stream/NodeTest.java	Thu Nov 19 18:18:05 2015 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,154 +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 java.util.stream;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
-import java.util.function.Function;
-
-import org.testng.annotations.DataProvider;
-import org.testng.annotations.Test;
-
-@Test
-public class NodeTest extends OpTestCase {
-
-    @DataProvider(name = "nodes")
-    public Object[][] createSizes() {
-        List<Object[]> params = new ArrayList<>();
-
-        for (int size : Arrays.asList(0, 1, 4, 15, 16, 17, 127, 128, 129, 1000)) {
-            Integer[] array = new Integer[size];
-            for (int i = 0; i < array.length; i++) {
-                array[i] = i;
-            }
-
-            List<Node<Integer>> nodes = new ArrayList<>();
-            nodes.add(Nodes.node(array));
-            nodes.add(Nodes.node(Arrays.asList(array)));
-            nodes.add(degenerateTree(Arrays.asList(array).iterator()));
-            nodes.add(tree(Arrays.asList(array), l -> Nodes.node(l.toArray(new Integer[l.size()]))));
-            nodes.add(tree(Arrays.asList(array), l -> Nodes.node(l)));
-            nodes.add(fill(array, Nodes.builder(array.length, LambdaTestHelpers.integerArrayGenerator)));
-            nodes.add(fill(array, Nodes.builder()));
-
-            for (int i = 0; i < nodes.size(); i++) {
-                params.add(new Object[]{array, nodes.get(i)});
-            }
-
-        }
-
-        return params.toArray(new Object[0][]);
-    }
-
-    Node<Integer> fill(Integer[] array, Node.Builder<Integer> nb) {
-        nb.begin(array.length);
-        for (Integer i : array) {
-            nb.accept(i);
-        }
-        nb.end();
-        return nb.build();
-    }
-
-    Node<Integer> degenerateTree(Iterator<Integer> it) {
-        if (!it.hasNext()) {
-            return Nodes.node(Collections.emptyList());
-        }
-
-        Integer i = it.next();
-        if (it.hasNext()) {
-            return new Nodes.ConcNode<Integer>(Nodes.node(new Integer[] {i}), degenerateTree(it));
-        }
-        else {
-            return Nodes.node(new Integer[]{i});
-        }
-    }
-
-    Node<Integer> tree(List<Integer> l, Function<List<Integer>, Node<Integer>> m) {
-        if (l.size() < 3) {
-            return m.apply(l);
-        }
-        else {
-            return new Nodes.ConcNode<>(
-                    tree(l.subList(0, l.size() / 2), m),
-                    tree(l.subList(l.size() / 2, l.size()), m ));
-        }
-    }
-
-    @Test(dataProvider = "nodes")
-    public void testAsArray(Integer[] array, Node<Integer> n) {
-        assertEquals(n.asArray(LambdaTestHelpers.integerArrayGenerator), array);
-    }
-
-    @Test(dataProvider = "nodes")
-    public void testFlattenAsArray(Integer[] array, Node<Integer> n) {
-        assertEquals(Nodes.flatten(n, LambdaTestHelpers.integerArrayGenerator)
-                          .asArray(LambdaTestHelpers.integerArrayGenerator), array);
-    }
-
-    @Test(dataProvider = "nodes")
-    public void testCopyTo(Integer[] array, Node<Integer> n) {
-        Integer[] copy = new Integer[(int) n.count()];
-        n.copyInto(copy, 0);
-
-        assertEquals(copy, array);
-    }
-
-    @Test(dataProvider = "nodes", groups = { "serialization-hostile" })
-    public void testForEach(Integer[] array, Node<Integer> n) {
-        List<Integer> l = new ArrayList<>((int) n.count());
-        n.forEach(e -> l.add(e));
-
-        assertEquals(l.toArray(), array);
-    }
-
-    @Test(dataProvider = "nodes")
-    public void testStreams(Integer[] array, Node<Integer> n) {
-        TestData<Integer, Stream<Integer>> data = TestData.Factory.ofRefNode("Node", n);
-
-        exerciseOps(data, s -> s);
-
-        exerciseTerminalOps(data, s -> s.toArray());
-    }
-
-    @Test(dataProvider = "nodes")
-    public void testSpliterator(Integer[] array, Node<Integer> n) {
-        SpliteratorTestHelper.testSpliterator(n::spliterator);
-    }
-
-    @Test(dataProvider = "nodes")
-    public void testTruncate(Integer[] array, Node<Integer> n) {
-        int[] nums = new int[] { 0, 1, array.length / 2, array.length - 1, array.length };
-        for (int start : nums)
-            for (int end : nums) {
-                if (start < 0 || end < 0 || end < start || end > array.length)
-                    continue;
-                Node<Integer> slice = n.truncate(start, end, Integer[]::new);
-                Integer[] asArray = slice.asArray(Integer[]::new);
-                for (int k = start; k < end; k++)
-                    assertEquals(array[k], asArray[k - start]);
-            }
-    }
-}
--- a/jdk/test/java/util/stream/boottest/java/util/stream/SliceSpliteratorTest.java	Thu Nov 19 18:18:05 2015 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,201 +0,0 @@
-/*
- * Copyright (c) 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 java.util.stream;
-
-import org.testng.annotations.DataProvider;
-import org.testng.annotations.Test;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
-import java.util.Spliterator;
-
-import static java.util.stream.Collectors.toList;
-import static org.testng.Assert.assertEquals;
-
-/**
- * @bug 8012987
- */
-@Test
-public class SliceSpliteratorTest extends LoggingTestCase {
-
-    static class UnorderedContentAsserter<T> implements SpliteratorTestHelper.ContentAsserter<T> {
-        Collection<T> source;
-
-        UnorderedContentAsserter(Collection<T> source) {
-            this.source = source;
-        }
-
-        @Override
-        public void assertContents(Collection<T> actual, Collection<T> expected, boolean isOrdered) {
-            if (isOrdered) {
-                assertEquals(actual, expected);
-            }
-            else {
-                assertEquals(actual.size(), expected.size());
-                assertTrue(source.containsAll(actual));
-            }
-        }
-    }
-
-    interface SliceTester {
-        void test(int size, int skip, int limit);
-    }
-
-    @DataProvider(name = "sliceSpliteratorDataProvider")
-    public static Object[][] sliceSpliteratorDataProvider() {
-        List<Object[]> data = new ArrayList<>();
-
-        // SIZED/SUBSIZED slice spliterator
-
-        {
-            SliceTester r = (size, skip, limit) -> {
-                final Collection<Integer> source =  IntStream.range(0, size).boxed().collect(toList());
-
-                SpliteratorTestHelper.testSpliterator(() -> {
-                    Spliterator<Integer> s = Arrays.spliterator(source.stream().toArray(Integer[]::new));
-
-                    return new StreamSpliterators.SliceSpliterator.OfRef<>(s, skip, limit);
-                });
-            };
-            data.add(new Object[]{"StreamSpliterators.SliceSpliterator.OfRef", r});
-        }
-
-        {
-            SliceTester r = (size, skip, limit) -> {
-                final Collection<Integer> source =  IntStream.range(0, size).boxed().collect(toList());
-
-                SpliteratorTestHelper.testIntSpliterator(() -> {
-                    Spliterator.OfInt s = Arrays.spliterator(source.stream().mapToInt(i->i).toArray());
-
-                    return new StreamSpliterators.SliceSpliterator.OfInt(s, skip, limit);
-                });
-            };
-            data.add(new Object[]{"StreamSpliterators.SliceSpliterator.OfInt", r});
-        }
-
-        {
-            SliceTester r = (size, skip, limit) -> {
-                final Collection<Long> source =  LongStream.range(0, size).boxed().collect(toList());
-
-                SpliteratorTestHelper.testLongSpliterator(() -> {
-                    Spliterator.OfLong s = Arrays.spliterator(source.stream().mapToLong(i->i).toArray());
-
-                    return new StreamSpliterators.SliceSpliterator.OfLong(s, skip, limit);
-                });
-            };
-            data.add(new Object[]{"StreamSpliterators.SliceSpliterator.OfLong", r});
-        }
-
-        {
-            SliceTester r = (size, skip, limit) -> {
-                final Collection<Double> source =  LongStream.range(0, size).asDoubleStream().boxed().collect(toList());
-
-                SpliteratorTestHelper.testDoubleSpliterator(() -> {
-                    Spliterator.OfDouble s = Arrays.spliterator(source.stream().mapToDouble(i->i).toArray());
-
-                    return new StreamSpliterators.SliceSpliterator.OfDouble(s, skip, limit);
-                });
-            };
-            data.add(new Object[]{"StreamSpliterators.SliceSpliterator.OfLong", r});
-        }
-
-
-        // Unordered slice spliterator
-
-        {
-            SliceTester r = (size, skip, limit) -> {
-                final Collection<Integer> source =  IntStream.range(0, size).boxed().collect(toList());
-                final UnorderedContentAsserter<Integer> uca = new UnorderedContentAsserter<>(source);
-
-                SpliteratorTestHelper.testSpliterator(() -> {
-                    Spliterator<Integer> s = Arrays.spliterator(source.stream().toArray(Integer[]::new));
-
-                    return new StreamSpliterators.UnorderedSliceSpliterator.OfRef<>(s, skip, limit);
-                }, uca);
-            };
-            data.add(new Object[]{"StreamSpliterators.UnorderedSliceSpliterator.OfRef", r});
-        }
-
-        {
-            SliceTester r = (size, skip, limit) -> {
-                final Collection<Integer> source =  IntStream.range(0, size).boxed().collect(toList());
-                final UnorderedContentAsserter<Integer> uca = new UnorderedContentAsserter<>(source);
-
-                SpliteratorTestHelper.testIntSpliterator(() -> {
-                    Spliterator.OfInt s = Arrays.spliterator(source.stream().mapToInt(i->i).toArray());
-
-                    return new StreamSpliterators.UnorderedSliceSpliterator.OfInt(s, skip, limit);
-                }, uca);
-            };
-            data.add(new Object[]{"StreamSpliterators.UnorderedSliceSpliterator.OfInt", r});
-        }
-
-        {
-            SliceTester r = (size, skip, limit) -> {
-                final Collection<Long> source =  LongStream.range(0, size).boxed().collect(toList());
-                final UnorderedContentAsserter<Long> uca = new UnorderedContentAsserter<>(source);
-
-                SpliteratorTestHelper.testLongSpliterator(() -> {
-                    Spliterator.OfLong s = Arrays.spliterator(source.stream().mapToLong(i->i).toArray());
-
-                    return new StreamSpliterators.UnorderedSliceSpliterator.OfLong(s, skip, limit);
-                }, uca);
-            };
-            data.add(new Object[]{"StreamSpliterators.UnorderedSliceSpliterator.OfLong", r});
-        }
-
-        {
-            SliceTester r = (size, skip, limit) -> {
-                final Collection<Double> source =  LongStream.range(0, size).asDoubleStream().boxed().collect(toList());
-                final UnorderedContentAsserter<Double> uca = new UnorderedContentAsserter<>(source);
-
-                SpliteratorTestHelper.testDoubleSpliterator(() -> {
-                    Spliterator.OfDouble s = Arrays.spliterator(LongStream.range(0, SIZE).asDoubleStream().toArray());
-
-                    return new StreamSpliterators.UnorderedSliceSpliterator.OfDouble(s, skip, limit);
-                }, uca);
-            };
-            data.add(new Object[]{"StreamSpliterators.UnorderedSliceSpliterator.OfLong", r});
-        }
-
-        return data.toArray(new Object[0][]);
-    }
-
-    static final int SIZE = 256;
-
-    static final int STEP = 32;
-
-    @Test(dataProvider = "sliceSpliteratorDataProvider")
-    public void testSliceSpliterator(String description, SliceTester r) {
-        setContext("size", SIZE);
-        for (int skip = 0; skip < SIZE; skip += STEP) {
-            setContext("skip", skip);
-            for (int limit = 0; limit < SIZE; limit += STEP) {
-                setContext("limit", skip);
-                r.test(SIZE, skip, limit);
-            }
-        }
-    }
-}
--- a/jdk/test/java/util/stream/boottest/java/util/stream/SpinedBufferTest.java	Thu Nov 19 18:18:05 2015 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,370 +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 java.util.stream;
-
-import org.testng.annotations.DataProvider;
-import org.testng.annotations.Test;
-
-import java.util.*;
-import java.util.function.DoubleConsumer;
-import java.util.function.IntConsumer;
-import java.util.function.LongConsumer;
-
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertFalse;
-
-@Test
-public class SpinedBufferTest {
-
-    // Create sizes around the boundary of spines
-    static List<Integer> sizes;
-    static {
-        try {
-            sizes = IntStream.range(0, 15)
-                             .map(i -> 1 << i)
-                             .flatMap(i -> Arrays.stream(new int[] { i-2, i-1, i, i+1, i+2 }))
-                             .filter(i -> i >= 0)
-                             .boxed()
-                             .distinct()
-                             .collect(Collectors.toList());
-        }
-        catch (Exception e) {
-            e.printStackTrace();
-        }
-    }
-
-    private static final int TEST_SIZE = 5000;
-
-    // SpinedBuffer
-
-    @DataProvider(name = "SpinedBuffer")
-    public Object[][] createSpinedBuffer() {
-        List<Object[]> params = new ArrayList<>();
-
-        for (int size : sizes) {
-            int[] array = IntStream.range(0, size).toArray();
-
-            SpinedBuffer<Integer> sb = new SpinedBuffer<>();
-            Arrays.stream(array).boxed().forEach(sb);
-            params.add(new Object[]{array, sb});
-
-            sb = new SpinedBuffer<>(size / 2);
-            Arrays.stream(array).boxed().forEach(sb);
-            params.add(new Object[]{array, sb});
-
-            sb = new SpinedBuffer<>(size);
-            Arrays.stream(array).boxed().forEach(sb);
-            params.add(new Object[]{array, sb});
-
-            sb = new SpinedBuffer<>(size * 2);
-            Arrays.stream(array).boxed().forEach(sb);
-            params.add(new Object[]{array, sb});
-        }
-
-        return params.toArray(new Object[0][]);
-    }
-
-    @Test(dataProvider = "SpinedBuffer")
-    public void testSpliterator(int[] array, SpinedBuffer<Integer> sb) {
-        assertEquals(sb.count(), array.length);
-        assertEquals(sb.count(), sb.spliterator().getExactSizeIfKnown());
-
-        SpliteratorTestHelper.testSpliterator(sb::spliterator);
-    }
-
-    @Test(dataProvider = "SpinedBuffer", groups = { "serialization-hostile" })
-    public void testLastSplit(int[] array, SpinedBuffer<Integer> sb) {
-        Spliterator<Integer> spliterator = sb.spliterator();
-        Spliterator<Integer> split = spliterator.trySplit();
-        long splitSizes = (split == null) ? 0 : split.getExactSizeIfKnown();
-        long lastSplitSize = spliterator.getExactSizeIfKnown();
-        splitSizes += lastSplitSize;
-
-        assertEquals(splitSizes, array.length);
-
-        List<Integer> contentOfLastSplit = new ArrayList<>();
-        spliterator.forEachRemaining(contentOfLastSplit::add);
-
-        assertEquals(contentOfLastSplit.size(), lastSplitSize);
-
-        List<Integer> end = Arrays.stream(array)
-                .boxed()
-                .skip(array.length - lastSplitSize)
-                .collect(Collectors.toList());
-        assertEquals(contentOfLastSplit, end);
-    }
-
-    @Test(groups = { "serialization-hostile" })
-    public void testSpinedBuffer() {
-        List<Integer> list1 = new ArrayList<>();
-        List<Integer> list2 = new ArrayList<>();
-        SpinedBuffer<Integer> sb = new SpinedBuffer<>();
-        for (int i = 0; i < TEST_SIZE; i++) {
-            list1.add(i);
-            sb.accept(i);
-        }
-        Iterator<Integer> it = sb.iterator();
-        for (int i = 0; i < TEST_SIZE; i++)
-            list2.add(it.next());
-        assertFalse(it.hasNext());
-        assertEquals(list1, list2);
-
-        for (int i = 0; i < TEST_SIZE; i++)
-            assertEquals(sb.get(i), (Integer) i, Integer.toString(i));
-
-        list2.clear();
-        sb.forEach(list2::add);
-        assertEquals(list1, list2);
-        Integer[] array = sb.asArray(LambdaTestHelpers.integerArrayGenerator);
-        list2.clear();
-        for (Integer i : array)
-            list2.add(i);
-        assertEquals(list1, list2);
-    }
-
-    // IntSpinedBuffer
-
-    @DataProvider(name = "IntSpinedBuffer")
-    public Object[][] createIntSpinedBuffer() {
-        List<Object[]> params = new ArrayList<>();
-
-        for (int size : sizes) {
-            int[] array = IntStream.range(0, size).toArray();
-            SpinedBuffer.OfInt sb = new SpinedBuffer.OfInt();
-            Arrays.stream(array).forEach(sb);
-
-            params.add(new Object[]{array, sb});
-        }
-
-        return params.toArray(new Object[0][]);
-    }
-
-    @Test(dataProvider = "IntSpinedBuffer")
-    public void testIntSpliterator(int[] array, SpinedBuffer.OfInt sb) {
-        assertEquals(sb.count(), array.length);
-        assertEquals(sb.count(), sb.spliterator().getExactSizeIfKnown());
-
-        SpliteratorTestHelper.testIntSpliterator(sb::spliterator);
-    }
-
-    @Test(dataProvider = "IntSpinedBuffer", groups = { "serialization-hostile" })
-    public void testIntLastSplit(int[] array, SpinedBuffer.OfInt sb) {
-        Spliterator.OfInt spliterator = sb.spliterator();
-        Spliterator.OfInt split = spliterator.trySplit();
-        long splitSizes = (split == null) ? 0 : split.getExactSizeIfKnown();
-        long lastSplitSize = spliterator.getExactSizeIfKnown();
-        splitSizes += lastSplitSize;
-
-        assertEquals(splitSizes, array.length);
-
-        List<Integer> contentOfLastSplit = new ArrayList<>();
-        spliterator.forEachRemaining((IntConsumer) contentOfLastSplit::add);
-
-        assertEquals(contentOfLastSplit.size(), lastSplitSize);
-
-        List<Integer> end = Arrays.stream(array)
-                .boxed()
-                .skip(array.length - lastSplitSize)
-                .collect(Collectors.toList());
-        assertEquals(contentOfLastSplit, end);
-    }
-
-    @Test(groups = { "serialization-hostile" })
-    public void testIntSpinedBuffer() {
-        List<Integer> list1 = new ArrayList<>();
-        List<Integer> list2 = new ArrayList<>();
-        SpinedBuffer.OfInt sb = new SpinedBuffer.OfInt();
-        for (int i = 0; i < TEST_SIZE; i++) {
-            list1.add(i);
-            sb.accept(i);
-        }
-        PrimitiveIterator.OfInt it = sb.iterator();
-        for (int i = 0; i < TEST_SIZE; i++)
-            list2.add(it.nextInt());
-        assertFalse(it.hasNext());
-        assertEquals(list1, list2);
-
-        for (int i = 0; i < TEST_SIZE; i++)
-            assertEquals(sb.get(i), i, Integer.toString(i));
-
-        list2.clear();
-        sb.forEach((int i) -> list2.add(i));
-        assertEquals(list1, list2);
-        int[] array = sb.asPrimitiveArray();
-        list2.clear();
-        for (int i : array)
-            list2.add(i);
-        assertEquals(list1, list2);
-    }
-
-    // LongSpinedBuffer
-
-    @DataProvider(name = "LongSpinedBuffer")
-    public Object[][] createLongSpinedBuffer() {
-        List<Object[]> params = new ArrayList<>();
-
-        for (int size : sizes) {
-            long[] array = LongStream.range(0, size).toArray();
-            SpinedBuffer.OfLong sb = new SpinedBuffer.OfLong();
-            Arrays.stream(array).forEach(sb);
-
-            params.add(new Object[]{array, sb});
-        }
-
-        return params.toArray(new Object[0][]);
-    }
-
-    @Test(dataProvider = "LongSpinedBuffer")
-    public void testLongSpliterator(long[] array, SpinedBuffer.OfLong sb) {
-        assertEquals(sb.count(), array.length);
-        assertEquals(sb.count(), sb.spliterator().getExactSizeIfKnown());
-
-        SpliteratorTestHelper.testLongSpliterator(sb::spliterator);
-    }
-
-    @Test(dataProvider = "LongSpinedBuffer", groups = { "serialization-hostile" })
-    public void testLongLastSplit(long[] array, SpinedBuffer.OfLong sb) {
-        Spliterator.OfLong spliterator = sb.spliterator();
-        Spliterator.OfLong split = spliterator.trySplit();
-        long splitSizes = (split == null) ? 0 : split.getExactSizeIfKnown();
-        long lastSplitSize = spliterator.getExactSizeIfKnown();
-        splitSizes += lastSplitSize;
-
-        assertEquals(splitSizes, array.length);
-
-        List<Long> contentOfLastSplit = new ArrayList<>();
-        spliterator.forEachRemaining((LongConsumer) contentOfLastSplit::add);
-
-        assertEquals(contentOfLastSplit.size(), lastSplitSize);
-
-        List<Long> end = Arrays.stream(array)
-                .boxed()
-                .skip(array.length - lastSplitSize)
-                .collect(Collectors.toList());
-        assertEquals(contentOfLastSplit, end);
-    }
-
-    @Test(groups = { "serialization-hostile" })
-    public void testLongSpinedBuffer() {
-        List<Long> list1 = new ArrayList<>();
-        List<Long> list2 = new ArrayList<>();
-        SpinedBuffer.OfLong sb = new SpinedBuffer.OfLong();
-        for (long i = 0; i < TEST_SIZE; i++) {
-            list1.add(i);
-            sb.accept(i);
-        }
-        PrimitiveIterator.OfLong it = sb.iterator();
-        for (int i = 0; i < TEST_SIZE; i++)
-            list2.add(it.nextLong());
-        assertFalse(it.hasNext());
-        assertEquals(list1, list2);
-
-        for (int i = 0; i < TEST_SIZE; i++)
-            assertEquals(sb.get(i), i, Long.toString(i));
-
-        list2.clear();
-        sb.forEach((long i) -> list2.add(i));
-        assertEquals(list1, list2);
-        long[] array = sb.asPrimitiveArray();
-        list2.clear();
-        for (long i : array)
-            list2.add(i);
-        assertEquals(list1, list2);
-    }
-
-    // DoubleSpinedBuffer
-
-    @DataProvider(name = "DoubleSpinedBuffer")
-    public Object[][] createDoubleSpinedBuffer() {
-        List<Object[]> params = new ArrayList<>();
-
-        for (int size : sizes) {
-            // @@@ replace with double range when implemented
-            double[] array = LongStream.range(0, size).asDoubleStream().toArray();
-            SpinedBuffer.OfDouble sb = new SpinedBuffer.OfDouble();
-            Arrays.stream(array).forEach(sb);
-
-            params.add(new Object[]{array, sb});
-        }
-
-        return params.toArray(new Object[0][]);
-    }
-
-    @Test(dataProvider = "DoubleSpinedBuffer")
-    public void testDoubleSpliterator(double[] array, SpinedBuffer.OfDouble sb) {
-        assertEquals(sb.count(), array.length);
-        assertEquals(sb.count(), sb.spliterator().getExactSizeIfKnown());
-
-        SpliteratorTestHelper.testDoubleSpliterator(sb::spliterator);
-    }
-
-    @Test(dataProvider = "DoubleSpinedBuffer", groups = { "serialization-hostile" })
-    public void testLongLastSplit(double[] array, SpinedBuffer.OfDouble sb) {
-        Spliterator.OfDouble spliterator = sb.spliterator();
-        Spliterator.OfDouble split = spliterator.trySplit();
-        long splitSizes = (split == null) ? 0 : split.getExactSizeIfKnown();
-        long lastSplitSize = spliterator.getExactSizeIfKnown();
-        splitSizes += lastSplitSize;
-
-        assertEquals(splitSizes, array.length);
-
-        List<Double> contentOfLastSplit = new ArrayList<>();
-        spliterator.forEachRemaining((DoubleConsumer) contentOfLastSplit::add);
-
-        assertEquals(contentOfLastSplit.size(), lastSplitSize);
-
-        List<Double> end = Arrays.stream(array)
-                .boxed()
-                .skip(array.length - lastSplitSize)
-                .collect(Collectors.toList());
-        assertEquals(contentOfLastSplit, end);
-    }
-
-    @Test(groups = { "serialization-hostile" })
-    public void testDoubleSpinedBuffer() {
-        List<Double> list1 = new ArrayList<>();
-        List<Double> list2 = new ArrayList<>();
-        SpinedBuffer.OfDouble sb = new SpinedBuffer.OfDouble();
-        for (long i = 0; i < TEST_SIZE; i++) {
-            list1.add((double) i);
-            sb.accept((double) i);
-        }
-        PrimitiveIterator.OfDouble it = sb.iterator();
-        for (int i = 0; i < TEST_SIZE; i++)
-            list2.add(it.nextDouble());
-        assertFalse(it.hasNext());
-        assertEquals(list1, list2);
-
-        for (int i = 0; i < TEST_SIZE; i++)
-            assertEquals(sb.get(i), (double) i, Double.toString(i));
-
-        list2.clear();
-        sb.forEach((double i) -> list2.add(i));
-        assertEquals(list1, list2);
-        double[] array = sb.asPrimitiveArray();
-        list2.clear();
-        for (double i : array)
-            list2.add(i);
-        assertEquals(list1, list2);
-    }
-}
--- a/jdk/test/java/util/stream/boottest/java/util/stream/StreamFlagsTest.java	Thu Nov 19 18:18:05 2015 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,96 +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 java.util.stream;
-
-import org.testng.annotations.Test;
-
-import java.util.*;
-import java.util.stream.Stream;
-import java.util.stream.StreamOpFlag;
-import java.util.stream.Streams;
-
-import static java.util.stream.StreamOpFlag.*;
-import static org.testng.Assert.assertEquals;
-import static org.testng.Assert.assertTrue;
-
-/**
- * StreamFlagsTest
- *
- * @author Brian Goetz
- */
-@Test
-public class StreamFlagsTest {
-    Stream<String> arrayList = new ArrayList<String>().stream();
-    Stream<String> linkedList = new LinkedList<String>().stream();
-    Stream<String> hashSet = new HashSet<String>().stream();
-    Stream<String> treeSet = new TreeSet<String>().stream();
-    Stream<String> linkedHashSet = new LinkedHashSet<String>().stream();
-    Stream<String> repeat = Stream.generate(() -> "");
-
-    Stream<?>[] streams = { arrayList, linkedList, hashSet, treeSet, linkedHashSet, repeat };
-
-    private void assertFlags(int value, EnumSet<StreamOpFlag> setFlags, EnumSet<StreamOpFlag> clearFlags) {
-        for (StreamOpFlag flag : setFlags)
-            assertTrue(flag.isKnown(value));
-        for (StreamOpFlag flag : clearFlags)
-            assertTrue(!flag.isKnown(value));
-    }
-
-    public void testBaseStreams() {
-        Stream<String> arrayList = new ArrayList<String>().stream();
-        Stream<String> linkedList = new LinkedList<String>().stream();
-        Stream<String> hashSet = new HashSet<String>().stream();
-        Stream<String> treeSet = new TreeSet<String>().stream();
-        Stream<String> linkedHashSet = new LinkedHashSet<String>().stream();
-        Stream<String> repeat = Stream.generate(() -> "");
-
-        assertFlags(OpTestCase.getStreamFlags(arrayList),
-                    EnumSet.of(ORDERED, SIZED),
-                    EnumSet.of(DISTINCT, SORTED, SHORT_CIRCUIT));
-        assertFlags(OpTestCase.getStreamFlags(linkedList),
-                    EnumSet.of(ORDERED, SIZED),
-                    EnumSet.of(DISTINCT, SORTED, SHORT_CIRCUIT));
-        assertFlags(OpTestCase.getStreamFlags(hashSet),
-                    EnumSet.of(SIZED, DISTINCT),
-                    EnumSet.of(ORDERED, SORTED, SHORT_CIRCUIT));
-        assertFlags(OpTestCase.getStreamFlags(treeSet),
-                    EnumSet.of(ORDERED, SIZED, DISTINCT, SORTED),
-                    EnumSet.of(SHORT_CIRCUIT));
-        assertFlags(OpTestCase.getStreamFlags(linkedHashSet),
-                    EnumSet.of(ORDERED, DISTINCT, SIZED),
-                    EnumSet.of(SORTED, SHORT_CIRCUIT));
-        assertFlags(OpTestCase.getStreamFlags(repeat),
-                    EnumSet.noneOf(StreamOpFlag.class),
-                    EnumSet.of(DISTINCT, SORTED, SHORT_CIRCUIT));
-    }
-
-    public void testFilter() {
-        for (Stream<?> s : streams) {
-            int baseFlags = OpTestCase.getStreamFlags(s);
-            int filteredFlags = OpTestCase.getStreamFlags(s.filter((Object e) -> true));
-            int expectedFlags = baseFlags & ~SIZED.set();
-
-            assertEquals(filteredFlags, expectedFlags);
-        }
-    }
-}
--- a/jdk/test/java/util/stream/boottest/java/util/stream/StreamOpFlagsTest.java	Thu Nov 19 18:18:05 2015 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,381 +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 java.util.stream;
-
-import org.testng.annotations.Test;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Comparator;
-import java.util.EnumSet;
-import java.util.List;
-import java.util.Spliterator;
-import java.util.function.Consumer;
-import java.util.function.Function;
-import java.util.function.ToDoubleFunction;
-import java.util.function.ToIntFunction;
-import java.util.function.ToLongFunction;
-
-import static java.util.stream.Collectors.toList;
-import static java.util.stream.StreamOpFlag.*;
-import static org.testng.Assert.*;
-import static org.testng.Assert.assertEquals;
-
-@Test
-public class StreamOpFlagsTest {
-
-    public void testNullCombine() {
-        int sourceFlags = StreamOpFlag.IS_SIZED;
-
-        assertEquals(sourceFlags, toStreamFlags(combineOpFlags(sourceFlags, StreamOpFlag.INITIAL_OPS_VALUE)));
-    }
-
-    public void testInitialOpFlagsFromSourceFlags() {
-        List<StreamOpFlag> flags = new ArrayList<>(StreamOpFlagTestHelper.allStreamFlags());
-        for (int i = 0; i < (1 << flags.size()); i++)  {
-            int sourceFlags = 0;
-            for (int f = 0; f < flags.size(); f++) {
-                if ((i & (1 << f)) != 0)  {
-                    sourceFlags |= flags.get(f).set();
-                }
-            }
-
-            int opsFlags = combineOpFlags(sourceFlags, StreamOpFlag.INITIAL_OPS_VALUE);
-            assertEquals(opsFlags, (~(sourceFlags << 1)) & StreamOpFlag.INITIAL_OPS_VALUE);
-        }
-    }
-
-    public void testSameCombine() {
-        for (StreamOpFlag f : StreamOpFlagTestHelper.allStreamFlags()) {
-            int sourceFlags = f.set();
-            int opsFlags;
-
-            opsFlags = combineOpFlags(sourceFlags, StreamOpFlag.INITIAL_OPS_VALUE);
-            opsFlags = combineOpFlags(f.set(), opsFlags);
-            assertEquals(sourceFlags, toStreamFlags(opsFlags));
-        }
-    }
-
-    public void testOpClear() {
-        for (StreamOpFlag f : StreamOpFlagTestHelper.allStreamFlags()) {
-            // Clear when source not set
-            int sourceFlags = 0;
-            int opsFlags;
-
-            opsFlags = combineOpFlags(sourceFlags, StreamOpFlag.INITIAL_OPS_VALUE);
-            opsFlags = combineOpFlags(f.clear(), opsFlags);
-            assertEquals(sourceFlags, toStreamFlags(opsFlags));
-
-            // Clear when source set
-            sourceFlags = f.set();
-
-            opsFlags = combineOpFlags(sourceFlags, StreamOpFlag.INITIAL_OPS_VALUE);
-            opsFlags = combineOpFlags(f.clear(), opsFlags);
-            assertEquals(0, toStreamFlags(opsFlags));
-        }
-    }
-
-    public void testOpInject() {
-        for (StreamOpFlag f : StreamOpFlagTestHelper.allStreamFlags()) {
-            // Set when source not set
-            int sourceFlags = 0;
-            int opsFlags;
-
-            opsFlags = combineOpFlags(sourceFlags, StreamOpFlag.INITIAL_OPS_VALUE);
-            opsFlags = combineOpFlags(f.set(), opsFlags);
-            assertEquals(f.set(), toStreamFlags(opsFlags));
-
-            // Set when source set
-            sourceFlags = f.set();
-
-            opsFlags = combineOpFlags(sourceFlags, StreamOpFlag.INITIAL_OPS_VALUE);
-            opsFlags = combineOpFlags(f.set(), opsFlags);
-            assertEquals(sourceFlags, toStreamFlags(opsFlags));
-        }
-    }
-
-    public void testPairSet() {
-        List<Integer> sourceFlagsList
-                = StreamOpFlagTestHelper.allStreamFlags().stream().map(StreamOpFlag::set).collect(toList());
-        sourceFlagsList.add(0, 0);
-
-        for (int sourceFlags : sourceFlagsList) {
-            for (StreamOpFlag f1 : StreamOpFlagTestHelper.allStreamFlags()) {
-                for (StreamOpFlag f2 : StreamOpFlagTestHelper.allStreamFlags()) {
-                    int opsFlags;
-
-                    opsFlags = combineOpFlags(sourceFlags, StreamOpFlag.INITIAL_OPS_VALUE);
-                    opsFlags = combineOpFlags(f1.set(), opsFlags);
-                    opsFlags = combineOpFlags(f2.set(), opsFlags);
-                    assertEquals(sourceFlags | f1.set() | f2.set(), toStreamFlags(opsFlags));
-                }
-            }
-        }
-    }
-
-    public void testPairSetAndClear() {
-        List<Integer> sourceFlagsList
-                = StreamOpFlagTestHelper.allStreamFlags().stream().map(StreamOpFlag::set).collect(toList());
-        sourceFlagsList.add(0, 0);
-
-        for (int sourceFlags : sourceFlagsList) {
-            for (StreamOpFlag f1 : StreamOpFlagTestHelper.allStreamFlags())  {
-                for (StreamOpFlag f2 : StreamOpFlagTestHelper.allStreamFlags()) {
-                    int opsFlags;
-
-                    opsFlags = combineOpFlags(sourceFlags, StreamOpFlag.INITIAL_OPS_VALUE);
-                    opsFlags = combineOpFlags(f1.set(), opsFlags);
-                    opsFlags = combineOpFlags(f2.clear(), opsFlags);
-                    if (f1 == f2)
-                        assertEquals((f2.set() == sourceFlags) ? 0 : sourceFlags, toStreamFlags(opsFlags));
-                    else
-                        assertEquals((f2.set() == sourceFlags) ? f1.set() : sourceFlags | f1.set(), toStreamFlags(opsFlags));
-                }
-            }
-        }
-    }
-
-    public void testShortCircuit() {
-        int opsFlags = combineOpFlags(0, StreamOpFlag.INITIAL_OPS_VALUE);
-        assertFalse(StreamOpFlag.SHORT_CIRCUIT.isKnown(opsFlags));
-
-        opsFlags = combineOpFlags(StreamOpFlag.IS_SHORT_CIRCUIT, opsFlags);
-        assertTrue(StreamOpFlag.SHORT_CIRCUIT.isKnown(opsFlags));
-
-        opsFlags = combineOpFlags(0, opsFlags);
-        assertTrue(StreamOpFlag.SHORT_CIRCUIT.isKnown(opsFlags));
-    }
-
-    public void testApplySourceFlags() {
-        int sourceFlags = StreamOpFlag.IS_SIZED | StreamOpFlag.IS_DISTINCT;
-
-        List<Integer> ops = Arrays.asList(StreamOpFlag.NOT_SIZED, StreamOpFlag.IS_ORDERED | StreamOpFlag.IS_SORTED);
-
-        int opsFlags = StreamOpFlag.combineOpFlags(sourceFlags, StreamOpFlag.INITIAL_OPS_VALUE);
-        for (int opFlags : ops) {
-            opsFlags = combineOpFlags(opFlags, opsFlags);
-        }
-        assertFalse(StreamOpFlag.SIZED.isKnown(opsFlags));
-        assertTrue(StreamOpFlag.SIZED.isCleared(opsFlags));
-        assertFalse(StreamOpFlag.SIZED.isPreserved(opsFlags));
-        assertTrue(StreamOpFlag.DISTINCT.isKnown(opsFlags));
-        assertFalse(StreamOpFlag.DISTINCT.isCleared(opsFlags));
-        assertFalse(StreamOpFlag.DISTINCT.isPreserved(opsFlags));
-        assertTrue(StreamOpFlag.SORTED.isKnown(opsFlags));
-        assertFalse(StreamOpFlag.SORTED.isCleared(opsFlags));
-        assertFalse(StreamOpFlag.SORTED.isPreserved(opsFlags));
-        assertTrue(StreamOpFlag.ORDERED.isKnown(opsFlags));
-        assertFalse(StreamOpFlag.ORDERED.isCleared(opsFlags));
-        assertFalse(StreamOpFlag.ORDERED.isPreserved(opsFlags));
-
-        int streamFlags = toStreamFlags(opsFlags);
-        assertFalse(StreamOpFlag.SIZED.isKnown(streamFlags));
-        assertTrue(StreamOpFlag.DISTINCT.isKnown(streamFlags));
-        assertTrue(StreamOpFlag.SORTED.isKnown(streamFlags));
-        assertTrue(StreamOpFlag.ORDERED.isKnown(streamFlags));
-    }
-
-    public void testSpliteratorMask() {
-        assertSpliteratorMask(StreamOpFlag.DISTINCT.set(), StreamOpFlag.IS_DISTINCT);
-        assertSpliteratorMask(StreamOpFlag.DISTINCT.clear(), 0);
-
-        assertSpliteratorMask(StreamOpFlag.SORTED.set(), StreamOpFlag.IS_SORTED);
-        assertSpliteratorMask(StreamOpFlag.SORTED.clear(), 0);
-
-        assertSpliteratorMask(StreamOpFlag.ORDERED.set(), StreamOpFlag.IS_ORDERED);
-        assertSpliteratorMask(StreamOpFlag.ORDERED.clear(), 0);
-
-        assertSpliteratorMask(StreamOpFlag.SIZED.set(), StreamOpFlag.IS_SIZED);
-        assertSpliteratorMask(StreamOpFlag.SIZED.clear(), 0);
-
-        assertSpliteratorMask(StreamOpFlag.SHORT_CIRCUIT.set(), 0);
-        assertSpliteratorMask(StreamOpFlag.SHORT_CIRCUIT.clear(), 0);
-    }
-
-    private void assertSpliteratorMask(int actual, int expected) {
-        assertEquals(actual & StreamOpFlag.SPLITERATOR_CHARACTERISTICS_MASK, expected);
-    }
-
-    public void testStreamMask() {
-        assertStreamMask(StreamOpFlag.DISTINCT.set(), StreamOpFlag.IS_DISTINCT);
-        assertStreamMask(StreamOpFlag.DISTINCT.clear(), 0);
-
-        assertStreamMask(StreamOpFlag.SORTED.set(), StreamOpFlag.IS_SORTED);
-        assertStreamMask(StreamOpFlag.SORTED.clear(), 0);
-
-        assertStreamMask(StreamOpFlag.ORDERED.set(), StreamOpFlag.IS_ORDERED);
-        assertStreamMask(StreamOpFlag.ORDERED.clear(), 0);
-
-        assertStreamMask(StreamOpFlag.SIZED.set(), StreamOpFlag.IS_SIZED);
-        assertStreamMask(StreamOpFlag.SIZED.clear(), 0);
-
-        assertStreamMask(StreamOpFlag.SHORT_CIRCUIT.set(), 0);
-        assertStreamMask(StreamOpFlag.SHORT_CIRCUIT.clear(), 0);
-    }
-
-    private void assertStreamMask(int actual, int expected) {
-        assertEquals(actual & StreamOpFlag.STREAM_MASK, expected);
-    }
-
-    public void testOpMask() {
-        assertOpMask(StreamOpFlag.DISTINCT.set(), StreamOpFlag.IS_DISTINCT);
-        assertOpMask(StreamOpFlag.DISTINCT.clear(), StreamOpFlag.NOT_DISTINCT);
-
-        assertOpMask(StreamOpFlag.SORTED.set(), StreamOpFlag.IS_SORTED);
-        assertOpMask(StreamOpFlag.SORTED.clear(), StreamOpFlag.NOT_SORTED);
-
-        assertOpMask(StreamOpFlag.ORDERED.set(), StreamOpFlag.IS_ORDERED);
-        assertOpMask(StreamOpFlag.ORDERED.clear(), StreamOpFlag.NOT_ORDERED);
-
-        assertOpMask(StreamOpFlag.SIZED.set(), 0);
-        assertOpMask(StreamOpFlag.SIZED.clear(), StreamOpFlag.NOT_SIZED);
-
-        assertOpMask(StreamOpFlag.SHORT_CIRCUIT.set(), StreamOpFlag.IS_SHORT_CIRCUIT);
-        assertOpMask(StreamOpFlag.SHORT_CIRCUIT.clear(), 0);
-    }
-
-    private void assertOpMask(int actual, int expected) {
-        assertEquals(actual & StreamOpFlag.OP_MASK, expected);
-    }
-
-    public void testTerminalOpMask() {
-        assertTerminalOpMask(StreamOpFlag.DISTINCT.set(), 0);
-        assertTerminalOpMask(StreamOpFlag.DISTINCT.clear(), 0);
-
-        assertTerminalOpMask(StreamOpFlag.SORTED.set(), 0);
-        assertTerminalOpMask(StreamOpFlag.SORTED.clear(), 0);
-
-        assertTerminalOpMask(StreamOpFlag.ORDERED.set(), 0);
-        assertTerminalOpMask(StreamOpFlag.ORDERED.clear(), StreamOpFlag.NOT_ORDERED);
-
-        assertTerminalOpMask(StreamOpFlag.SIZED.set(), 0);
-        assertTerminalOpMask(StreamOpFlag.SIZED.clear(), 0);
-
-        assertTerminalOpMask(StreamOpFlag.SHORT_CIRCUIT.set(), StreamOpFlag.IS_SHORT_CIRCUIT);
-        assertTerminalOpMask(StreamOpFlag.SHORT_CIRCUIT.clear(), 0);
-    }
-
-    private void assertTerminalOpMask(int actual, int expected) {
-        assertEquals(actual & StreamOpFlag.TERMINAL_OP_MASK, expected);
-    }
-
-    public void testUpstreamTerminalOpMask() {
-        assertUpstreamTerminalOpMask(StreamOpFlag.DISTINCT.set(), 0);
-        assertUpstreamTerminalOpMask(StreamOpFlag.DISTINCT.clear(), 0);
-
-        assertUpstreamTerminalOpMask(StreamOpFlag.SORTED.set(), 0);
-        assertUpstreamTerminalOpMask(StreamOpFlag.SORTED.clear(), 0);
-
-        assertUpstreamTerminalOpMask(StreamOpFlag.ORDERED.set(), 0);
-        assertUpstreamTerminalOpMask(StreamOpFlag.ORDERED.clear(), StreamOpFlag.NOT_ORDERED);
-
-        assertUpstreamTerminalOpMask(StreamOpFlag.SIZED.set(), 0);
-        assertUpstreamTerminalOpMask(StreamOpFlag.SIZED.clear(), 0);
-
-        assertUpstreamTerminalOpMask(StreamOpFlag.SHORT_CIRCUIT.set(), 0);
-        assertUpstreamTerminalOpMask(StreamOpFlag.SHORT_CIRCUIT.clear(), 0);
-    }
-
-    private void assertUpstreamTerminalOpMask(int actual, int expected) {
-        assertEquals(actual & StreamOpFlag.UPSTREAM_TERMINAL_OP_MASK, expected);
-    }
-
-    public void testSpliteratorCharacteristics() {
-        assertEquals(Spliterator.DISTINCT, StreamOpFlag.IS_DISTINCT);
-        assertEquals(Spliterator.SORTED, StreamOpFlag.IS_SORTED);
-        assertEquals(Spliterator.ORDERED, StreamOpFlag.IS_ORDERED);
-        assertEquals(Spliterator.SIZED, StreamOpFlag.IS_SIZED);
-
-        List<Integer> others = Arrays.asList(Spliterator.NONNULL, Spliterator.IMMUTABLE,
-                                             Spliterator.CONCURRENT, Spliterator.SUBSIZED);
-        for (int c : others) {
-            assertNotEquals(c, StreamOpFlag.IS_SHORT_CIRCUIT);
-        }
-    }
-
-    public void testSpliteratorCharacteristicsMask() {
-        assertSpliteratorCharacteristicsMask(StreamOpFlag.DISTINCT.set(), StreamOpFlag.IS_DISTINCT);
-        assertSpliteratorCharacteristicsMask(StreamOpFlag.DISTINCT.clear(), 0);
-
-        assertSpliteratorCharacteristicsMask(StreamOpFlag.SORTED.set(), StreamOpFlag.IS_SORTED);
-        assertSpliteratorCharacteristicsMask(StreamOpFlag.SORTED.clear(), 0);
-
-        assertSpliteratorCharacteristicsMask(StreamOpFlag.ORDERED.set(), StreamOpFlag.IS_ORDERED);
-        assertSpliteratorCharacteristicsMask(StreamOpFlag.ORDERED.clear(), 0);
-
-        assertSpliteratorCharacteristicsMask(StreamOpFlag.SIZED.set(), StreamOpFlag.IS_SIZED);
-        assertSpliteratorCharacteristicsMask(StreamOpFlag.SIZED.clear(), 0);
-
-        assertSpliteratorCharacteristicsMask(StreamOpFlag.SHORT_CIRCUIT.set(), 0);
-        assertSpliteratorCharacteristicsMask(StreamOpFlag.SHORT_CIRCUIT.clear(), 0);
-    }
-
-    private void assertSpliteratorCharacteristicsMask(int actual, int expected) {
-        assertEquals(StreamOpFlag.fromCharacteristics(actual), expected);
-    }
-
-    public void testSpliteratorSorted() {
-        class SortedEmptySpliterator implements Spliterator<Object> {
-            final Comparator<Object> c;
-
-            SortedEmptySpliterator(Comparator<Object> c) {
-                this.c = c;
-            }
-
-            @Override
-            public Spliterator<Object> trySplit() {
-                return null;
-            }
-
-            @Override
-            public boolean tryAdvance(Consumer<? super Object> action) {
-                return false;
-            }
-
-            @Override
-            public long estimateSize() {
-                return Long.MAX_VALUE;
-            }
-
-            @Override
-            public int characteristics() {
-                return Spliterator.SORTED;
-            }
-
-            @Override
-            public Comparator<? super Object> getComparator() {
-                return c;
-            }
-        };
-
-        {
-            int flags = StreamOpFlag.fromCharacteristics(new SortedEmptySpliterator(null));
-            assertEquals(flags, StreamOpFlag.IS_SORTED);
-        }
-
-        {
-            int flags = StreamOpFlag.fromCharacteristics(new SortedEmptySpliterator((a, b) -> 0));
-            assertEquals(flags, 0);
-        }
-    }
-}
--- a/jdk/test/java/util/stream/boottest/java/util/stream/StreamReuseTest.java	Thu Nov 19 18:18:05 2015 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,441 +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 java.util.stream;
-
-import org.testng.annotations.Test;
-
-import java.util.function.Function;
-
-import static org.testng.Assert.fail;
-
-/**
- * StreamReuseTest
- *
- * @author Brian Goetz
- */
-@Test
-public class StreamReuseTest {
-
-    private <T, U, E, S extends BaseStream<E, S>, D extends TestData<E, S>> void assertSecondFails(
-            D data,
-            Function<S, T> first,
-            Function<S, U> second,
-            Class<? extends Throwable> exception,
-            String text) {
-        S stream = data.stream();
-        T fr = first.apply(stream);
-        try {
-            U sr = second.apply(stream);
-            fail(text + " (seq)");
-        }
-        catch (Throwable e) {
-            if (exception.isAssignableFrom(e.getClass())) {
-                // Expected
-            }
-            else if (e instanceof Error)
-                throw (Error) e;
-            else if (e instanceof RuntimeException)
-                throw (RuntimeException) e;
-            else
-                throw new AssertionError("Unexpected exception " + e.getClass(), e);
-        }
-
-        stream = data.parallelStream();
-        fr = first.apply(stream);
-        try {
-            U sr = second.apply(stream);
-            fail(text + " (par)");
-        }
-        catch (Throwable e) {
-            if (exception.isAssignableFrom(e.getClass())) {
-                // Expected
-            }
-            else if (e instanceof Error)
-                throw (Error) e;
-            else if (e instanceof RuntimeException)
-                throw (RuntimeException) e;
-            else
-                throw new AssertionError("Unexpected exception " + e.getClass(), e);
-        }
-    }
-
-    // Stream
-
-    @Test(dataProvider = "StreamTestData<Integer>", dataProviderClass = StreamTestDataProvider.class)
-    public void testTwoStreams(String name, TestData<Integer, Stream<Integer>> data) {
-        assertSecondFails(data,
-                          (Stream<Integer> s) -> s.map(i -> i), (Stream<Integer> s) -> s.map(i -> i),
-                          IllegalStateException.class,
-                          "Stream map / map succeeded erroneously");
-        assertSecondFails(data,
-                          Stream::distinct, (Stream<Integer> s) -> s.map(i -> i),
-                          IllegalStateException.class,
-                          "Stream distinct / map succeeded erroneously");
-        assertSecondFails(data,
-                          (Stream<Integer> s) -> s.map(i -> i), Stream::distinct,
-                          IllegalStateException.class,
-                          "Stream map / distinct succeeded erroneously");
-        assertSecondFails(data,
-                          Stream::distinct, Stream::distinct,
-                          IllegalStateException.class,
-                          "Stream distinct / distinct succeeded erroneously");
-    }
-
-    @Test(dataProvider = "StreamTestData<Integer>", dataProviderClass = StreamTestDataProvider.class)
-    public void testTwoTerminals(String name, TestData<Integer, Stream<Integer>> data) {
-        assertSecondFails(data,
-                          Stream::findFirst, Stream::findFirst,
-                          IllegalStateException.class,
-                          "Stream findFirst / findFirst succeeded erroneously");
-    }
-
-    @Test(dataProvider = "StreamTestData<Integer>", dataProviderClass = StreamTestDataProvider.class)
-    public void testTerminalStream(String name, TestData<Integer, Stream<Integer>> data) {
-        assertSecondFails(data,
-                          Stream::findFirst, (Stream<Integer> s) -> s.map(i -> i),
-                          IllegalStateException.class,
-                          "Stream findFirst / map succeeded erroneously");
-        assertSecondFails(data,
-                          (Stream<Integer> s) -> s.map(i -> i), Stream::findFirst,
-                          IllegalStateException.class,
-                          "Stream map / findFirst succeeded erroneously");
-        assertSecondFails(data,
-                          Stream::findFirst, Stream::distinct,
-                          IllegalStateException.class,
-                          "Stream findFirst / distinct succeeded erroneously");
-        assertSecondFails(data,
-                          Stream::distinct, Stream::findFirst,
-                          IllegalStateException.class,
-                          "Stream distinct / findFirst succeeded erroneously");
-    }
-
-    @Test(dataProvider = "StreamTestData<Integer>", dataProviderClass = StreamTestDataProvider.class)
-    public void testTwoIterators(String name, TestData<Integer, Stream<Integer>> data) {
-        assertSecondFails(data,
-                          Stream::iterator, Stream::iterator,
-                          IllegalStateException.class,
-                          "Stream iterator / iterator succeeded erroneously");
-    }
-
-    @Test(dataProvider = "StreamTestData<Integer>", dataProviderClass = StreamTestDataProvider.class)
-    public void testTerminalIterator(String name, TestData<Integer, Stream<Integer>> data) {
-        assertSecondFails(data,
-                          Stream::iterator, Stream::findFirst,
-                          IllegalStateException.class,
-                          "Stream iterator / findFirst succeeded erroneously");
-        assertSecondFails(data,
-                          Stream::findFirst, Stream::iterator,
-                          IllegalStateException.class,
-                          "Stream findFirst / iterator succeeded erroneously");
-    }
-
-    @Test(dataProvider = "StreamTestData<Integer>", dataProviderClass = StreamTestDataProvider.class)
-    public void testStreamIterator(String name, TestData<Integer, Stream<Integer>> data) {
-        assertSecondFails(data,
-                          Stream::iterator, (Stream<Integer> s) -> s.map(i -> i),
-                          IllegalStateException.class,
-                          "Stream iterator / map succeeded erroneously");
-        assertSecondFails(data,
-                          (Stream<Integer> s) -> s.map(i -> i), Stream::iterator,
-                          IllegalStateException.class,
-                          "Stream map / iterator succeeded erroneously");
-        assertSecondFails(data,
-                          Stream::iterator, Stream::distinct,
-                          IllegalStateException.class,
-                          "Stream iterator / distinct succeeded erroneously");
-        assertSecondFails(data,
-                          Stream::distinct, Stream::iterator,
-                          IllegalStateException.class,
-                          "Stream distinct / iterator succeeded erroneously");
-    }
-
-    // IntStream
-
-    @Test(dataProvider = "IntStreamTestData", dataProviderClass = IntStreamTestDataProvider.class)
-    public void testTwoStreams(String name, TestData.OfInt data) {
-        assertSecondFails(data,
-                          (IntStream s) -> s.mapToObj(i -> i), (IntStream s) -> s.mapToObj(i -> i),
-                          IllegalStateException.class,
-                          "IntStream map / map succeeded erroneously");
-        assertSecondFails(data,
-                          IntStream::distinct, (IntStream s) -> s.mapToObj(i -> i),
-                          IllegalStateException.class,
-                          "IntStream distinct / map succeeded erroneously");
-        assertSecondFails(data,
-                          (IntStream s) -> s.mapToObj(i -> i), IntStream::distinct,
-                          IllegalStateException.class,
-                          "IntStream map / distinct succeeded erroneously");
-        assertSecondFails(data,
-                          IntStream::distinct, IntStream::distinct,
-                          IllegalStateException.class,
-                          "IntStream distinct / distinct succeeded erroneously");
-    }
-
-    @Test(dataProvider = "IntStreamTestData", dataProviderClass = IntStreamTestDataProvider.class)
-    public void testTwoTerminals(String name, TestData.OfInt data) {
-        assertSecondFails(data,
-                          IntStream::sum, IntStream::sum,
-                          IllegalStateException.class,
-                          "IntStream sum / sum succeeded erroneously");
-    }
-
-    @Test(dataProvider = "IntStreamTestData", dataProviderClass = IntStreamTestDataProvider.class)
-    public void testTerminalStream(String name, TestData.OfInt data) {
-        assertSecondFails(data,
-                          IntStream::sum, (IntStream s) -> s.mapToObj(i -> i),
-                          IllegalStateException.class,
-                          "IntStream sum / map succeeded erroneously");
-        assertSecondFails(data,
-                          (IntStream s) -> s.mapToObj(i -> i), IntStream::sum,
-                          IllegalStateException.class,
-                          "IntStream map / sum succeeded erroneously");
-        assertSecondFails(data,
-                          IntStream::sum, IntStream::distinct,
-                          IllegalStateException.class,
-                          "IntStream sum / distinct succeeded erroneously");
-        assertSecondFails(data,
-                          IntStream::distinct, IntStream::sum,
-                          IllegalStateException.class,
-                          "IntStream distinct / sum succeeded erroneously");
-    }
-
-    @Test(dataProvider = "IntStreamTestData", dataProviderClass = IntStreamTestDataProvider.class)
-    public void testTwoIterators(String name, TestData.OfInt data) {
-        assertSecondFails(data,
-                          IntStream::iterator, IntStream::iterator,
-                          IllegalStateException.class,
-                          "IntStream iterator / iterator succeeded erroneously");
-    }
-
-    @Test(dataProvider = "IntStreamTestData", dataProviderClass = IntStreamTestDataProvider.class)
-    public void testTerminalIterator(String name, TestData.OfInt data) {
-        assertSecondFails(data,
-                          IntStream::iterator, IntStream::sum,
-                          IllegalStateException.class,
-                          "IntStream iterator / sum succeeded erroneously");
-        assertSecondFails(data,
-                          IntStream::sum, IntStream::iterator,
-                          IllegalStateException.class,
-                          "Stream sum / iterator succeeded erroneously");
-    }
-
-    @Test(dataProvider = "IntStreamTestData", dataProviderClass = IntStreamTestDataProvider.class)
-    public void testStreamIterator(String name, TestData.OfInt data) {
-        assertSecondFails(data,
-                          IntStream::iterator, (IntStream s) -> s.mapToObj(i -> i),
-                          IllegalStateException.class,
-                          "IntStream iterator / map succeeded erroneously");
-        assertSecondFails(data,
-                          (IntStream s) -> s.mapToObj(i -> i), IntStream::iterator,
-                          IllegalStateException.class,
-                          "IntStream map / iterator succeeded erroneously");
-        assertSecondFails(data,
-                          IntStream::iterator, IntStream::distinct,
-                          IllegalStateException.class,
-                          "IntStream iterator / distinct succeeded erroneously");
-        assertSecondFails(data,
-                          IntStream::distinct, IntStream::iterator,
-                          IllegalStateException.class,
-                          "IntStream distinct / iterator succeeded erroneously");
-    }
-
-    // LongStream
-
-    @Test(dataProvider = "LongStreamTestData", dataProviderClass = LongStreamTestDataProvider.class)
-    public void testTwoStreams(String name, TestData.OfLong data) {
-        assertSecondFails(data,
-                          (LongStream s) -> s.mapToObj(i -> i), (LongStream s) -> s.mapToObj(i -> i),
-                          IllegalStateException.class,
-                          "LongStream map / map succeeded erroneously");
-        assertSecondFails(data,
-                          LongStream::distinct, (LongStream s) -> s.mapToObj(i -> i),
-                          IllegalStateException.class,
-                          "LongStream distinct / map succeeded erroneously");
-        assertSecondFails(data,
-                          (LongStream s) -> s.mapToObj(i -> i), LongStream::distinct,
-                          IllegalStateException.class,
-                          "LongStream map / distinct succeeded erroneously");
-        assertSecondFails(data,
-                          LongStream::distinct, LongStream::distinct,
-                          IllegalStateException.class,
-                          "LongStream distinct / distinct succeeded erroneously");
-    }
-
-    @Test(dataProvider = "LongStreamTestData", dataProviderClass = LongStreamTestDataProvider.class)
-    public void testTwoTerminals(String name, TestData.OfLong data) {
-        assertSecondFails(data,
-                          LongStream::sum, LongStream::sum,
-                          IllegalStateException.class,
-                          "LongStream sum / sum succeeded erroneously");
-    }
-
-    @Test(dataProvider = "LongStreamTestData", dataProviderClass = LongStreamTestDataProvider.class)
-    public void testTerminalStream(String name, TestData.OfLong data) {
-        assertSecondFails(data,
-                          LongStream::sum, (LongStream s) -> s.mapToObj(i -> i),
-                          IllegalStateException.class,
-                          "LongStream sum / map succeeded erroneously");
-        assertSecondFails(data,
-                          (LongStream s) -> s.mapToObj(i -> i), LongStream::sum,
-                          IllegalStateException.class,
-                          "LongStream map / sum succeeded erroneously");
-        assertSecondFails(data,
-                          LongStream::sum, LongStream::distinct,
-                          IllegalStateException.class,
-                          "LongStream sum / distinct succeeded erroneously");
-        assertSecondFails(data,
-                          LongStream::distinct, LongStream::sum,
-                          IllegalStateException.class,
-                          "LongStream distinct / sum succeeded erroneously");
-    }
-
-    @Test(dataProvider = "LongStreamTestData", dataProviderClass = LongStreamTestDataProvider.class)
-    public void testTwoIterators(String name, TestData.OfLong data) {
-        assertSecondFails(data,
-                          LongStream::iterator, LongStream::iterator,
-                          IllegalStateException.class,
-                          "LongStream iterator / iterator succeeded erroneously");
-    }
-
-    @Test(dataProvider = "LongStreamTestData", dataProviderClass = LongStreamTestDataProvider.class)
-    public void testTerminalIterator(String name, TestData.OfLong data) {
-        assertSecondFails(data,
-                          LongStream::iterator, LongStream::sum,
-                          IllegalStateException.class,
-                          "LongStream iterator / sum succeeded erroneously");
-        assertSecondFails(data,
-                          LongStream::sum, LongStream::iterator,
-                          IllegalStateException.class,
-                          "Stream sum / iterator succeeded erroneously");
-    }
-
-    @Test(dataProvider = "LongStreamTestData", dataProviderClass = LongStreamTestDataProvider.class)
-    public void testStreamIterator(String name, TestData.OfLong data) {
-        assertSecondFails(data,
-                          LongStream::iterator, (LongStream s) -> s.mapToObj(i -> i),
-                          IllegalStateException.class,
-                          "LongStream iterator / map succeeded erroneously");
-        assertSecondFails(data,
-                          (LongStream s) -> s.mapToObj(i -> i), LongStream::iterator,
-                          IllegalStateException.class,
-                          "LongStream map / iterator succeeded erroneously");
-        assertSecondFails(data,
-                          LongStream::iterator, LongStream::distinct,
-                          IllegalStateException.class,
-                          "LongStream iterator / distinct succeeded erroneously");
-        assertSecondFails(data,
-                          LongStream::distinct, LongStream::iterator,
-                          IllegalStateException.class,
-                          "LongStream distinct / iterator succeeded erroneously");
-    }
-
-    // DoubleStream
-
-    @Test(dataProvider = "DoubleStreamTestData", dataProviderClass = DoubleStreamTestDataProvider.class)
-    public void testTwoStreams(String name, TestData.OfDouble data) {
-        assertSecondFails(data,
-                          (DoubleStream s) -> s.mapToObj(i -> i), (DoubleStream s) -> s.mapToObj(i -> i),
-                          IllegalStateException.class,
-                          "DoubleStream map / map succeeded erroneously");
-        assertSecondFails(data,
-                          DoubleStream::distinct, (DoubleStream s) -> s.mapToObj(i -> i),
-                          IllegalStateException.class,
-                          "DoubleStream distinct / map succeeded erroneously");
-        assertSecondFails(data,
-                          (DoubleStream s) -> s.mapToObj(i -> i), DoubleStream::distinct,
-                          IllegalStateException.class,
-                          "DoubleStream map / distinct succeeded erroneously");
-        assertSecondFails(data,
-                          DoubleStream::distinct, DoubleStream::distinct,
-                          IllegalStateException.class,
-                          "DoubleStream distinct / distinct succeeded erroneously");
-    }
-
-    @Test(dataProvider = "DoubleStreamTestData", dataProviderClass = DoubleStreamTestDataProvider.class)
-    public void testTwoTerminals(String name, TestData.OfDouble data) {
-        assertSecondFails(data,
-                          DoubleStream::sum, DoubleStream::sum,
-                          IllegalStateException.class,
-                          "DoubleStream sum / sum succeeded erroneously");
-    }
-
-    @Test(dataProvider = "DoubleStreamTestData", dataProviderClass = DoubleStreamTestDataProvider.class)
-    public void testTerminalStream(String name, TestData.OfDouble data) {
-        assertSecondFails(data,
-                          DoubleStream::sum, (DoubleStream s) -> s.mapToObj(i -> i),
-                          IllegalStateException.class,
-                          "DoubleStream sum / map succeeded erroneously");
-        assertSecondFails(data,
-                          (DoubleStream s) -> s.mapToObj(i -> i), DoubleStream::sum,
-                          IllegalStateException.class,
-                          "DoubleStream map / sum succeeded erroneously");
-        assertSecondFails(data,
-                          DoubleStream::sum, DoubleStream::distinct,
-                          IllegalStateException.class,
-                          "DoubleStream sum / distinct succeeded erroneously");
-        assertSecondFails(data,
-                          DoubleStream::distinct, DoubleStream::sum,
-                          IllegalStateException.class,
-                          "DoubleStream distinct / sum succeeded erroneously");
-    }
-
-    @Test(dataProvider = "DoubleStreamTestData", dataProviderClass = DoubleStreamTestDataProvider.class)
-    public void testTwoIterators(String name, TestData.OfDouble data) {
-        assertSecondFails(data,
-                          DoubleStream::iterator, DoubleStream::iterator,
-                          IllegalStateException.class,
-                          "DoubleStream iterator / iterator succeeded erroneously");
-    }
-
-    @Test(dataProvider = "DoubleStreamTestData", dataProviderClass = DoubleStreamTestDataProvider.class)
-    public void testTerminalIterator(String name, TestData.OfDouble data) {
-        assertSecondFails(data,
-                          DoubleStream::iterator, DoubleStream::sum,
-                          IllegalStateException.class,
-                          "DoubleStream iterator / sum succeeded erroneously");
-        assertSecondFails(data,
-                          DoubleStream::sum, DoubleStream::iterator,
-                          IllegalStateException.class,
-                          "Stream sum / iterator succeeded erroneously");
-    }
-
-    @Test(dataProvider = "DoubleStreamTestData", dataProviderClass = DoubleStreamTestDataProvider.class)
-    public void testStreamIterator(String name, TestData.OfDouble data) {
-        assertSecondFails(data,
-                          DoubleStream::iterator, (DoubleStream s) -> s.mapToObj(i -> i),
-                          IllegalStateException.class,
-                          "DoubleStream iterator / map succeeded erroneously");
-        assertSecondFails(data,
-                          (DoubleStream s) -> s.mapToObj(i -> i), DoubleStream::iterator,
-                          IllegalStateException.class,
-                          "DoubleStream map / iterator succeeded erroneously");
-        assertSecondFails(data,
-                          DoubleStream::iterator, DoubleStream::distinct,
-                          IllegalStateException.class,
-                          "DoubleStream iterator / distinct succeeded erroneously");
-        assertSecondFails(data,
-                          DoubleStream::distinct, DoubleStream::iterator,
-                          IllegalStateException.class,
-                          "DoubleStream distinct / iterator succeeded erroneously");
-    }
-}
--- a/jdk/test/java/util/stream/test/TEST.properties	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/test/java/util/stream/test/TEST.properties	Mon Nov 23 14:37:04 2015 -0500
@@ -2,7 +2,7 @@
 
 TestNG.dirs = .
 
-lib.dirs = /java/util/stream/bootlib
+lib.dirs = /java/util/stream/bootlib/java.base
 
 # Tests that must run in othervm mode
 othervm.dirs= /java/util/stream
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/swing/LookAndFeel/8138881/TestOSVersion.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug      8138881
+ * @summary  typo in OSInfo.java
+ * @modules  java.desktop/sun.awt
+ * @requires (os.family == "windows")
+ * @run main TestOSVersion
+ */
+
+import sun.awt.OSInfo;
+
+public class TestOSVersion {
+
+    private static final String WIN_VISTA_VERSION = "6.0";
+
+    public static void main(String[] arg) {
+
+        String oSVersion = System.getProperty("os.version");
+        if (WIN_VISTA_VERSION.equals(oSVersion)) {
+            if (OSInfo.getWindowsVersion().toString().equals("6.1") ) {
+                throw new RuntimeException("Incorrect Windows VISTA OS version "
+                        + "in OSInfo");
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/swing/plaf/metal/MetalUtils/bug6190373.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.awt.Graphics;
+import java.awt.image.BufferedImage;
+import java.util.concurrent.CyclicBarrier;
+
+import javax.swing.JButton;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+import javax.swing.UnsupportedLookAndFeelException;
+
+import sun.awt.AppContext;
+import sun.awt.SunToolkit;
+
+import static javax.swing.UIManager.getInstalledLookAndFeels;
+
+/**
+ * @test
+ * @bug 6190373
+ * @summary Tests 6190373
+ * @author Scott Violet
+ * @modules java.desktop/sun.awt
+ */
+public final class bug6190373 {
+
+    private static AppContext app1;
+    private static AppContext app2;
+    private static final int LOOP_COUNT = 10000;
+    private static final CyclicBarrier barrier = new CyclicBarrier(2);
+
+    public static void main(final String[] args) throws Exception {
+        final Thread t1 = new Thread(new ThreadGroup("firstGroup"), () -> {
+            app1 = SunToolkit.createNewAppContext();
+            test(true);
+        });
+        final Thread t2 = new Thread(new ThreadGroup("secondGroup"), () -> {
+            app2 = SunToolkit.createNewAppContext();
+            test(false);
+        });
+
+        t1.start();
+        t2.start();
+        t1.join();
+        t2.join();
+        app1.dispose();
+        app2.dispose();
+    }
+
+    private static void test(final boolean lock) {
+        for (final UIManager.LookAndFeelInfo laf : getInstalledLookAndFeels()) {
+            try {
+                SwingUtilities.invokeAndWait(() -> setLookAndFeel(laf));
+                barrier.await();
+                SwingUtilities.invokeAndWait(() -> slam(lock));
+                barrier.await();
+            } catch (final Exception e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    private static void slam(final boolean lock) {
+        JButton button = new JButton("HI");
+        button.setSize(100, 100);
+        BufferedImage image = new BufferedImage(100, 100,
+                                                BufferedImage.TYPE_INT_RGB);
+        for (int i = 0; i < LOOP_COUNT; i++) {
+            Graphics g = image.getGraphics();
+            if (lock) {
+                synchronized (button.getTreeLock()) {
+                    button.paint(g);
+                }
+            } else {
+                button.paint(g);
+            }
+            g.dispose();
+        }
+    }
+
+    private static void setLookAndFeel(final UIManager.LookAndFeelInfo laf) {
+        try {
+            UIManager.setLookAndFeel(laf.getClassName());
+            System.out.println("LookAndFeel: " + laf.getClassName());
+        } catch (ClassNotFoundException | InstantiationException |
+                UnsupportedLookAndFeelException | IllegalAccessException e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/swing/plaf/nimbus/8041642/ScrollBarThumbVisibleTest.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/* @test
+   @bug 8134828
+   @summary Scrollbar thumb disappears with Nimbus L&F
+   @author Semyon Sadetsky
+*/
+
+import javax.swing.*;
+import java.awt.*;
+
+public class ScrollBarThumbVisibleTest
+{
+    private static JFrame frame;
+    private static Point point;
+    private static JScrollBar bar;
+
+    public static void main(String[] args) throws Exception {
+        for (UIManager.LookAndFeelInfo info : UIManager
+                .getInstalledLookAndFeels()) {
+            if ("Nimbus".equals(info.getName())) {
+                try {
+                    UIManager.setLookAndFeel(info.getClassName());
+                } catch (Exception ex) {
+                }
+                break;
+            }
+        }
+        try {
+            SwingUtilities.invokeAndWait(new Runnable() {
+                public void run() {
+                    frame = new JFrame();
+                    frame.setUndecorated(true);
+                    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+                    setup(frame);
+                }
+            });
+            final Robot robot = new Robot();
+            robot.delay(200);
+            robot.waitForIdle();
+            SwingUtilities.invokeAndWait(new Runnable() {
+                @Override
+                public void run() {
+                    point = bar.getLocationOnScreen();
+                }
+            });
+            Color color1 = robot.getPixelColor(point.x + 48, point.y + 55);
+            Color color2 = robot.getPixelColor(point.x + 48, point.y + 125);
+            System.out.println(color1);
+            System.out.println(color2);
+           if (color1.equals(color2)) {
+                throw new RuntimeException("Thump is not visible");
+            }
+        } finally {
+            SwingUtilities.invokeAndWait(new Runnable() {
+                @Override
+                public void run() {
+                    frame.dispose();
+                }
+            });
+        }
+        System.out.println("ok");
+    }
+
+    static void setup(JFrame frame) {
+        bar = new JScrollBar(Adjustable.VERTICAL, 500, 0, 0, 1000);
+        frame.getContentPane().add(bar);
+        frame.setSize(50, 250);
+        frame.setLocation(100, 100);
+        frame.setVisible(true);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/jdk/security/jarsigner/Function.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,182 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8056174
+ * @summary test the functions of JarSigner API
+ * @modules java.base/sun.security.tools.keytool
+ *          jdk.jartool
+ */
+
+import jdk.security.jarsigner.JarSigner;
+
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.InvalidKeyException;
+import java.security.InvalidParameterException;
+import java.security.KeyStore;
+import java.security.MessageDigest;
+import java.security.PrivateKey;
+import java.security.Provider;
+import java.security.PublicKey;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateFactory;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.jar.JarFile;
+import java.util.jar.Manifest;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipOutputStream;
+
+public class Function {
+    public static void main(String[] args) throws Exception {
+
+        try (FileOutputStream fout =new FileOutputStream("src.zip");
+                ZipOutputStream zout = new ZipOutputStream(fout)) {
+            zout.putNextEntry(new ZipEntry("x"));
+            zout.write(new byte[10]);
+            zout.closeEntry();
+        }
+
+        sun.security.tools.keytool.Main.main(
+                ("-storetype jks -keystore ks -storepass changeit" +
+                        " -keypass changeit -dname" +
+                        " CN=RSA -alias r -genkeypair -keyalg rsa").split(" "));
+
+        KeyStore ks = KeyStore.getInstance("JKS");
+        ks.load(new FileInputStream("ks"), "changeit".toCharArray());
+        PrivateKey key = (PrivateKey)ks.getKey("r", "changeit".toCharArray());
+        Certificate cert = ks.getCertificate("r");
+        JarSigner.Builder jsb = new JarSigner.Builder(key,
+                CertificateFactory.getInstance("X.509").generateCertPath(
+                        Collections.singletonList(cert)));
+
+        jsb.digestAlgorithm("SHA1");
+        jsb.signatureAlgorithm("SHA1withRSA");
+
+        AtomicInteger counter = new AtomicInteger(0);
+        StringBuilder sb = new StringBuilder();
+        jsb.eventHandler(
+                (a, f)->{
+                    counter.incrementAndGet();
+                    sb.append(a).append(' ').append(f).append('\n');
+                });
+
+        OutputStream blackHole = new OutputStream() {
+            @Override
+            public void write(int b) throws IOException { }
+        };
+
+        try (ZipFile src = new ZipFile("src.zip")) {
+            jsb.build().sign(src, blackHole);
+        }
+
+        if (counter.get() != 4) {
+            throw new Exception("Event number is " + counter.get()
+                    + ":\n" + sb.toString());
+        }
+
+        // Provider test.
+        Provider p = new MyProvider();
+        jsb.digestAlgorithm("Five", p);
+        jsb.signatureAlgorithm("SHA1WithRSA", p);
+        try (ZipFile src = new ZipFile("src.zip");
+                FileOutputStream out = new FileOutputStream("out.jar")) {
+            jsb.build().sign(src, out);
+        }
+
+        try (JarFile signed = new JarFile("out.jar")) {
+            Manifest man = signed.getManifest();
+            assertTrue(man.getAttributes("x").getValue("Five-Digest").equals("FAKE"));
+
+            Manifest sf = new Manifest(signed.getInputStream(
+                    signed.getJarEntry("META-INF/SIGNER.SF")));
+            assertTrue(sf.getMainAttributes().getValue("Five-Digest-Manifest")
+                    .equals("FAKE"));
+            assertTrue(sf.getAttributes("x").getValue("Five-Digest").equals("FAKE"));
+
+            try (InputStream sig = signed.getInputStream(
+                    signed.getJarEntry("META-INF/SIGNER.RSA"))) {
+                byte[] data = sig.readAllBytes();
+                assertTrue(Arrays.equals(
+                        Arrays.copyOfRange(data, data.length-8, data.length),
+                        "FAKEFAKE".getBytes()));
+            }
+        }
+    }
+
+    private static void assertTrue(boolean v) {
+        if (!v) {
+            throw new AssertionError();
+        }
+    }
+
+    public static class MyProvider extends Provider {
+        MyProvider() {
+            super("MY", 1.0d, null);
+            put("MessageDigest.Five", Five.class.getName());
+            put("Signature.SHA1WithRSA", SHA1WithRSA.class.getName());
+        }
+    }
+
+    // "Five" is a MessageDigest always returns the same value
+    public static class Five extends MessageDigest {
+        static final byte[] dig = {0x14, 0x02, (byte)0x84}; //base64 -> FAKE
+        public Five() { super("Five"); }
+        protected void engineUpdate(byte input) { }
+        protected void engineUpdate(byte[] input, int offset, int len) { }
+        protected byte[] engineDigest() { return dig; }
+        protected void engineReset() { }
+    }
+
+    // This fake "SHA1withRSA" is a Signature always returns the same value.
+    // An existing name must be used otherwise PKCS7 does not which OID to use.
+    public static class SHA1WithRSA extends Signature {
+        static final byte[] sig = "FAKEFAKE".getBytes();
+        public SHA1WithRSA() { super("SHA1WithRSA"); }
+        protected void engineInitVerify(PublicKey publicKey)
+                throws InvalidKeyException { }
+        protected void engineInitSign(PrivateKey privateKey)
+                throws InvalidKeyException { }
+        protected void engineUpdate(byte b) throws SignatureException { }
+        protected void engineUpdate(byte[] b, int off, int len)
+                throws SignatureException { }
+        protected byte[] engineSign() throws SignatureException { return sig; }
+        protected boolean engineVerify(byte[] sigBytes)
+                throws SignatureException {
+            return Arrays.equals(sigBytes, sig);
+        }
+        protected void engineSetParameter(String param, Object value)
+                throws InvalidParameterException { }
+        protected Object engineGetParameter(String param)
+                throws InvalidParameterException { return null; }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/jdk/security/jarsigner/Spec.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,251 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @bug 8056174
+ * @summary Make sure JarSigner impl conforms to spec
+ * @library /lib/testlibrary
+ * @modules java.base/sun.security.tools.keytool
+ *          java.base/sun.security.provider.certpath
+ *          jdk.jartool
+ */
+
+import com.sun.jarsigner.ContentSigner;
+import com.sun.jarsigner.ContentSignerParameters;
+import jdk.security.jarsigner.JarSigner;
+import jdk.testlibrary.JarUtils;
+import sun.security.provider.certpath.X509CertPath;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URI;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.security.*;
+import java.security.cert.CertPath;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.function.BiConsumer;
+
+public class Spec {
+
+    public static void main(String[] args) throws Exception {
+
+        // Prepares raw file
+        Files.write(Paths.get("a"), "a".getBytes());
+
+        // Pack
+        JarUtils.createJar("a.jar", "a");
+
+        // Prepare a keystore
+        sun.security.tools.keytool.Main.main(
+                ("-keystore ks -storepass changeit -keypass changeit -dname" +
+                        " CN=RSA -alias r -genkeypair -keyalg rsa").split(" "));
+        sun.security.tools.keytool.Main.main(
+                ("-keystore ks -storepass changeit -keypass changeit -dname" +
+                        " CN=DSA -alias d -genkeypair -keyalg dsa").split(" "));
+
+        char[] pass = "changeit".toCharArray();
+
+        KeyStore ks = KeyStore.getInstance(
+                new File("ks"), pass);
+        PrivateKey pkr = (PrivateKey)ks.getKey("r", pass);
+        PrivateKey pkd = (PrivateKey)ks.getKey("d", pass);
+        CertPath cp = CertificateFactory.getInstance("X.509")
+                .generateCertPath(Arrays.asList(ks.getCertificateChain("r")));
+
+        Provider sun = Security.getProvider("SUN");
+
+        // throws
+        npe(()->new JarSigner.Builder(null));
+        npe(()->new JarSigner.Builder(null, cp));
+        iae(()->new JarSigner.Builder(
+                pkr, new X509CertPath(Collections.emptyList())));
+        iae(()->new JarSigner.Builder(pkd, cp));    // unmatched certs alg
+
+        JarSigner.Builder b1 = new JarSigner.Builder(pkr, cp);
+
+        npe(()->b1.digestAlgorithm(null));
+        nsae(()->b1.digestAlgorithm("HAHA"));
+        b1.digestAlgorithm("SHA-256");
+
+        npe(()->b1.digestAlgorithm("SHA-256", null));
+        npe(()->b1.digestAlgorithm(null, sun));
+        nsae(()->b1.digestAlgorithm("HAHA", sun));
+        b1.digestAlgorithm("SHA-256", sun);
+
+        npe(()->b1.signatureAlgorithm(null));
+        nsae(()->b1.signatureAlgorithm("HAHAwithHEHE"));
+        iae(()->b1.signatureAlgorithm("SHA256withECDSA"));
+
+        npe(()->b1.signatureAlgorithm(null, sun));
+        npe(()->b1.signatureAlgorithm("SHA256withRSA", null));
+        nsae(()->b1.signatureAlgorithm("HAHAwithHEHE", sun));
+        iae(()->b1.signatureAlgorithm("SHA256withDSA", sun));
+
+        npe(()->b1.tsa(null));
+
+        npe(()->b1.signerName(null));
+        iae(()->b1.signerName(""));
+        iae(()->b1.signerName("123456789"));
+        iae(()->b1.signerName("a+b"));
+
+        npe(()->b1.setProperty(null, ""));
+        uoe(()->b1.setProperty("what", ""));
+        npe(()->b1.setProperty("tsadigestalg", null));
+        iae(()->b1.setProperty("tsadigestalg", "HAHA"));
+        npe(()->b1.setProperty("tsapolicyid", null));
+        npe(()->b1.setProperty("internalsf", null));
+        iae(()->b1.setProperty("internalsf", "Hello"));
+        npe(()->b1.setProperty("sectionsonly", null));
+        iae(()->b1.setProperty("sectionsonly", "OK"));
+        npe(()->b1.setProperty("altsigner", null));
+        npe(()->b1.eventHandler(null));
+
+        // default values
+        JarSigner.Builder b2 = new JarSigner.Builder(pkr, cp);
+        JarSigner js2 = b2.build();
+
+        assertTrue(js2.getDigestAlgorithm().equals(
+                JarSigner.Builder.getDefaultDigestAlgorithm()));
+        assertTrue(js2.getSignatureAlgorithm().equals(
+                JarSigner.Builder.getDefaultSignatureAlgorithm(pkr)));
+        assertTrue(js2.getTsa() == null);
+        assertTrue(js2.getSignerName().equals("SIGNER"));
+        assertTrue(js2.getProperty("tsadigestalg").equals(
+                JarSigner.Builder.getDefaultDigestAlgorithm()));
+        assertTrue(js2.getProperty("tsapolicyid") == null);
+        assertTrue(js2.getProperty("internalsf").equals("false"));
+        assertTrue(js2.getProperty("sectionsonly").equals("false"));
+        assertTrue(js2.getProperty("altsigner") == null);
+        uoe(()->js2.getProperty("invalid"));
+
+        // default values
+        BiConsumer<String,String> myeh = (a,s)->{};
+        URI tsa = new URI("https://tsa.com");
+
+        JarSigner.Builder b3 = new JarSigner.Builder(pkr, cp)
+                .digestAlgorithm("SHA-1")
+                .signatureAlgorithm("SHA1withRSA")
+                .signerName("Duke")
+                .tsa(tsa)
+                .setProperty("tsadigestalg", "SHA-512")
+                .setProperty("tsapolicyid", "1.2.3.4")
+                .setProperty("internalsf", "true")
+                .setProperty("sectionsonly", "true")
+                .setProperty("altsigner", "MyContentSigner")
+                .eventHandler(myeh);
+        JarSigner js3 = b3.build();
+
+        assertTrue(js3.getDigestAlgorithm().equals("SHA-1"));
+        assertTrue(js3.getSignatureAlgorithm().equals("SHA1withRSA"));
+        assertTrue(js3.getTsa().equals(tsa));
+        assertTrue(js3.getSignerName().equals("DUKE"));
+        assertTrue(js3.getProperty("tsadigestalg").equals("SHA-512"));
+        assertTrue(js3.getProperty("tsapolicyid").equals("1.2.3.4"));
+        assertTrue(js3.getProperty("internalsf").equals("true"));
+        assertTrue(js3.getProperty("sectionsonly").equals("true"));
+        assertTrue(js3.getProperty("altsigner").equals("MyContentSigner"));
+        assertTrue(js3.getProperty("altsignerpath") == null);
+
+        assertTrue(JarSigner.Builder.getDefaultDigestAlgorithm().equals("SHA-256"));
+
+        // Calculating large DSA and RSA keys are too slow.
+        KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
+        kpg.initialize(1024);
+        assertTrue(JarSigner.Builder
+                .getDefaultSignatureAlgorithm(kpg.generateKeyPair().getPrivate())
+                    .equals("SHA256withRSA"));
+
+        kpg = KeyPairGenerator.getInstance("DSA");
+        kpg.initialize(1024);
+        assertTrue(JarSigner.Builder
+                .getDefaultSignatureAlgorithm(kpg.generateKeyPair().getPrivate())
+                .equals("SHA256withDSA"));
+
+        kpg = KeyPairGenerator.getInstance("EC");
+        kpg.initialize(192);
+        assertTrue(JarSigner.Builder
+                .getDefaultSignatureAlgorithm(kpg.generateKeyPair().getPrivate())
+                .equals("SHA256withECDSA"));
+        kpg.initialize(384);
+        assertTrue(JarSigner.Builder
+                .getDefaultSignatureAlgorithm(kpg.generateKeyPair().getPrivate())
+                .equals("SHA384withECDSA"));
+        kpg.initialize(571);
+        assertTrue(JarSigner.Builder
+                .getDefaultSignatureAlgorithm(kpg.generateKeyPair().getPrivate())
+                .equals("SHA512withECDSA"));
+    }
+
+    interface RunnableWithException {
+        void run() throws Exception;
+    }
+
+    static void uoe(RunnableWithException r) throws Exception {
+        checkException(r, UnsupportedOperationException.class);
+    }
+
+    static void nsae(RunnableWithException r) throws Exception {
+        checkException(r, NoSuchAlgorithmException.class);
+    }
+
+    static void npe(RunnableWithException r) throws Exception {
+        checkException(r, NullPointerException.class);
+    }
+
+    static void iae(RunnableWithException r) throws Exception {
+        checkException(r, IllegalArgumentException.class);
+    }
+
+    static void checkException(RunnableWithException r, Class ex)
+            throws Exception {
+        try {
+            r.run();
+        } catch (Exception e) {
+            if (ex.isAssignableFrom(e.getClass())) {
+                return;
+            }
+            throw e;
+        }
+        throw new Exception("No exception thrown");
+    }
+
+    static void assertTrue(boolean x) throws Exception {
+        if (!x) throw new Exception("Not true");
+    }
+
+    static class MyContentSigner extends ContentSigner {
+        @Override
+        public byte[] generateSignedData(
+                ContentSignerParameters parameters,
+                boolean omitContent,
+                boolean applyTimestamp) throws NoSuchAlgorithmException,
+                CertificateException, IOException {
+            return new byte[0];
+        }
+    }
+}
--- a/jdk/test/lib/testlibrary/jdk/testlibrary/OutputAnalyzer.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/test/lib/testlibrary/jdk/testlibrary/OutputAnalyzer.java	Mon Nov 23 14:37:04 2015 -0500
@@ -38,14 +38,14 @@
     private final OutputBuffer output;
     private final String stdout;
     private final String stderr;
-    private final int exitValue;
+    private final int exitValue;    // useless now. output contains exit value.
 
     /**
      * Create an OutputAnalyzer, a utility class for verifying output and exit
      * value from a Process.
      * <p>
      * OutputAnalyzer should never be instantiated directly -
-     * use {@linkplain ProcessTools#executeProcess(p)} instead
+     * use {@linkplain ProcessTools#executeProcess(ProcessBuilder)} instead
      *
      * @param process
      *            Process to analyze
@@ -93,13 +93,14 @@
      * @throws RuntimeException
      *             If the string was not found
      */
-    public void shouldContain(String expectedString) {
+    public OutputAnalyzer shouldContain(String expectedString) {
         if (!getStdout().contains(expectedString)
                 && !getStderr().contains(expectedString)) {
             reportDiagnosticSummary();
             throw new RuntimeException("'" + expectedString
                     + "' missing from stdout/stderr \n");
         }
+        return this;
     }
 
     /**
@@ -110,12 +111,13 @@
      * @throws RuntimeException
      *             If the string was not found
      */
-    public void stdoutShouldContain(String expectedString) {
+    public OutputAnalyzer stdoutShouldContain(String expectedString) {
         if (!getStdout().contains(expectedString)) {
             reportDiagnosticSummary();
             throw new RuntimeException("'" + expectedString
                     + "' missing from stdout \n");
         }
+        return this;
     }
 
     /**
@@ -126,24 +128,25 @@
      * @throws RuntimeException
      *             If the string was not found
      */
-    public void stderrShouldContain(String expectedString) {
+    public OutputAnalyzer stderrShouldContain(String expectedString) {
         if (!getStderr().contains(expectedString)) {
             reportDiagnosticSummary();
             throw new RuntimeException("'" + expectedString
                     + "' missing from stderr \n");
         }
+        return this;
     }
 
     /**
      * Verify that the stdout and stderr contents of output buffer does not
      * contain the string
      *
-     * @param expectedString
+     * @param notExpectedString
      *            String that the buffer should not contain
      * @throws RuntimeException
      *             If the string was found
      */
-    public void shouldNotContain(String notExpectedString) {
+    public OutputAnalyzer shouldNotContain(String notExpectedString) {
         if (getStdout().contains(notExpectedString)) {
             reportDiagnosticSummary();
             throw new RuntimeException("'" + notExpectedString
@@ -154,40 +157,43 @@
             throw new RuntimeException("'" + notExpectedString
                     + "' found in stderr \n");
         }
+        return this;
     }
 
     /**
      * Verify that the stdout contents of output buffer does not contain the
      * string
      *
-     * @param expectedString
+     * @param notExpectedString
      *            String that the buffer should not contain
      * @throws RuntimeException
      *             If the string was found
      */
-    public void stdoutShouldNotContain(String notExpectedString) {
+    public OutputAnalyzer stdoutShouldNotContain(String notExpectedString) {
         if (getStdout().contains(notExpectedString)) {
             reportDiagnosticSummary();
             throw new RuntimeException("'" + notExpectedString
                     + "' found in stdout \n");
         }
+        return this;
     }
 
     /**
      * Verify that the stderr contents of output buffer does not contain the
      * string
      *
-     * @param expectedString
+     * @param notExpectedString
      *            String that the buffer should not contain
      * @throws RuntimeException
      *             If the string was found
      */
-    public void stderrShouldNotContain(String notExpectedString) {
+    public OutputAnalyzer stderrShouldNotContain(String notExpectedString) {
         if (getStderr().contains(notExpectedString)) {
             reportDiagnosticSummary();
             throw new RuntimeException("'" + notExpectedString
                     + "' found in stderr \n");
         }
+        return this;
     }
 
     /**
@@ -198,7 +204,7 @@
      * @throws RuntimeException
      *             If the pattern was not found
      */
-    public void shouldMatch(String pattern) {
+    public OutputAnalyzer shouldMatch(String pattern) {
         Matcher stdoutMatcher = Pattern.compile(pattern, Pattern.MULTILINE)
                 .matcher(getStdout());
         Matcher stderrMatcher = Pattern.compile(pattern, Pattern.MULTILINE)
@@ -208,6 +214,7 @@
             throw new RuntimeException("'" + pattern
                     + "' missing from stdout/stderr \n");
         }
+        return this;
     }
 
     /**
@@ -217,7 +224,7 @@
      * @throws RuntimeException
      *             If the pattern was not found
      */
-    public void stdoutShouldMatch(String pattern) {
+    public OutputAnalyzer stdoutShouldMatch(String pattern) {
         Matcher matcher = Pattern.compile(pattern, Pattern.MULTILINE).matcher(
                 getStdout());
         if (!matcher.find()) {
@@ -225,6 +232,7 @@
             throw new RuntimeException("'" + pattern
                     + "' missing from stdout \n");
         }
+        return this;
     }
 
     /**
@@ -234,7 +242,7 @@
      * @throws RuntimeException
      *             If the pattern was not found
      */
-    public void stderrShouldMatch(String pattern) {
+    public OutputAnalyzer stderrShouldMatch(String pattern) {
         Matcher matcher = Pattern.compile(pattern, Pattern.MULTILINE).matcher(
                 getStderr());
         if (!matcher.find()) {
@@ -242,6 +250,7 @@
             throw new RuntimeException("'" + pattern
                     + "' missing from stderr \n");
         }
+        return this;
     }
 
     /**
@@ -252,7 +261,7 @@
      * @throws RuntimeException
      *             If the pattern was found
      */
-    public void shouldNotMatch(String pattern) {
+    public OutputAnalyzer shouldNotMatch(String pattern) {
         Matcher matcher = Pattern.compile(pattern, Pattern.MULTILINE).matcher(
                 getStdout());
         if (matcher.find()) {
@@ -266,6 +275,7 @@
             throw new RuntimeException("'" + pattern + "' found in stderr: '"
                     + matcher.group() + "' \n");
         }
+        return this;
     }
 
     /**
@@ -276,13 +286,14 @@
      * @throws RuntimeException
      *             If the pattern was found
      */
-    public void stdoutShouldNotMatch(String pattern) {
+    public OutputAnalyzer stdoutShouldNotMatch(String pattern) {
         Matcher matcher = Pattern.compile(pattern, Pattern.MULTILINE).matcher(
                 getStdout());
         if (matcher.find()) {
             reportDiagnosticSummary();
             throw new RuntimeException("'" + pattern + "' found in stdout \n");
         }
+        return this;
     }
 
     /**
@@ -293,13 +304,14 @@
      * @throws RuntimeException
      *             If the pattern was found
      */
-    public void stderrShouldNotMatch(String pattern) {
+    public OutputAnalyzer stderrShouldNotMatch(String pattern) {
         Matcher matcher = Pattern.compile(pattern, Pattern.MULTILINE).matcher(
                 getStderr());
         if (matcher.find()) {
             reportDiagnosticSummary();
             throw new RuntimeException("'" + pattern + "' found in stderr \n");
         }
+        return this;
     }
 
     /**
@@ -347,12 +359,13 @@
      *             If the exit value from the process did not match the expected
      *             value
      */
-    public void shouldHaveExitValue(int expectedExitValue) {
+    public OutputAnalyzer shouldHaveExitValue(int expectedExitValue) {
         if (getExitValue() != expectedExitValue) {
             reportDiagnosticSummary();
             throw new RuntimeException("Expected to get exit value of ["
                     + expectedExitValue + "]\n");
         }
+        return this;
     }
 
     /**
@@ -360,11 +373,12 @@
      * - standard input produced by the process under test - standard output -
      * exit code Note: the command line is printed by the ProcessTools
      */
-    private void reportDiagnosticSummary() {
+    private OutputAnalyzer reportDiagnosticSummary() {
         String msg = " stdout: [" + getStdout() + "];\n" + " stderr: [" + getStderr()
                 + "]\n" + " exitValue = " + getExitValue() + "\n";
 
         System.err.println(msg);
+        return this;
     }
 
     /**
--- a/jdk/test/lib/testlibrary/jdk/testlibrary/ProcessTools.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/test/lib/testlibrary/jdk/testlibrary/ProcessTools.java	Mon Nov 23 14:37:04 2015 -0500
@@ -365,11 +365,31 @@
      * @return The {@linkplain OutputAnalyzer} instance wrapping the process.
      */
     public static OutputAnalyzer executeProcess(ProcessBuilder pb) throws Exception {
+        return executeProcess(pb, null);
+    }
+
+    /**
+     * Executes a process, pipe some text into its STDIN, waits for it
+     * to finish and returns the process output. The process will have exited
+     * before this method returns.
+     * @param pb The ProcessBuilder to execute.
+     * @param input The text to pipe into STDIN. Can be null.
+     * @return The {@linkplain OutputAnalyzer} instance wrapping the process.
+     */
+    public static OutputAnalyzer executeProcess(ProcessBuilder pb, String input)
+            throws Exception {
         OutputAnalyzer output = null;
         Process p = null;
         boolean failed = false;
         try {
             p = pb.start();
+            if (input != null) {
+                try (OutputStream os = p.getOutputStream();
+                        PrintStream ps = new PrintStream(os)) {
+                    ps.print(input);
+                    ps.flush();
+                }
+            }
             output = new OutputAnalyzer(p);
             p.waitFor();
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/invoke/util/WrapperTest.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package test.sun.invoke.util;
+
+import sun.invoke.util.ValueConversions;
+import sun.invoke.util.Wrapper;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.lang.invoke.MethodHandle;
+import java.io.Serializable;
+import java.util.Arrays;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+/* @test
+ * @summary unit tests to assert Wrapper zero identities and conversion behave correctly
+ * @modules java.base/sun.invoke.util
+ * @compile -XDignore.symbol.file WrapperTest.java
+ * @run junit test.sun.invoke.util.WrapperTest
+ */
+public class WrapperTest {
+
+    @Test
+    public void testShortZeroConversion() throws Throwable {
+        MethodHandle h1 = MethodHandles.constant(Short.class, (short)42);
+        MethodHandle h2 = h1.asType(MethodType.methodType(void.class));  // drop 42
+        MethodHandle h3 = h2.asType(MethodType.methodType(short.class));  // add 0
+        MethodHandle h4 = h3.asType(MethodType.methodType(Object.class));  // box
+
+        Object x = h4.invokeExact();
+        assertEquals(x, (short)0);
+        assertTrue(x == Short.valueOf((short)0));
+        assertTrue(x == Wrapper.SHORT.zero());
+    }
+
+    @Test
+    public void testIntZeroConversion() throws Throwable {
+        MethodHandle h1 = MethodHandles.constant(Integer.class, 42);
+        MethodHandle h2 = h1.asType(MethodType.methodType(void.class));  // drop 42
+        MethodHandle h3 = h2.asType(MethodType.methodType(int.class));  // add 0
+        MethodHandle h4 = h3.asType(MethodType.methodType(Object.class));  // box
+
+        Object x = h4.invokeExact();
+        assertEquals(x, 0);
+        assertTrue(x == Integer.valueOf(0));
+        assertTrue(x == Wrapper.INT.zero());
+    }
+
+    @Test
+    public void testLongZeroConversion() throws Throwable {
+        MethodHandle h1 = MethodHandles.constant(Long.class, 42L);
+        MethodHandle h2 = h1.asType(MethodType.methodType(void.class));  // drop 42
+        MethodHandle h3 = h2.asType(MethodType.methodType(long.class));  // add 0
+        MethodHandle h4 = h3.asType(MethodType.methodType(Object.class));  // box
+
+        Object x = h4.invokeExact();
+        assertEquals(x, 0L);
+        assertTrue(x == Long.valueOf(0));
+        assertTrue(x == Wrapper.LONG.zero());
+    }
+
+    @Test
+    public void testByteZeroConversion() throws Throwable {
+        MethodHandle h1 = MethodHandles.constant(Byte.class, (byte)42);
+        MethodHandle h2 = h1.asType(MethodType.methodType(void.class));  // drop 42
+        MethodHandle h3 = h2.asType(MethodType.methodType(byte.class));  // add 0
+        MethodHandle h4 = h3.asType(MethodType.methodType(Object.class));  // box
+
+        Object x = h4.invokeExact();
+        assertEquals(x, (byte)0);
+        assertTrue(x == Byte.valueOf((byte)0));
+        assertTrue(x == Wrapper.BYTE.zero());
+    }
+
+    @Test
+    public void testCharacterZeroConversion() throws Throwable {
+        MethodHandle h1 = MethodHandles.constant(Character.class, (char)42);
+        MethodHandle h2 = h1.asType(MethodType.methodType(void.class));  // drop 42
+        MethodHandle h3 = h2.asType(MethodType.methodType(char.class));  // add 0
+        MethodHandle h4 = h3.asType(MethodType.methodType(Object.class));  // box
+
+        Object x = h4.invokeExact();
+        assertEquals(x, (char)0);
+        assertTrue(x == Character.valueOf((char)0));
+        assertTrue(x == Wrapper.CHAR.zero());
+    }
+}
--- a/jdk/test/sun/security/mscapi/AccessKeyStore.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/test/sun/security/mscapi/AccessKeyStore.java	Mon Nov 23 14:37:04 2015 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -36,17 +36,6 @@
 
     public static void main(String[] args) throws Exception {
 
-        // Check if the provider is available
-        try {
-            Class.forName("sun.security.mscapi.SunMSCAPI");
-
-        } catch (Exception e) {
-            System.out.println(
-                "The SunMSCAPI provider is not available on this platform: " +
-                e);
-            return;
-        }
-
         // Check that a security manager has been installed
         if (System.getSecurityManager() == null) {
             throw new Exception("A security manager has not been installed");
@@ -86,8 +75,8 @@
         }
 
         int i = 0;
-        for (Enumeration e = keyStore.aliases(); e.hasMoreElements(); ) {
-            String alias = (String) e.nextElement();
+        for (Enumeration<String> e = keyStore.aliases(); e.hasMoreElements(); ) {
+            String alias = e.nextElement();
             displayEntry(keyStore, alias, i++);
         }
     }
--- a/jdk/test/sun/security/mscapi/AccessKeyStore.sh	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/test/sun/security/mscapi/AccessKeyStore.sh	Mon Nov 23 14:37:04 2015 -0500
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 #
-# Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved.
 # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 #
 # This code is free software; you can redistribute it and/or modify it
@@ -26,6 +26,7 @@
 
 # @test
 # @bug 6324295 6931562
+# @requires os.family == "windows"
 # @run shell AccessKeyStore.sh
 # @summary Confirm that permission must be granted to access keystores.
 
--- a/jdk/test/sun/security/mscapi/IsSunMSCAPIAvailable.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/test/sun/security/mscapi/IsSunMSCAPIAvailable.java	Mon Nov 23 14:37:04 2015 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, 2007, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -33,16 +33,6 @@
 
     public static void main(String[] args) throws Exception {
 
-        // Check if the provider is available
-        try {
-            Class.forName("sun.security.mscapi.SunMSCAPI");
-
-        } catch (Exception e) {
-            System.out.println(
-                "The SunMSCAPI provider is not available on this platform");
-            return;
-        }
-
         // Dynamically register the SunMSCAPI provider
         Security.addProvider(new sun.security.mscapi.SunMSCAPI());
 
@@ -58,7 +48,6 @@
         /*
          * Secure Random
          */
-
         SecureRandom random = SecureRandom.getInstance("Windows-PRNG", p);
         System.out.println("    Windows-PRNG is implemented by: " +
             random.getClass().getName());
--- a/jdk/test/sun/security/mscapi/IsSunMSCAPIAvailable.sh	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/test/sun/security/mscapi/IsSunMSCAPIAvailable.sh	Mon Nov 23 14:37:04 2015 -0500
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 #
-# Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved.
 # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 #
 # This code is free software; you can redistribute it and/or modify it
@@ -26,6 +26,7 @@
 
 # @test
 # @bug 6318171 6931562
+# @requires os.family == "windows"
 # @run shell IsSunMSCAPIAvailable.sh
 # @summary Basic test of the Microsoft CryptoAPI provider.
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/mscapi/IterateWindowsRootStore.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.InputStream;
+import java.security.KeyStore;
+import java.security.Provider;
+import java.security.Security;
+import java.security.cert.CRL;
+import java.security.cert.CRLException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactorySpi;
+import java.util.Collection;
+import java.util.Enumeration;
+
+/*
+ * @test
+ * @bug 8139436
+ * @summary This test validates an iteration over the Windows-ROOT certificate store
+ *          and retrieving all certificates.
+ *          Bug 8139436 reports an issue when 3rd party JCE providers would throw exceptions
+ *          upon creating Certificate objects.
+ *          This would for instance happen when using IAIK 3.15 and Elliptic Curve certificates
+ *          are contained in the Windows-ROOT certificate store.
+ *          The test uses a simple dummy provider which just throws Exceptions in its CertificateFactory.
+ *          To test an external provider, you can use property sun.security.mscapi.testprovider and
+ *          set it to the provider class name which has to be constructible by a constructor without
+ *          arguments. The provider jar has to be added to the classpath.
+ *          E.g. run jtreg with -javaoption:-Dsun.security.mscapi.testprovider=iaik.security.provider.IAIK and
+ *          -cpa:<path to iaik_jce.jar>
+ *
+ * @requires os.family == "windows"
+ * @author Christoph Langer
+ * @run main IterateWindowsRootStore
+ */
+public class IterateWindowsRootStore {
+    public static class TestFactory extends CertificateFactorySpi {
+        @Override
+        public Certificate engineGenerateCertificate(InputStream inStream) throws CertificateException {
+            throw new CertificateException("unimplemented");
+        }
+
+        @Override
+        public Collection<? extends Certificate> engineGenerateCertificates(InputStream inStream) throws CertificateException {
+            throw new CertificateException("unimplemented");
+        }
+
+        @Override
+        public CRL engineGenerateCRL(InputStream inStream) throws CRLException {
+            throw new CRLException("unimplemented");
+        }
+
+        @Override
+        public Collection<? extends CRL> engineGenerateCRLs(InputStream inStream) throws CRLException {
+            throw new CRLException("unimplemented");
+        }
+    }
+
+    public static class TestProvider extends Provider {
+        private static final long serialVersionUID = 1L;
+
+        public TestProvider() {
+            super("TestProvider", 0.1, "Test provider for IterateWindowsRootStore");
+
+            /*
+             * Certificates
+             */
+            this.put("CertificateFactory.X.509", "IterateWindowsRootStore$TestFactory");
+            this.put("Alg.Alias.CertificateFactory.X509", "X.509");
+        }
+    }
+
+    public static void main(String[] args) throws Exception {
+        // Try to register a JCE provider from property sun.security.mscapi.testprovider in the first slot
+        // otherwise register a dummy provider which would provoke the issue of bug 8139436
+        boolean providerPrepended = false;
+        String testprovider = System.getProperty("sun.security.mscapi.testprovider");
+        if (testprovider != null && !testprovider.isEmpty()) {
+            try {
+                System.out.println("Trying to prepend external JCE provider " + testprovider);
+                Class<?> providerclass = Class.forName(testprovider);
+                Object provider = providerclass.newInstance();
+                Security.insertProviderAt((Provider)provider, 1);
+            } catch (Exception e) {
+                System.out.println("Could not load JCE provider " + testprovider +". Exception is:");
+                e.printStackTrace(System.out);
+            }
+            providerPrepended = true;
+            System.out.println("Sucessfully prepended JCE provider " + testprovider);
+        }
+        if (!providerPrepended) {
+            System.out.println("Trying to prepend dummy JCE provider");
+            Security.insertProviderAt(new TestProvider(), 1);
+            System.out.println("Sucessfully prepended dummy JCE provider");
+        }
+
+        // load Windows-ROOT KeyStore
+        KeyStore keyStore = KeyStore.getInstance("Windows-ROOT", "SunMSCAPI");
+        keyStore.load(null, null);
+
+        // iterate KeyStore
+        Enumeration<String> aliases = keyStore.aliases();
+        while (aliases.hasMoreElements()) {
+            String alias = aliases.nextElement();
+            System.out.print("Reading certificate for alias: " + alias + "...");
+            keyStore.getCertificate(alias);
+            System.out.println(" done.");
+        }
+    }
+}
--- a/jdk/test/sun/security/mscapi/KeyStoreCompatibilityMode.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/test/sun/security/mscapi/KeyStoreCompatibilityMode.java	Mon Nov 23 14:37:04 2015 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -38,17 +38,6 @@
 
     public static void main(String[] args) throws Exception {
 
-        // Check if the provider is available
-        try {
-            Class.forName("sun.security.mscapi.SunMSCAPI");
-
-        } catch (Exception e) {
-            System.out.println(
-                "The SunMSCAPI provider is not available on this platform: " +
-                e);
-            return;
-        }
-
         if (args.length > 0 && "-disable".equals(args[0])) {
             mode = false;
         } else {
--- a/jdk/test/sun/security/mscapi/KeyStoreCompatibilityMode.sh	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/test/sun/security/mscapi/KeyStoreCompatibilityMode.sh	Mon Nov 23 14:37:04 2015 -0500
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 #
-# Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved.
 # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 #
 # This code is free software; you can redistribute it and/or modify it
@@ -23,9 +23,9 @@
 # questions.
 #
 
-
 # @test
 # @bug 6324294 6931562
+# @requires os.family == "windows"
 # @run shell KeyStoreCompatibilityMode.sh
 # @summary Confirm that a null stream or password is not permitted when 
 #          compatibility mode is enabled (and vice versa).
--- a/jdk/test/sun/security/mscapi/KeytoolChangeAlias.sh	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/test/sun/security/mscapi/KeytoolChangeAlias.sh	Mon Nov 23 14:37:04 2015 -0500
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 #
-# Copyright (c) 2006, 2011, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2006, 2015, Oracle and/or its affiliates. All rights reserved.
 # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 #
 # This code is free software; you can redistribute it and/or modify it
@@ -26,6 +26,7 @@
 
 # @test
 # @bug 6415696 6931562
+# @requires os.family == "windows"
 # @run shell KeytoolChangeAlias.sh
 # @summary Test "keytool -changealias" using the Microsoft CryptoAPI provider.
 
--- a/jdk/test/sun/security/mscapi/PrngSlow.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/test/sun/security/mscapi/PrngSlow.java	Mon Nov 23 14:37:04 2015 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 2015 Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -24,6 +24,7 @@
 /**
  * @test
  * @bug 6449335
+ * @requires os.family == "windows"
  * @summary MSCAPI's PRNG is too slow
  * @key randomness
  */
@@ -34,23 +35,15 @@
 
     public static void main(String[] args) throws Exception {
         double t = 0.0;
-        try {
-            SecureRandom sr = null;
-            sr = SecureRandom.getInstance("PRNG", "SunMSCAPI");
-            long start = System.nanoTime();
-            int x = 0;
-            for(int i = 0; i < 10000; i++) {
-                if (i % 100 == 0) System.err.print(".");
-                if (sr.nextBoolean()) x++;
-            };
-            t = (System.nanoTime() - start) / 1000000000.0;
-            System.err.println("\nSpend " + t + " seconds");
-        } catch (Exception e) {
-            // Not supported here, maybe not a Win32
-            System.err.println("Cannot find PRNG for SunMSCAPI or other mysterious bugs");
-            e.printStackTrace();
-            return;
-        }
+        SecureRandom sr = null;
+        sr = SecureRandom.getInstance("Windows-PRNG", "SunMSCAPI");
+        long start = System.nanoTime();
+        for (int i = 0; i < 10000; i++) {
+            if (i % 100 == 0) System.err.print(".");
+            sr.nextBoolean();
+        };
+        t = (System.nanoTime() - start) / 1000000000.0;
+        System.err.println("\nSpend " + t + " seconds");
         if (t > 5)
             throw new RuntimeException("Still too slow");
     }
--- a/jdk/test/sun/security/mscapi/PublicKeyInterop.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/test/sun/security/mscapi/PublicKeyInterop.java	Mon Nov 23 14:37:04 2015 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -38,8 +38,6 @@
 public class PublicKeyInterop {
 
     public static void main(String[] arg) throws Exception {
-        PrivateKey privKey = null;
-        Certificate cert = null;
         KeyStore ks = KeyStore.getInstance("Windows-MY");
         ks.load(null, null);
         System.out.println("Loaded keystore: Windows-MY");
--- a/jdk/test/sun/security/mscapi/PublicKeyInterop.sh	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/test/sun/security/mscapi/PublicKeyInterop.sh	Mon Nov 23 14:37:04 2015 -0500
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 #
-# Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2011, 2015 Oracle and/or its affiliates. All rights reserved.
 # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 #
 # This code is free software; you can redistribute it and/or modify it
@@ -25,6 +25,7 @@
 
 # @test
 # @bug 6888925
+# @requires os.family == "windows"
 # @run shell PublicKeyInterop.sh
 # @summary SunMSCAPI's Cipher can't use RSA public keys obtained from other
 #          sources.
--- a/jdk/test/sun/security/mscapi/RSAEncryptDecrypt.sh	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/test/sun/security/mscapi/RSAEncryptDecrypt.sh	Mon Nov 23 14:37:04 2015 -0500
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 #
-# Copyright (c) 2006, 2011, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2006, 2015, Oracle and/or its affiliates. All rights reserved.
 # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 #
 # This code is free software; you can redistribute it and/or modify it
@@ -25,6 +25,7 @@
 
 # @test
 # @bug 6457422 6931562
+# @requires os.family == "windows"
 # @run shell RSAEncryptDecrypt.sh
 # @summary Confirm that plaintext can be encrypted and then decrypted using the
 #	   RSA cipher in the SunMSCAPI crypto provider. NOTE: The RSA cipher is 
--- a/jdk/test/sun/security/mscapi/ShortRSAKey1024.sh	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/test/sun/security/mscapi/ShortRSAKey1024.sh	Mon Nov 23 14:37:04 2015 -0500
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 #
-# Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
 # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 #
 # This code is free software; you can redistribute it and/or modify it
@@ -27,6 +27,7 @@
 # @test
 # @bug 7106773
 # @summary 512 bits RSA key cannot work with SHA384 and SHA512
+# @requires os.family == "windows"
 # @run shell ShortRSAKey1024.sh 1024
 # @run shell ShortRSAKey1024.sh 768
 # @run shell ShortRSAKey1024.sh 512
--- a/jdk/test/sun/security/mscapi/ShortRSAKeyWithinTLS.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/test/sun/security/mscapi/ShortRSAKeyWithinTLS.java	Mon Nov 23 14:37:04 2015 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -22,12 +22,9 @@
  */
 
 import java.io.*;
-import java.net.*;
-import java.util.*;
 import java.security.*;
 import javax.net.*;
 import javax.net.ssl.*;
-import java.lang.reflect.*;
 
 import sun.security.util.KeyUtil;
 
--- a/jdk/test/sun/security/mscapi/SignUsingNONEwithRSA.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/test/sun/security/mscapi/SignUsingNONEwithRSA.java	Mon Nov 23 14:37:04 2015 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -118,12 +118,12 @@
         ks.load(null, null);
         System.out.println("Loaded keystore: Windows-MY");
 
-        Enumeration e = ks.aliases();
+        Enumeration<String> e = ks.aliases();
         PrivateKey privateKey = null;
         PublicKey publicKey = null;
 
         while (e.hasMoreElements()) {
-            String alias = (String) e.nextElement();
+            String alias = e.nextElement();
             if (alias.equals("6578658")) {
                 System.out.println("Loaded entry: " + alias);
                 privateKey = (PrivateKey) ks.getKey(alias, null);
--- a/jdk/test/sun/security/mscapi/SignUsingNONEwithRSA.sh	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/test/sun/security/mscapi/SignUsingNONEwithRSA.sh	Mon Nov 23 14:37:04 2015 -0500
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 #
-# Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
 # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 #
 # This code is free software; you can redistribute it and/or modify it
@@ -26,6 +26,7 @@
 
 # @test
 # @bug 6578658
+# @requires os.family == "windows"
 # @run shell SignUsingNONEwithRSA.sh
 # @summary Sign using the NONEwithRSA signature algorithm from SunMSCAPI
 # @key intermittent
--- a/jdk/test/sun/security/mscapi/SignUsingSHA2withRSA.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/test/sun/security/mscapi/SignUsingSHA2withRSA.java	Mon Nov 23 14:37:04 2015 -0500
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -58,12 +58,12 @@
         ks.load(null, null);
         System.out.println("Loaded keystore: Windows-MY");
 
-        Enumeration e = ks.aliases();
+        Enumeration<String> e = ks.aliases();
         PrivateKey privateKey = null;
         PublicKey publicKey = null;
 
         while (e.hasMoreElements()) {
-            String alias = (String) e.nextElement();
+            String alias = e.nextElement();
             if (alias.equals("6753664")) {
                 System.out.println("Loaded entry: " + alias);
                 privateKey = (PrivateKey) ks.getKey(alias, null);
--- a/jdk/test/sun/security/mscapi/SignUsingSHA2withRSA.sh	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/test/sun/security/mscapi/SignUsingSHA2withRSA.sh	Mon Nov 23 14:37:04 2015 -0500
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 #
-# Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved.
 # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 #
 # This code is free software; you can redistribute it and/or modify it
@@ -26,6 +26,7 @@
 
 # @test
 # @bug 6753664
+# @requires os.family == "windows"
 # @run shell SignUsingSHA2withRSA.sh
 # @summary Support SHA256 (and higher) in SunMSCAPI
 # @key intermittent
--- a/jdk/test/sun/security/mscapi/SignatureOffsets.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/test/sun/security/mscapi/SignatureOffsets.java	Mon Nov 23 14:37:04 2015 -0500
@@ -36,6 +36,7 @@
  *          and passing in different signature offset (0, 33, 66, 99).
  * @library /lib/testlibrary
  * @compile ../../../java/security/Signature/Offsets.java
+ * @requires os.family == "windows"
  * @run main SignatureOffsets SunMSCAPI NONEwithRSA
  * @run main SignatureOffsets SunMSCAPI MD2withRSA
  * @run main SignatureOffsets SunMSCAPI MD5withRSA
--- a/jdk/test/sun/security/mscapi/SignedObjectChain.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/test/sun/security/mscapi/SignedObjectChain.java	Mon Nov 23 14:37:04 2015 -0500
@@ -25,6 +25,7 @@
  * @test
  * @bug 8050374
  * @compile ../../../java/security/SignedObject/Chain.java
+ * @requires os.family == "windows"
  * @summary Verify a chain of signed objects
  */
 public class SignedObjectChain {
--- a/jdk/test/sun/security/mscapi/SmallPrimeExponentP.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/test/sun/security/mscapi/SmallPrimeExponentP.java	Mon Nov 23 14:37:04 2015 -0500
@@ -28,8 +28,6 @@
 import java.security.SecureRandom;
 import java.security.cert.X509Certificate;
 import java.security.interfaces.RSAPrivateCrtKey;
-import java.util.HashSet;
-import java.util.Set;
 
 /*
  * @test
@@ -37,6 +35,7 @@
  * @modules java.base/sun.security.x509
  *          java.base/sun.security.tools.keytool
  * @summary sun/security/mscapi/ShortRSAKey1024.sh fails intermittently
+ * @requires os.family == "windows"
  */
 public class SmallPrimeExponentP {
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/pkcs/pkcs10/PKCS10AttrEncoding.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8048357
+ * @summary test DER encoding of PKCS10 attributes
+ * @modules java.base/sun.security.pkcs
+ *          java.base/sun.security.pkcs10
+ *          java.base/sun.security.util
+ *          java.base/sun.security.x509
+ * @compile -XDignore.symbol.file PKCS10AttrEncoding.java
+ * @run main PKCS10AttrEncoding
+ */
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.PrivateKey;
+import java.security.Signature;
+import java.util.Enumeration;
+import java.util.GregorianCalendar;
+import java.util.HashMap;
+import sun.security.pkcs.PKCS9Attribute;
+import sun.security.pkcs10.PKCS10;
+import sun.security.pkcs10.PKCS10Attribute;
+import sun.security.pkcs10.PKCS10Attributes;
+import sun.security.util.ObjectIdentifier;
+import sun.security.x509.X500Name;
+import sun.security.x509.X509Key;
+
+public class PKCS10AttrEncoding {
+
+    static final ObjectIdentifier[] ids = {
+        PKCS9Attribute.CONTENT_TYPE_OID, // ContentType
+        PKCS9Attribute.SIGNING_TIME_OID, // SigningTime
+        PKCS9Attribute.CHALLENGE_PASSWORD_OID // ChallengePassword
+    };
+    static int failedCount = 0;
+    static HashMap<ObjectIdentifier, Object> constructedMap = new HashMap<>();
+
+    public static void main(String[] args) throws Exception {
+
+        // initializations
+        int len = ids.length;
+        Object[] values = {
+            new ObjectIdentifier("1.2.3.4"),
+            new GregorianCalendar(1970, 1, 25, 8, 56, 7).getTime(),
+            "challenging"
+        };
+        for (int j = 0; j < len; j++) {
+            constructedMap.put(ids[j], values[j]);
+        }
+
+        X500Name subject = new X500Name("cn=Test");
+        KeyPairGenerator keyGen = KeyPairGenerator.getInstance("DSA");
+        String sigAlg = "DSA";
+
+        keyGen.initialize(512);
+
+        KeyPair pair = keyGen.generateKeyPair();
+        X509Key publicKey = (X509Key) pair.getPublic();
+        PrivateKey privateKey = pair.getPrivate();
+
+        Signature signature = Signature.getInstance(sigAlg);
+        signature.initSign(privateKey);
+
+        // Create the PKCS10 request
+        PKCS10Attribute[] attrs = new PKCS10Attribute[len];
+        for (int j = 0; j < len; j++) {
+            attrs[j] = new PKCS10Attribute(ids[j], values[j]);
+        }
+        PKCS10 req = new PKCS10(publicKey, new PKCS10Attributes(attrs));
+        System.out.println("List of attributes in constructed PKCS10 "
+                + "request: ");
+        checkAttributes(req.getAttributes().getElements());
+
+        // Encode the PKCS10 request and generate another PKCS10 request from
+        // the encoded byte array
+        req.encodeAndSign(subject, signature);
+        PKCS10 resp = new PKCS10(req.getEncoded());
+        System.out.println("List of attributes in DER encoded PKCS10 Request:");
+        checkAttributes(resp.getAttributes().getElements());
+
+        if (failedCount > 0) {
+            throw new RuntimeException("Attributes Compared : Failed");
+        }
+        System.out.println("Attributes Compared : Pass");
+    }
+
+    static void checkAttributes(Enumeration attrs) {
+        int numOfAttrs = 0;
+        while (attrs.hasMoreElements()) {
+            numOfAttrs ++;
+            PKCS10Attribute attr = (PKCS10Attribute) attrs.nextElement();
+
+            if (constructedMap.containsKey(attr.getAttributeId())) {
+                if (constructedMap.get(attr.getAttributeId()).
+                        equals(attr.getAttributeValue())) {
+                    System.out.print("AttributeId: " + attr.getAttributeId());
+                    System.out.println(" AttributeValue: "
+                            + attr.getAttributeValue());
+                } else {
+                    failedCount++;
+                    System.out.print("< AttributeId: " + attr.getAttributeId());
+                    System.out.println("  AttributeValue: " + constructedMap.
+                            get(attr.getAttributeId()));
+                    System.out.print("< AttributeId: " + attr.getAttributeId());
+                    System.out.println("  AttributeValue: "
+                            + attr.getAttributeValue());
+                }
+            } else {
+                failedCount++;
+                System.out.println("No " + attr.getAttributeId()
+                        + " in DER encoded PKCS10 Request");
+            }
+        }
+        if(numOfAttrs != constructedMap.size()){
+            failedCount++;
+            System.out.println("Incorrect number of attributes.");
+
+        }
+        System.out.println();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/pkcs/pkcs10/PKCS10AttributeReader.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8048357
+ * @summary Read in a file containing a DER encoded PKCS10 certificate request,
+ * flanked with "begin" and "end" lines.
+ * @modules java.base/sun.security.pkcs
+ *          java.base/sun.security.pkcs10
+ *          java.base/sun.security.util
+ * @compile -XDignore.symbol.file PKCS10AttributeReader.java
+ * @run main PKCS10AttributeReader
+ */
+import java.util.Base64;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Date;
+import sun.security.pkcs.PKCS9Attribute;
+import sun.security.pkcs10.PKCS10Attribute;
+import sun.security.pkcs10.PKCS10Attributes;
+import sun.security.util.DerInputStream;
+import sun.security.util.ObjectIdentifier;
+
+/*
+ Tests only reads DER encoding files, contents of corresponding asn.1 files
+ are copied below for reference.
+
+ # An attribute set for testing with PKCS10.
+
+ {A0  # implicit tag
+    {SEQ  # Content Type
+        {OID 1.2.840.113549.1.9.3}
+        {SET
+            {OID "1234"}
+        }
+    }
+     {SEQ  # Challenge Password
+         {OID 1.2.840.113549.1.9.7}
+         {SET
+             {T61String "GuessWhoAmI"}
+         }
+     }
+     {SEQ  # Signing Time
+        {OID 1.2.840.113549.1.9.5}
+        {SET
+            {UTCTime "970422145010Z"}
+        }
+     }
+ }
+ */
+public class PKCS10AttributeReader {
+    // DER encoded files are binary files, to avoid attaching binary files,
+    // DER files were encoded in base64
+    static final String ATTRIBS = "oE8wEwYJKoZIhvcNAQkDMQYGBDEyMzQwGgYJKoZIhv"
+            + "cNAQkHMQ0UC0d1ZXNzV2hv\nQW1JMBwGCSqGSIb3DQEJBTEPFw05NzA0MjIxND"
+            + "UwMTBa";
+
+    public static void main(String[] args) throws Exception {
+
+        // Decode base64 encoded DER file
+        byte[] pkcs10Bytes = Base64.getMimeDecoder().decode(ATTRIBS.getBytes());
+
+        HashMap<ObjectIdentifier, Object> RequestStander = new HashMap() {
+            {
+                put(PKCS9Attribute.CHALLENGE_PASSWORD_OID, "GuessWhoAmI");
+                put(PKCS9Attribute.SIGNING_TIME_OID, new Date(861720610000L));
+                put(PKCS9Attribute.CONTENT_TYPE_OID,
+                        new ObjectIdentifier("1.9.50.51.52"));
+            }
+        };
+
+        int invalidNum = 0;
+        PKCS10Attributes resp = new PKCS10Attributes(
+                new DerInputStream(pkcs10Bytes));
+        Enumeration eReq = resp.getElements();
+        int numOfAttrs = 0;
+        while (eReq.hasMoreElements()) {
+            numOfAttrs++;
+            PKCS10Attribute attr = (PKCS10Attribute) eReq.nextElement();
+            if (RequestStander.containsKey(attr.getAttributeId())) {
+                if (RequestStander.get(attr.getAttributeId())
+                        .equals(attr.getAttributeValue())) {
+                    System.out.println(attr.getAttributeId() + " "
+                            + attr.getAttributeValue());
+                } else {
+                    invalidNum++;
+                    System.out.println("< " + attr.getAttributeId() + " "
+                            + attr.getAttributeValue());
+                    System.out.println("< " + attr.getAttributeId() + " "
+                            + RequestStander.get(attr.getAttributeId()));
+                }
+            } else {
+                invalidNum++;
+                System.out.println("No" + attr.getAttributeId()
+                        + "in Certificate Request list");
+            }
+        }
+        if (numOfAttrs != RequestStander.size()) {
+            invalidNum++;
+            System.out.println("Incorrect number of attributes.");
+        }
+        System.out.println();
+        if (invalidNum > 0) {
+            throw new RuntimeException(
+                    "Attributes Compared with Stander :" + " Failed");
+        }
+        System.out.println("Attributes Compared with Stander: Pass");
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/pkcs/pkcs7/PKCS7VerifyTest.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8048357
+ * @summary Read signed data in one or more PKCS7 objects from individual files,
+ * verify SignerInfos and certificate chain.
+ * @modules java.base/sun.security.pkcs
+ * @run main PKCS7VerifyTest PKCS7TEST.DSA.base64
+ * @run main PKCS7VerifyTest PKCS7TEST.DSA.base64 PKCS7TEST.SF
+ */
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.security.cert.X509Certificate;
+import java.util.Base64;
+import java.util.HashMap;
+import java.util.Map;
+import sun.security.pkcs.PKCS7;
+import sun.security.pkcs.SignerInfo;
+
+public class PKCS7VerifyTest {
+
+    static final String TESTSRC = System.getProperty("test.src", ".");
+    static final String FS = File.separator;
+    static final String FILEPATH = TESTSRC + FS + "jarsigner" + FS + "META-INF"
+            + FS;
+
+    public static void main(String[] args) throws Exception {
+        if (args.length == 0) {
+            throw new RuntimeException("usage: java JarVerify <file1> <file2>");
+        }
+
+        // The command " java PKCS7VerifyTest file1 [file2] "
+        // treats file1 as containing the DER encoding of a PKCS7 signed data
+        // object. If file2 is absent, the program verifies that some signature
+        // (SignerInfo) file1 correctly signs the data contained in the
+        // ContentInfo component of the PKCS7 object encoded by file1. If file2
+        // is present, the program verifies file1 contains a correct signature
+        // for the contents of file2.
+
+        PKCS7 pkcs7;
+        byte[] data;
+
+        // to avoid attaching binary DSA file, the DSA file was encoded
+        // in Base64, decode encoded Base64 DSA file below
+        byte[] base64Bytes = Files.readAllBytes(Paths.get(FILEPATH + args[0]));
+        pkcs7 = new PKCS7(new ByteArrayInputStream(
+                Base64.getMimeDecoder().decode(base64Bytes)));
+        if (args.length < 2) {
+            data = null;
+        } else {
+            data = Files.readAllBytes(Paths.get(FILEPATH + args[1]));
+
+        }
+
+        SignerInfo[] signerInfos = pkcs7.verify(data);
+
+        if (signerInfos == null) {
+            throw new RuntimeException("no signers verify");
+        }
+        System.out.println("Verifying SignerInfos:");
+        for (SignerInfo signerInfo : signerInfos) {
+            System.out.println(signerInfo.toString());
+        }
+
+        X509Certificate certs[] = pkcs7.getCertificates();
+
+        HashMap<String, X509Certificate> certTable = new HashMap(certs.length);
+        for (X509Certificate cert : certs) {
+            certTable.put(cert.getSubjectDN().toString(), cert);
+        }
+
+        // try to verify all the certs
+        for (Map.Entry<String, X509Certificate> entry : certTable.entrySet()) {
+
+            X509Certificate cert = entry.getValue();
+            X509Certificate issuerCert = certTable
+                    .get(cert.getIssuerDN().toString());
+
+            System.out.println("Subject: " + cert.getSubjectDN());
+            if (issuerCert == null) {
+                System.out.println("Issuer certificate not found");
+            } else {
+                System.out.println("Issuer:  " + cert.getIssuerDN());
+                cert.verify(issuerCert.getPublicKey());
+                System.out.println("Cert verifies.");
+            }
+            System.out.println();
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/pkcs/pkcs7/SignerOrder.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,274 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8048357
+ * @summary test PKCS7 data signing, encoding and verification
+ * @modules java.base/sun.security.pkcs
+ *          java.base/sun.security.util
+ *          java.base/sun.security.x509
+ * @run main SignerOrder
+ */
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.PrivateKey;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.security.cert.X509Certificate;
+import java.util.Date;
+import sun.misc.HexDumpEncoder;
+import sun.security.pkcs.ContentInfo;
+import sun.security.pkcs.PKCS7;
+import sun.security.pkcs.SignerInfo;
+import sun.security.util.DerOutputStream;
+import sun.security.x509.AlgorithmId;
+import sun.security.x509.CertificateAlgorithmId;
+import sun.security.x509.CertificateSerialNumber;
+import sun.security.x509.CertificateValidity;
+import sun.security.x509.CertificateVersion;
+import sun.security.x509.CertificateX509Key;
+import sun.security.x509.X500Name;
+import sun.security.x509.X509CertImpl;
+import sun.security.x509.X509CertInfo;
+import sun.security.x509.X509Key;
+
+public class SignerOrder {
+
+    static final HexDumpEncoder hexDump = new HexDumpEncoder();
+
+    //signer infos
+    static final byte[] data1 = "12345".getBytes();
+    static final byte[] data2 = "abcde".getBytes();
+
+    public static void main(String[] argv) throws Exception {
+
+        SignerInfo[] signerInfos = new SignerInfo[9];
+        SimpleSigner signer1 = new SimpleSigner(null, null, null, null);
+        signerInfos[8] = signer1.genSignerInfo(data1);
+        signerInfos[7] = signer1.genSignerInfo(new byte[]{});
+        signerInfos[6] = signer1.genSignerInfo(data2);
+
+        SimpleSigner signer2 = new SimpleSigner(null, null, null, null);
+        signerInfos[5] = signer2.genSignerInfo(data1);
+        signerInfos[4] = signer2.genSignerInfo(new byte[]{});
+        signerInfos[3] = signer2.genSignerInfo(data2);
+
+        SimpleSigner signer3 = new SimpleSigner(null, null, null, null);
+        signerInfos[2] = signer3.genSignerInfo(data1);
+        signerInfos[1] = signer3.genSignerInfo(new byte[]{});
+        signerInfos[0] = signer3.genSignerInfo(data2);
+
+        ContentInfo contentInfo = new ContentInfo(data1);
+
+        AlgorithmId[] algIds = {new AlgorithmId(AlgorithmId.SHA256_oid)};
+
+        X509Certificate[] certs = {signer3.getCert(), signer2.getCert(),
+            signer1.getCert()};
+
+        PKCS7 pkcs71 = new PKCS7(algIds, contentInfo,
+                certs,
+                signerInfos);
+
+        System.out.println("SignerInfos in original.");
+        printSignerInfos(pkcs71.getSignerInfos());
+
+        DerOutputStream out = new DerOutputStream();
+        pkcs71.encodeSignedData(out);
+
+        PKCS7 pkcs72 = new PKCS7(out.toByteArray());
+        System.out.println("\nSignerInfos read back in:");
+        printSignerInfos(pkcs72.getSignerInfos());
+
+        System.out.println("Verified signers of original:");
+        SignerInfo[] verifs1 = pkcs71.verify();
+
+        System.out.println("Verified signers of after read-in:");
+        SignerInfo[] verifs2 = pkcs72.verify();
+
+        if (verifs1.length != verifs2.length) {
+            throw new RuntimeException("Length or Original vs read-in "
+                    + "should be same");
+        }
+    }
+
+    static void printSignerInfos(SignerInfo signerInfo) throws IOException {
+        ByteArrayOutputStream strm = new ByteArrayOutputStream();
+        signerInfo.derEncode(strm);
+        System.out.println("SignerInfo, length: "
+                + strm.toByteArray().length);
+        System.out.println(hexDump.encode(strm.toByteArray()));
+        System.out.println("\n");
+        strm.reset();
+    }
+
+    static void printSignerInfos(SignerInfo[] signerInfos) throws IOException {
+        ByteArrayOutputStream strm = new ByteArrayOutputStream();
+        for (int i = 0; i < signerInfos.length; i++) {
+            signerInfos[i].derEncode(strm);
+            System.out.println("SignerInfo[" + i + "], length: "
+                    + strm.toByteArray().length);
+            System.out.println(hexDump.encode(strm.toByteArray()));
+            System.out.println("\n");
+            strm.reset();
+        }
+    }
+
+}
+
+/**
+ * A simple extension of sun.security.x509.X500Signer that adds a no-fuss
+ * signing algorithm.
+ */
+class SimpleSigner {
+
+    private final Signature sig;
+    private final X500Name agent;
+    private final AlgorithmId digestAlgId;
+    private final AlgorithmId encryptionAlgId;
+    private final AlgorithmId algId; // signature algid;
+                                     //combines digest + encryption
+    private final X509Key publicKey;
+    private final PrivateKey privateKey;
+    private final X509Certificate cert;
+
+    public SimpleSigner(String digestAlg,
+            String encryptionAlg,
+            KeyPair keyPair,
+            X500Name agent) throws Exception {
+
+        if (agent == null) {
+            agent = new X500Name("cn=test");
+        }
+        if (digestAlg == null) {
+            digestAlg = "SHA";
+        }
+        if (encryptionAlg == null) {
+            encryptionAlg = "DSA";
+        }
+        if (keyPair == null) {
+            KeyPairGenerator keyGen =
+                    KeyPairGenerator.getInstance(encryptionAlg);
+            keyGen.initialize(1024);
+            keyPair = keyGen.generateKeyPair();
+        }
+        publicKey = (X509Key) keyPair.getPublic();
+        privateKey = keyPair.getPrivate();
+
+        if ("DSA".equals(encryptionAlg)) {
+            this.sig = Signature.getInstance(encryptionAlg);
+        } else { // RSA
+            this.sig = Signature.getInstance(digestAlg + "/" + encryptionAlg);
+        }
+        this.sig.initSign(privateKey);
+
+        this.agent = agent;
+        this.digestAlgId = AlgorithmId.get(digestAlg);
+        this.encryptionAlgId = AlgorithmId.get(encryptionAlg);
+        this.algId = AlgorithmId.get(this.sig.getAlgorithm());
+
+        this.cert = getSelfCert();
+    }
+
+    /**
+     * Take the data and sign it.
+     *
+     * @param buf buffer holding the next chunk of the data to be signed
+     * @param offset starting point of to-be-signed data
+     * @param len how many bytes of data are to be signed
+     * @return the signature for the input data.
+     * @exception SignatureException on errors.
+     */
+    public byte[] simpleSign(byte[] buf, int offset, int len)
+            throws SignatureException {
+        sig.update(buf, offset, len);
+        return sig.sign();
+    }
+
+    /**
+     * Returns the digest algorithm used to sign.
+     */
+    public AlgorithmId getDigestAlgId() {
+        return digestAlgId;
+    }
+
+    /**
+     * Returns the encryption algorithm used to sign.
+     */
+    public AlgorithmId getEncryptionAlgId() {
+        return encryptionAlgId;
+    }
+
+    /**
+     * Returns the name of the signing agent.
+     */
+    public X500Name getSigner() {
+        return agent;
+    }
+
+    public X509Certificate getCert() {
+        return cert;
+    }
+
+    private X509Certificate getSelfCert() throws Exception {
+        long validity = 1000;
+        X509CertImpl certLocal;
+        Date firstDate, lastDate;
+
+        firstDate = new Date();
+        lastDate = new Date();
+        lastDate.setTime(lastDate.getTime() + validity + 1000);
+
+        CertificateValidity interval = new CertificateValidity(firstDate,
+                lastDate);
+
+        X509CertInfo info = new X509CertInfo();
+        // Add all mandatory attributes
+        info.set(X509CertInfo.VERSION,
+                new CertificateVersion(CertificateVersion.V1));
+        info.set(X509CertInfo.SERIAL_NUMBER,
+                new CertificateSerialNumber(
+                        (int) (firstDate.getTime() / 1000)));
+        info.set(X509CertInfo.ALGORITHM_ID,
+                new CertificateAlgorithmId(algId));
+        info.set(X509CertInfo.SUBJECT, agent);
+        info.set(X509CertInfo.KEY, new CertificateX509Key(publicKey));
+        info.set(X509CertInfo.VALIDITY, interval);
+        info.set(X509CertInfo.ISSUER, agent);
+
+        certLocal = new X509CertImpl(info);
+        certLocal.sign(privateKey, algId.getName());
+
+        return certLocal;
+    }
+
+    public SignerInfo genSignerInfo(byte[] data) throws SignatureException {
+        return new SignerInfo((X500Name) cert.getIssuerDN(),
+                new BigInteger("" + cert.getSerialNumber()),
+                getDigestAlgId(), algId,
+                simpleSign(data, 0, data.length));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/pkcs/pkcs7/jarsigner/META-INF/MANIFEST.MF	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,82 @@
+Manifest-Version: 1.0
+
+Name: CheckCerts.class
+Digest-Algorithms: SHA
+SHA-Digest: xLygljhRro6990piIVEilVI8szQ=
+
+Name: ContentInfoTest.class
+Digest-Algorithms: SHA
+SHA-Digest: TSVdEMQW2gdFi6qeba+UixdHSdo=
+
+Name: JarVerify.class
+Digest-Algorithms: SHA
+SHA-Digest: Wg+PiDzunNGH4KrWAp00/okp39s=
+
+Name: JarVerify2.class
+Digest-Algorithms: SHA
+SHA-Digest: 5uYBQxwGWgYmNBwhnWRbymeXmWM=
+
+Name: PKCS7Read.class
+Digest-Algorithms: SHA
+SHA-Digest: JPIxttHBfRpQaFyiQJ2Wfkvj/ls=
+
+Name: PKCS7Test.class
+Digest-Algorithms: SHA
+SHA-Digest: R64SXXgZrOvGiO/eMsfG/T1Vn30=
+
+Name: PKCS7Test10.class
+Digest-Algorithms: SHA
+SHA-Digest: 2R0yxuxRHTPqdAzJJcrvqkpbQgo=
+
+Name: PKCS7Test11.class
+Digest-Algorithms: SHA
+SHA-Digest: /0HcwnpQi0hwJsJtvt5peWFGvtc=
+
+Name: PKCS7Test12.class
+Digest-Algorithms: SHA
+SHA-Digest: s5CcqimfRqR9CW25tFBY0JK3RVU=
+
+Name: PKCS7Test2.class
+Digest-Algorithms: SHA
+SHA-Digest: 71VkFEMUle5sjXNFbSW31F1ZJ58=
+
+Name: PKCS7Test3.class
+Digest-Algorithms: SHA
+SHA-Digest: mU/D5C6SgPRmwoLQzwF5VnN3aqM=
+
+Name: PKCS7Test4.class
+Digest-Algorithms: SHA
+SHA-Digest: ss9NFvxF8emaEjdKdvtzWXfs0/E=
+
+Name: PKCS7Test5.class
+Digest-Algorithms: SHA
+SHA-Digest: DHvQ20UAXoYgfCPAOeCOrglsJwU=
+
+Name: PKCS7Test6.class
+Digest-Algorithms: SHA
+SHA-Digest: aiCb8chroH7XDaNfAz6wr57lXsA=
+
+Name: PKCS7Test7.class
+Digest-Algorithms: SHA
+SHA-Digest: UoieXLC68alFgfD/Q1NW9/r2kaY=
+
+Name: PKCS7Test8.class
+Digest-Algorithms: SHA
+SHA-Digest: eMW7mq5b/KVB1M5L76wcV1+uFQs=
+
+Name: PKCS7Test9.class
+Digest-Algorithms: SHA
+SHA-Digest: EEWCZG1creWjqVZVIEgr0on3y6A=
+
+Name: SignerInfoTest.class
+Digest-Algorithms: SHA
+SHA-Digest: l6SNfpnFipGg8gy4XqY3HhA0RrY=
+
+Name: SignerInfoTest2.class
+Digest-Algorithms: SHA
+SHA-Digest: 5jbzlkZqXKNmmmE+pcjQka8D6WE=
+
+Name: SimpleSigner.class
+Digest-Algorithms: SHA
+SHA-Digest: l9ODQHY4wxhIvLw4/B0qe9NjwxQ=
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/pkcs/pkcs7/jarsigner/META-INF/PKCS7TEST.DSA.base64	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,60 @@
+MIILKAYJKoZIhvcNAQcCoIILGTCCCxUCAQExCzAJBgUrDgMCGgUAMIIHbQYJKoZI
+hvcNAQcBoIIHXgSCB1pTaWduYXR1cmUtVmVyc2lvbjogMS4wDQoNCk5hbWU6IENo
+ZWNrQ2VydHMuY2xhc3MNCkRpZ2VzdC1BbGdvcml0aG1zOiBTSEENClNIQS1EaWdl
+c3Q6IHlhMXh3dnNRTytEUnBRYnczRmgyblJCMkpRYz0NCg0KTmFtZTogQ29udGVu
+dEluZm9UZXN0LmNsYXNzDQpEaWdlc3QtQWxnb3JpdGhtczogU0hBDQpTSEEtRGln
+ZXN0OiBDYStFSmFrVTZ6dzRLQWhvcWNuQ3BOcWsyTEk9DQoNCk5hbWU6IEphclZl
+cmlmeS5jbGFzcw0KRGlnZXN0LUFsZ29yaXRobXM6IFNIQQ0KU0hBLURpZ2VzdDog
+K0RHYVdXa25md2U0Wk9wc29NVEZ6ZldSdmhRPQ0KDQpOYW1lOiBKYXJWZXJpZnky
+LmNsYXNzDQpEaWdlc3QtQWxnb3JpdGhtczogU0hBDQpTSEEtRGlnZXN0OiBHcUR6
+WXlZNFAvV0g1SEt2aVdxWHR0UGc1ckU9DQoNCk5hbWU6IFBLQ1M3UmVhZC5jbGFz
+cw0KRGlnZXN0LUFsZ29yaXRobXM6IFNIQQ0KU0hBLURpZ2VzdDogUW1mOEs5aFhW
+bHdJZFBZNm52MmpGUGZHcWtBPQ0KDQpOYW1lOiBQS0NTN1Rlc3QuY2xhc3MNCkRp
+Z2VzdC1BbGdvcml0aG1zOiBTSEENClNIQS1EaWdlc3Q6IEdiZS9nenl2MkY1OGY2
+RUVoU1oxQnFHWHRsbz0NCg0KTmFtZTogUEtDUzdUZXN0MTAuY2xhc3MNCkRpZ2Vz
+dC1BbGdvcml0aG1zOiBTSEENClNIQS1EaWdlc3Q6IDh3QnFXLy9lVzJzTlJJOTFi
+TFlFT29kY2dhRT0NCg0KTmFtZTogUEtDUzdUZXN0MTEuY2xhc3MNCkRpZ2VzdC1B
+bGdvcml0aG1zOiBTSEENClNIQS1EaWdlc3Q6IGJYaExLRXNsY3VFWGk0dS9haGdU
+MnE2dGNFVT0NCg0KTmFtZTogUEtDUzdUZXN0MTIuY2xhc3MNCkRpZ2VzdC1BbGdv
+cml0aG1zOiBTSEENClNIQS1EaWdlc3Q6IDlLRVkxYjUyUUxtTjBxei81ejB3QkZy
+T216MD0NCg0KTmFtZTogUEtDUzdUZXN0Mi5jbGFzcw0KRGlnZXN0LUFsZ29yaXRo
+bXM6IFNIQQ0KU0hBLURpZ2VzdDogK1VhMzIvMlE4RjJiclFRbVNYWCtYUytNL2g0
+PQ0KDQpOYW1lOiBQS0NTN1Rlc3QzLmNsYXNzDQpEaWdlc3QtQWxnb3JpdGhtczog
+U0hBDQpTSEEtRGlnZXN0OiAwSFhVWnlhU2ZkZUtlZThuWnpFalJTeXJldTQ9DQoN
+Ck5hbWU6IFBLQ1M3VGVzdDQuY2xhc3MNCkRpZ2VzdC1BbGdvcml0aG1zOiBTSEEN
+ClNIQS1EaWdlc3Q6IEo3eXJTMjRvS3VTZ2F1dHZkemhxQmo3ZGJjUT0NCg0KTmFt
+ZTogUEtDUzdUZXN0NS5jbGFzcw0KRGlnZXN0LUFsZ29yaXRobXM6IFNIQQ0KU0hB
+LURpZ2VzdDogSlR2OVdTb3gxTEVTUjJMcTdzMFVxU2x0RFNRPQ0KDQpOYW1lOiBQ
+S0NTN1Rlc3Q2LmNsYXNzDQpEaWdlc3QtQWxnb3JpdGhtczogU0hBDQpTSEEtRGln
+ZXN0OiBnR3Yra05oK3UzSFExdHp4bGNBVzdTcEZUS2s9DQoNCk5hbWU6IFBLQ1M3
+VGVzdDcuY2xhc3MNCkRpZ2VzdC1BbGdvcml0aG1zOiBTSEENClNIQS1EaWdlc3Q6
+IGZpSEYxYUExYWN6czFPd0V5OEc3VkMrcjdMST0NCg0KTmFtZTogUEtDUzdUZXN0
+OC5jbGFzcw0KRGlnZXN0LUFsZ29yaXRobXM6IFNIQQ0KU0hBLURpZ2VzdDogNzRU
+VzdJOVZPdzVWZ0x2aFJtRGZxRVd2ZkFRPQ0KDQpOYW1lOiBQS0NTN1Rlc3Q5LmNs
+YXNzDQpEaWdlc3QtQWxnb3JpdGhtczogU0hBDQpTSEEtRGlnZXN0OiAxY0JJbkdU
+Y08xQVFaKy8wdmhGa2laV3dsQTA9DQoNCk5hbWU6IFNpZ25lckluZm9UZXN0LmNs
+YXNzDQpEaWdlc3QtQWxnb3JpdGhtczogU0hBDQpTSEEtRGlnZXN0OiBjRlk0Q3RT
+anphMUErV2pBS05TVnF1cGpSWUU9DQoNCk5hbWU6IFNpZ25lckluZm9UZXN0Mi5j
+bGFzcw0KRGlnZXN0LUFsZ29yaXRobXM6IFNIQQ0KU0hBLURpZ2VzdDogYU5NMEZQ
+MHpFelF6eGxYeDZxQ0J4dWtta0hRPQ0KDQpOYW1lOiBTaW1wbGVTaWduZXIuY2xh
+c3MNCkRpZ2VzdC1BbGdvcml0aG1zOiBTSEENClNIQS1EaWdlc3Q6IC9MV0NzbkM3
+TVpNUjZHb3czeTJjdnA3STBTTT0NCg0KoIICvzCCArswggJ3AgUA59UzNDALBgcq
+hkjOOAQDBQAwdTELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRIwEAYDVQQHEwlD
+dXBlcnRpbm8xGTAXBgNVBAoTEFN1biBNaWNyb3N5c3RlbXMxETAPBgNVBAsTCEph
+dmFTb2Z0MRcwFQYDVQQDEw5Eb3VnbGFzIEhvb3ZlcjAeFw05NzEwMDIxODEyMDda
+Fw05NzEyMzExNzEyMDdaMHUxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTESMBAG
+A1UEBxMJQ3VwZXJ0aW5vMRkwFwYDVQQKExBTdW4gTWljcm9zeXN0ZW1zMREwDwYD
+VQQLEwhKYXZhU29mdDEXMBUGA1UEAxMORG91Z2xhcyBIb292ZXIwggFRMIHoBgcq
+hkjOOAQBMIHcAmEA6eZCWZ01XzfJf/01ZxILjiXJzUPpJ7OpZw++xdiQFBki0sOz
+rSSACTeZhp0ehGqrSfqwrSbSzmoiIZ1HC859d31KIfvpwnC1f2BwAvPO+Dk2lM9F
+7jaIwRqMVqsSej2vAhUAnNvYTJ8awvOND4D0KrlS5zOL9RECYDBHCtWgBfsUzi2d
+zYfji8fRscX6y67L6V8ZCqejHSPE27y+BhdFREAaWywCCWXYwr0hcdNmhEV3H3S6
+CE0gKdg8HBWFR/Op8aJxW+I9Ua5NPlofanBk8xaTOjRtP1KSUgNkAAJhAMN5uB+B
+ZJ0W2UjXMyKoFUFXRYiLpnaSw63kl9tKnR9R5rEreiyHQ5IelPxjwCHGgTbYK0y+
+xKTGHVWiQN/YJmHLbSrcSSM/d89aR/sVbGoAwQOyYraFGUNIOTQjjXcXCjALBgcq
+hkjOOAQDBQADMQAwLgIVAJxmL029GLXDJVbk72d4cSPQ4/rvAhUAll9UPl8aOMEg
+V4egANhwbynMGSgxgc4wgcsCAQEwfjB1MQswCQYDVQQGEwJVUzELMAkGA1UECBMC
+Q0ExEjAQBgNVBAcTCUN1cGVydGlubzEZMBcGA1UEChMQU3VuIE1pY3Jvc3lzdGVt
+czERMA8GA1UECxMISmF2YVNvZnQxFzAVBgNVBAMTDkRvdWdsYXMgSG9vdmVyAgUA
+59UzNDAJBgUrDgMCGgUAMAsGByqGSM44BAMFAAQuMCwCFDmry17kzDD6Y5X1BqIS
+lq6swckPAhRtiXvBHa5CRGjbwk8yqf9hGgZfFA==
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/pkcs/pkcs7/jarsigner/META-INF/PKCS7TEST.SF	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,82 @@
+Signature-Version: 1.0
+
+Name: CheckCerts.class
+Digest-Algorithms: SHA
+SHA-Digest: ya1xwvsQO+DRpQbw3Fh2nRB2JQc=
+
+Name: ContentInfoTest.class
+Digest-Algorithms: SHA
+SHA-Digest: Ca+EJakU6zw4KAhoqcnCpNqk2LI=
+
+Name: JarVerify.class
+Digest-Algorithms: SHA
+SHA-Digest: +DGaWWknfwe4ZOpsoMTFzfWRvhQ=
+
+Name: JarVerify2.class
+Digest-Algorithms: SHA
+SHA-Digest: GqDzYyY4P/WH5HKviWqXttPg5rE=
+
+Name: PKCS7Read.class
+Digest-Algorithms: SHA
+SHA-Digest: Qmf8K9hXVlwIdPY6nv2jFPfGqkA=
+
+Name: PKCS7Test.class
+Digest-Algorithms: SHA
+SHA-Digest: Gbe/gzyv2F58f6EEhSZ1BqGXtlo=
+
+Name: PKCS7Test10.class
+Digest-Algorithms: SHA
+SHA-Digest: 8wBqW//eW2sNRI91bLYEOodcgaE=
+
+Name: PKCS7Test11.class
+Digest-Algorithms: SHA
+SHA-Digest: bXhLKEslcuEXi4u/ahgT2q6tcEU=
+
+Name: PKCS7Test12.class
+Digest-Algorithms: SHA
+SHA-Digest: 9KEY1b52QLmN0qz/5z0wBFrOmz0=
+
+Name: PKCS7Test2.class
+Digest-Algorithms: SHA
+SHA-Digest: +Ua32/2Q8F2brQQmSXX+XS+M/h4=
+
+Name: PKCS7Test3.class
+Digest-Algorithms: SHA
+SHA-Digest: 0HXUZyaSfdeKee8nZzEjRSyreu4=
+
+Name: PKCS7Test4.class
+Digest-Algorithms: SHA
+SHA-Digest: J7yrS24oKuSgautvdzhqBj7dbcQ=
+
+Name: PKCS7Test5.class
+Digest-Algorithms: SHA
+SHA-Digest: JTv9WSox1LESR2Lq7s0UqSltDSQ=
+
+Name: PKCS7Test6.class
+Digest-Algorithms: SHA
+SHA-Digest: gGv+kNh+u3HQ1tzxlcAW7SpFTKk=
+
+Name: PKCS7Test7.class
+Digest-Algorithms: SHA
+SHA-Digest: fiHF1aA1aczs1OwEy8G7VC+r7LI=
+
+Name: PKCS7Test8.class
+Digest-Algorithms: SHA
+SHA-Digest: 74TW7I9VOw5VgLvhRmDfqEWvfAQ=
+
+Name: PKCS7Test9.class
+Digest-Algorithms: SHA
+SHA-Digest: 1cBInGTcO1AQZ+/0vhFkiZWwlA0=
+
+Name: SignerInfoTest.class
+Digest-Algorithms: SHA
+SHA-Digest: cFY4CtSjza1A+WjAKNSVqupjRYE=
+
+Name: SignerInfoTest2.class
+Digest-Algorithms: SHA
+SHA-Digest: aNM0FP0zEzQzxlXx6qCBxukmkHQ=
+
+Name: SimpleSigner.class
+Digest-Algorithms: SHA
+SHA-Digest: /LWCsnC7MZMR6Gow3y2cvp7I0SM=
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/pkcs/pkcs8/PKCS8Test.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,294 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 8048357
+ * @summary PKCS8 Standards Conformance Tests
+ * @modules java.base/sun.security.pkcs
+ *          java.base/sun.security.util
+ *          java.base/sun.security.provider
+ *          java.base/sun.security.x509
+ *          java.base/sun.misc
+ * @compile -XDignore.symbol.file PKCS8Test.java
+ * @run main PKCS8Test
+ */
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.InvalidKeyException;
+import java.util.Arrays;
+import sun.misc.HexDumpEncoder;
+import sun.security.pkcs.PKCS8Key;
+import sun.security.provider.DSAPrivateKey;
+import sun.security.util.DerOutputStream;
+import sun.security.util.DerValue;
+import sun.security.x509.AlgorithmId;
+
+import static java.lang.System.out;
+
+public class PKCS8Test {
+
+    static final HexDumpEncoder hexDump = new HexDumpEncoder();
+
+    static final DerOutputStream derOutput = new DerOutputStream();
+
+    static final String FORMAT = "PKCS#8";
+    static final String EXPECTED_ALG_ID_CHRS = "DSA\n\tp:     02\n\tq:     03\n"
+            + "\tg:     04\n";
+    static final String ALGORITHM = "DSA";
+    static final String EXCEPTION_MESSAGE = "version mismatch: (supported:     "
+            + "00, parsed:     01";
+
+    // test second branch in byte[] encode()
+    // DER encoding,include (empty) set of attributes
+    static final int[] NEW_ENCODED_KEY_INTS = { 0x30,
+            // length 30 = 0x1e
+            0x1e,
+            // first element
+            // version Version (= INTEGER)
+            0x02,
+            // length 1
+            0x01,
+            // value 0
+            0x00,
+            // second element
+            // privateKeyAlgorithmIdentifier PrivateKeyAlgorithmIdentifier
+            // (sequence)
+            // (an object identifier?)
+            0x30,
+            // length 18
+            0x12,
+            // contents
+            // object identifier, 5 bytes
+            0x06, 0x05,
+            // { 1 3 14 3 2 12 }
+            0x2b, 0x0e, 0x03, 0x02, 0x0c,
+            // sequence, 9 bytes
+            0x30, 0x09,
+            // integer 2
+            0x02, 0x01, 0x02,
+            // integer 3
+            0x02, 0x01, 0x03,
+            // integer 4
+            0x02, 0x01, 0x04,
+            // third element
+            // privateKey PrivateKey (= OCTET STRING)
+            0x04,
+            // length
+            0x03,
+            // privateKey contents
+            0x02, 0x01, 0x01,
+            // 4th (optional) element -- attributes [0] IMPLICIT Attributes
+            // OPTIONAL
+            // (Attributes = SET OF Attribute) Here, it will be empty.
+            0xA0,
+            // length
+            0x00 };
+
+    // encoding originally created, but with the version changed
+    static final int[] NEW_ENCODED_KEY_INTS_2 = {
+            // sequence
+            0x30,
+            // length 28 = 0x1c
+            0x1c,
+            // first element
+            // version Version (= INTEGER)
+            0x02,
+            // length 1
+            0x01,
+            // value 1 (illegal)
+            0x01,
+            // second element
+            // privateKeyAlgorithmIdentifier PrivateKeyAlgorithmIdentifier
+            // (sequence)
+            // (an object identifier?)
+            0x30,
+            // length 18
+            0x12,
+            // contents
+            // object identifier, 5 bytes
+            0x06, 0x05,
+            // { 1 3 14 3 2 12 }
+            0x2b, 0x0e, 0x03, 0x02, 0x0c,
+            // sequence, 9 bytes
+            0x30, 0x09,
+            // integer 2
+            0x02, 0x01, 0x02,
+            // integer 3
+            0x02, 0x01, 0x03,
+            // integer 4
+            0x02, 0x01, 0x04,
+            // third element
+            // privateKey PrivateKey (= OCTET STRING)
+            0x04,
+            // length
+            0x03,
+            // privateKey contents
+            0x02, 0x01, 0x01 };
+
+    // 0000: 30 1E 02 01 00 30 14 06 07 2A 86 48 CE 38 04 01 0....0...*.H.8..
+    // 0010: 30 09 02 01 02 02 01 03 02 01 04 04 03 02 01 01 0...............
+    static final int[] EXPECTED = { 0x30,
+            // length 30 = 0x1e
+            0x1e,
+            // first element
+            // version Version (= INTEGER)
+            0x02,
+            // length 1
+            0x01,
+            // value 0
+            0x00,
+            // second element
+            // privateKeyAlgorithmIdentifier PrivateKeyAlgorithmIdentifier
+            // (sequence)
+            // (an object identifier?)
+            0x30, 0x14, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x38, 0x04, 0x01,
+            // integer 2
+            0x30, 0x09, 0x02,
+            // integer 3
+            0x01, 0x02, 0x02,
+            // integer 4
+            0x01, 0x03, 0x02,
+            // third element
+            // privateKey PrivateKey (= OCTET STRING)
+            0x01,
+            // length
+            0x04,
+            // privateKey contents
+            0x04, 0x03, 0x02,
+            // 4th (optional) element -- attributes [0] IMPLICIT Attributes
+            // OPTIONAL
+            // (Attributes = SET OF Attribute) Here, it will be empty.
+            0x01,
+            // length
+            0x01 };
+
+    static void raiseException(String expected, String received) {
+        throw new RuntimeException(
+                "Expected " + expected + "; Received " + received);
+    }
+
+    public static void main(String[] args)
+            throws IOException, InvalidKeyException {
+
+        byte[] encodedKey = getEncodedKey();
+        byte[] expectedBytes = new byte[EXPECTED.length];
+        for (int i = 0; i < EXPECTED.length; i++) {
+            expectedBytes[i] = (byte) EXPECTED[i];
+        }
+
+        dumpByteArray("encodedKey :", encodedKey);
+        if (!Arrays.equals(encodedKey, expectedBytes)) {
+            raiseException(new String(expectedBytes), new String(encodedKey));
+        }
+
+        PKCS8Key decodedKey = PKCS8Key.parse(new DerValue(encodedKey));
+        String alg = decodedKey.getAlgorithm();
+        AlgorithmId algId = decodedKey.getAlgorithmId();
+        out.println("Algorithm :" + alg);
+        out.println("AlgorithmId: " + algId);
+
+        if (!ALGORITHM.equals(alg)) {
+            raiseException(ALGORITHM, alg);
+        }
+        if (!EXPECTED_ALG_ID_CHRS.equalsIgnoreCase(algId.toString())) {
+            raiseException(EXPECTED_ALG_ID_CHRS, algId.toString());
+        }
+
+        decodedKey.encode(derOutput);
+        dumpByteArray("Stream encode: ", derOutput.toByteArray());
+        if (!Arrays.equals(derOutput.toByteArray(), expectedBytes)) {
+            raiseException(new String(expectedBytes), derOutput.toString());
+        }
+
+        dumpByteArray("byte[] encoding: ", decodedKey.getEncoded());
+        if (!Arrays.equals(decodedKey.getEncoded(), expectedBytes)) {
+            raiseException(new String(expectedBytes),
+                    new String(decodedKey.getEncoded()));
+        }
+
+        if (!FORMAT.equals(decodedKey.getFormat())) {
+            raiseException(FORMAT, decodedKey.getFormat());
+        }
+
+        try {
+            byte[] newEncodedKey = new byte[NEW_ENCODED_KEY_INTS.length];
+            for (int i = 0; i < newEncodedKey.length; i++) {
+                newEncodedKey[i] = (byte) NEW_ENCODED_KEY_INTS[i];
+            }
+            PKCS8Key newDecodedKey = PKCS8Key
+                    .parse(new DerValue(newEncodedKey));
+
+            throw new RuntimeException(
+                    "key1: Expected an IOException during " + "parsing");
+        } catch (IOException e) {
+            System.out.println("newEncodedKey: should have excess data due to "
+                    + "attributes, which are not supported");
+        }
+
+        try {
+            byte[] newEncodedKey2 = new byte[NEW_ENCODED_KEY_INTS_2.length];
+            for (int i = 0; i < newEncodedKey2.length; i++) {
+                newEncodedKey2[i] = (byte) NEW_ENCODED_KEY_INTS_2[i];
+            }
+
+            PKCS8Key newDecodedKey2 = PKCS8Key
+                    .parse(new DerValue(newEncodedKey2));
+
+            throw new RuntimeException(
+                    "key2: Expected an IOException during " + "parsing");
+        } catch (IOException e) {
+            out.println("Key 2: should be illegal version");
+            out.println(e.getMessage());
+            if (!EXCEPTION_MESSAGE.equals(e.getMessage())) {
+                throw new RuntimeException("Key2: expected: "
+                        + EXCEPTION_MESSAGE + " get: " + e.getMessage());
+            }
+        }
+
+    }
+
+    // get a byte array from somewhere
+    static byte[] getEncodedKey() throws InvalidKeyException {
+        BigInteger p = BigInteger.valueOf(1);
+        BigInteger q = BigInteger.valueOf(2);
+        BigInteger g = BigInteger.valueOf(3);
+        BigInteger x = BigInteger.valueOf(4);
+
+        DSAPrivateKey priv = new DSAPrivateKey(p, q, g, x);
+        return priv.getEncoded();
+    }
+
+    static void dumpByteArray(String nm, byte[] bytes) throws IOException {
+        out.println(nm + " length: " + bytes.length);
+        hexDump.encodeBuffer(bytes, out);
+    }
+
+    static String toString(PKCS8Key key) {
+        StringBuilder builder = new StringBuilder(key.getAlgorithm());
+        builder.append('\n').append("parameters:")
+                .append(key.getAlgorithmId().toString());
+        return builder.toString();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/tools/jarsigner/Options.java	Mon Nov 23 14:37:04 2015 -0500
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @bug 8056174
+ * @summary Make sure the jarsigner tool still works after it's modified to
+ *          be based on JarSigner API
+ * @library /lib/testlibrary
+ * @modules java.base/sun.security.tools.keytool
+ *          jdk.jartool/sun.security.tools.jarsigner
+ *          java.base/sun.security.pkcs
+ *          java.base/sun.security.x509
+ */
+
+import com.sun.jarsigner.ContentSigner;
+import com.sun.jarsigner.ContentSignerParameters;
+import jdk.testlibrary.JarUtils;
+import sun.security.pkcs.PKCS7;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateException;
+import java.util.*;
+import java.util.jar.Attributes;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.jar.Manifest;
+
+public class Options {
+
+    public static void main(String[] args) throws Exception {
+
+        // Prepares raw file
+        Files.write(Paths.get("a"), "a".getBytes());
+
+        // Pack
+        JarUtils.createJar("a.jar", "a");
+
+        // Prepare a keystore
+        sun.security.tools.keytool.Main.main(
+                ("-keystore jks -storepass changeit -keypass changeit -dname" +
+                        " CN=A -alias a -genkeypair -keyalg rsa").split(" "));
+
+        // -altsign
+        sun.security.tools.jarsigner.Main.main(
+                ("-debug -signedjar altsign.jar -keystore jks -storepass changeit" +
+                        " -altsigner Options$X a.jar a").split(" "));
+
+        try (JarFile jf = new JarFile("altsign.jar")) {
+            JarEntry je = jf.getJarEntry("META-INF/A.RSA");
+            try (InputStream is = jf.getInputStream(je)) {
+                if (!Arrays.equals(is.readAllBytes(), "1234".getBytes())) {
+                    throw new Exception("altsign go wrong");
+                }
+            }
+        }
+
+        // -sigfile, -digestalg, -sigalg, -internalsf, -sectionsonly
+        sun.security.tools.jarsigner.Main.main(
+                ("-debug -signedjar new.jar -keystore jks -storepass changeit" +
+                " -sigfile olala -digestalg SHA1 -sigalg SHA224withRSA" +
+                " -internalsf -sectionsonly a.jar a").split(" "));
+
+        try (JarFile jf = new JarFile("new.jar")) {
+            JarEntry je = jf.getJarEntry("META-INF/OLALA.SF");
+            Objects.requireNonNull(je);     // check -sigfile
+            byte[] sf = null;               // content of .SF
+            try (InputStream is = jf.getInputStream(je)) {
+                sf = is.readAllBytes();     // save for later comparison
+                Attributes attrs = new Manifest(new ByteArrayInputStream(sf))
+                        .getMainAttributes();
+                // check -digestalg
+                if (!attrs.containsKey(new Attributes.Name(
+                        "SHA1-Digest-Manifest-Main-Attributes"))) {
+                    throw new Exception("digestalg incorrect");
+                }
+                // check -sectionsonly
+                if (attrs.containsKey(new Attributes.Name(
+                        "SHA1-Digest-Manifest"))) {
+                    throw new Exception("SF should not have file digest");
+                }
+            }
+
+            je = jf.getJarEntry("META-INF/OLALA.RSA");
+            try (InputStream is = jf.getInputStream(je)) {
+                PKCS7 p7 = new PKCS7(is.readAllBytes());
+                String alg = p7.getSignerInfos()[0]
+                        .getDigestAlgorithmId().getName();
+                if (!alg.equals("SHA-224")) {   // check -sigalg
+                    throw new Exception("PKCS7 signing is using " + alg);
+                }
+                // check -internalsf
+                if (!Arrays.equals(sf, p7.getContentInfo().getData())) {
+                    throw new Exception("SF not in RSA");
+                }
+            }
+
+        }
+
+        // TSA-related ones are checked in ts.sh
+    }
+
+    public static class X extends ContentSigner {
+        @Override
+        public byte[] generateSignedData(ContentSignerParameters parameters,
+                boolean omitContent, boolean applyTimestamp)
+                throws NoSuchAlgorithmException, CertificateException,
+                        IOException {
+            return "1234".getBytes();
+        }
+    }
+}
--- a/jdk/test/sun/util/logging/PlatformLoggerTest.java	Thu Nov 19 18:18:05 2015 -0500
+++ b/jdk/test/sun/util/logging/PlatformLoggerTest.java	Mon Nov 23 14:37:04 2015 -0500
@@ -38,7 +38,6 @@
 import java.lang.reflect.Modifier;
 import java.util.logging.*;
 import sun.util.logging.PlatformLogger;
-import sun.util.logging.LoggingSupport;
 import static sun.util.logging.PlatformLogger.Level.*;
 
 public class PlatformLoggerTest {
@@ -195,7 +194,9 @@
         System.out.println("Testing Java Level with: " + level.getName());
 
         // create a brand new java logger
-        Logger javaLogger = (Logger) LoggingSupport.getLogger(logger.getName()+"."+level.getName());
+        Logger javaLogger = sun.util.logging.internal.LoggingProviderImpl.getLogManagerAccess()
+                     .demandLoggerFor(LogManager.getLogManager(),
+                          logger.getName()+"."+level.getName(), Thread.class);
 
         // Set a non standard java.util.logging.Level on the java logger
         // (except for OFF & ALL - which will remain unchanged)