Merge
authorjprovino
Mon, 07 Dec 2015 17:04:33 +0000
changeset 34598 24c22a1b14ff
parent 34489 c742968c922b (current diff)
parent 34597 e5f2344c6fab (diff)
child 34600 242c2cfbc666
Merge
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/lib/share/classes/jdk/test/lib/Asserts.java	Mon Dec 07 17:04:33 2015 +0000
@@ -0,0 +1,620 @@
+/*
+ * 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 jdk.test.lib;
+
+import java.util.Objects;
+
+/**
+ * Asserts that can be used for verifying assumptions in tests.
+ *
+ * An assertion will throw a {@link RuntimeException} if the assertion isn't true.
+ * All the asserts can be imported into a test by using a static import:
+ *
+ * <pre>
+ * {@code
+ * import static jdk.testlibrary.Asserts.*;
+ * }
+ *
+ * Always provide a message describing the assumption if the line number of the
+ * failing assertion isn't enough to understand why the assumption failed. For
+ * example, if the assertion is in a loop or in a method that is called
+ * multiple times, then the line number won't provide enough context to
+ * understand the failure.
+ * </pre>
+ */
+public class Asserts {
+
+    /**
+     * Shorthand for {@link #assertLessThan(Comparable, Comparable)}.
+     *
+     * @param <T> a type
+     * @param lhs The left hand side of the comparison.
+     * @param rhs The right hand side of the comparison.
+     * @see #assertLessThan(Comparable, Comparable)
+     */
+    public static <T extends Comparable<T>> void assertLT(T lhs, T rhs) {
+        assertLessThan(lhs, rhs);
+    }
+
+    /**
+     * Shorthand for {@link #assertLessThan(Comparable, Comparable, String)}.
+     *
+     * @param <T> a type
+     * @param lhs The left hand side of the comparison.
+     * @param rhs The right hand side of the comparison.
+     * @param msg A description of the assumption; {@code null} for a default message.
+     * @see #assertLessThan(Comparable, Comparable, String)
+     */
+    public static <T extends Comparable<T>> void assertLT(T lhs, T rhs, String msg) {
+        assertLessThan(lhs, rhs, msg);
+    }
+
+    /**
+     * Calls {@link #assertLessThan(Comparable, Comparable, String)} with a default message.
+     *
+     * @param <T> a type
+     * @param lhs The left hand side of the comparison.
+     * @param rhs The right hand side of the comparison.
+     * @see #assertLessThan(Comparable, Comparable, String)
+     */
+    public static <T extends Comparable<T>> void assertLessThan(T lhs, T rhs) {
+        assertLessThan(lhs, rhs, null);
+    }
+
+    /**
+     * Asserts that {@code lhs} is less than {@code rhs}.
+     *
+     * @param <T> a type
+     * @param lhs The left hand side of the comparison.
+     * @param rhs The right hand side of the comparison.
+     * @param msg A description of the assumption; {@code null} for a default message.
+     * @throws RuntimeException if the assertion is not true.
+     */
+    public static <T extends Comparable<T>>void assertLessThan(T lhs, T rhs, String msg) {
+        if (!(compare(lhs, rhs, msg) < 0)) {
+            msg = Objects.toString(msg, "assertLessThan")
+                    + ": expected that " + Objects.toString(lhs)
+                    + " < " + Objects.toString(rhs);
+            fail(msg);
+        }
+    }
+
+    /**
+     * Shorthand for {@link #assertLessThanOrEqual(Comparable, Comparable)}.
+     *
+     * @param <T> a type
+     * @param lhs The left hand side of the comparison.
+     * @param rhs The right hand side of the comparison.
+     * @see #assertLessThanOrEqual(Comparable, Comparable)
+     */
+    public static <T extends Comparable<T>> void assertLTE(T lhs, T rhs) {
+        assertLessThanOrEqual(lhs, rhs);
+    }
+
+    /**
+     * Shorthand for {@link #assertLessThanOrEqual(Comparable, Comparable, String)}.
+     *
+     * @param <T> a type
+     * @param lhs The left hand side of the comparison.
+     * @param rhs The right hand side of the comparison.
+     * @param msg A description of the assumption; {@code null} for a default message.
+     * @see #assertLessThanOrEqual(Comparable, Comparable, String)
+     */
+    public static <T extends Comparable<T>> void assertLTE(T lhs, T rhs, String msg) {
+        assertLessThanOrEqual(lhs, rhs, msg);
+    }
+
+    /**
+     * Calls {@link #assertLessThanOrEqual(Comparable, Comparable, String)} with a default message.
+     *
+     * @param <T> a type
+     * @param lhs The left hand side of the comparison.
+     * @param rhs The right hand side of the comparison.
+     * @see #assertLessThanOrEqual(Comparable, Comparable, String)
+     */
+    public static <T extends Comparable<T>> void assertLessThanOrEqual(T lhs, T rhs) {
+        assertLessThanOrEqual(lhs, rhs, null);
+    }
+
+    /**
+     * Asserts that {@code lhs} is less than or equal to {@code rhs}.
+     *
+     * @param <T> a type
+     * @param lhs The left hand side of the comparison.
+     * @param rhs The right hand side of the comparison.
+     * @param msg A description of the assumption; {@code null} for a default message.
+     * @throws RuntimeException if the assertion is not true.
+     */
+    public static <T extends Comparable<T>> void assertLessThanOrEqual(T lhs, T rhs, String msg) {
+        if (!(compare(lhs, rhs, msg) <= 0)) {
+            msg = Objects.toString(msg, "assertLessThanOrEqual")
+                    + ": expected that " + Objects.toString(lhs)
+                    + " <= " + Objects.toString(rhs);
+            fail(msg);
+        }
+    }
+
+    /**
+     * Shorthand for {@link #assertEquals(Object, Object)}.
+     *
+     * @param lhs The left hand side of the comparison.
+     * @param rhs The right hand side of the comparison.
+     * @see #assertEquals(Object, Object)
+     */
+    public static void assertEQ(Object lhs, Object rhs) {
+        assertEquals(lhs, rhs);
+    }
+
+    /**
+     * Shorthand for {@link #assertEquals(Object, Object, String)}.
+     *
+     * @param lhs The left hand side of the comparison.
+     * @param rhs The right hand side of the comparison.
+     * @param msg A description of the assumption; {@code null} for a default message.
+     * @see #assertEquals(Object, Object, String)
+     */
+    public static void assertEQ(Object lhs, Object rhs, String msg) {
+        assertEquals(lhs, rhs, msg);
+    }
+
+    /**
+     * Calls {@link #assertEquals(java.lang.Object, java.lang.Object, java.lang.String)} with a default message.
+     *
+     * @param lhs The left hand side of the comparison.
+     * @param rhs The right hand side of the comparison.
+     * @see #assertEquals(Object, Object, String)
+     */
+    public static void assertEquals(Object lhs, Object rhs) {
+        assertEquals(lhs, rhs, null);
+    }
+
+    /**
+     * Asserts that {@code lhs} is equal to {@code rhs}.
+     *
+     * @param lhs The left hand side of the comparison.
+     * @param rhs The right hand side of the comparison.
+     * @param msg A description of the assumption; {@code null} for a default message.
+     * @throws RuntimeException if the assertion is not true.
+     */
+    public static void assertEquals(Object lhs, Object rhs, String msg) {
+        if ((lhs != rhs) && ((lhs == null) || !(lhs.equals(rhs)))) {
+            msg = Objects.toString(msg, "assertEquals")
+                    + ": expected " + Objects.toString(lhs)
+                    + " to equal " + Objects.toString(rhs);
+            fail(msg);
+        }
+    }
+
+    /**
+     * Calls {@link #assertSame(java.lang.Object, java.lang.Object, java.lang.String)} with a default message.
+     *
+     * @param lhs The left hand side of the comparison.
+     * @param rhs The right hand side of the comparison.
+     * @see #assertSame(Object, Object, String)
+     */
+    public static void assertSame(Object lhs, Object rhs) {
+        assertSame(lhs, rhs, null);
+    }
+
+    /**
+     * Asserts that {@code lhs} is the same as {@code rhs}.
+     *
+     * @param lhs The left hand side of the comparison.
+     * @param rhs The right hand side of the comparison.
+     * @param msg A description of the assumption; {@code null} for a default message.
+     * @throws RuntimeException if the assertion is not true.
+     */
+    public static void assertSame(Object lhs, Object rhs, String msg) {
+        if (lhs != rhs) {
+            msg = Objects.toString(msg, "assertSame")
+                    + ": expected " + Objects.toString(lhs)
+                    + " to equal " + Objects.toString(rhs);
+            fail(msg);
+        }
+    }
+
+    /**
+     * Shorthand for {@link #assertGreaterThanOrEqual(Comparable, Comparable)}.
+     *
+     * @param <T> a type
+     * @param lhs The left hand side of the comparison.
+     * @param rhs The right hand side of the comparison.
+     * @see #assertGreaterThanOrEqual(Comparable, Comparable)
+     */
+    public static <T extends Comparable<T>> void assertGTE(T lhs, T rhs) {
+        assertGreaterThanOrEqual(lhs, rhs);
+    }
+
+    /**
+     * Shorthand for {@link #assertGreaterThanOrEqual(Comparable, Comparable, String)}.
+     *
+     * @param <T> a type
+     * @param lhs The left hand side of the comparison.
+     * @param rhs The right hand side of the comparison.
+     * @param msg A description of the assumption; {@code null} for a default message.
+     * @see #assertGreaterThanOrEqual(Comparable, Comparable, String)
+     */
+    public static <T extends Comparable<T>> void assertGTE(T lhs, T rhs, String msg) {
+        assertGreaterThanOrEqual(lhs, rhs, msg);
+    }
+
+    /**
+     * Calls {@link #assertGreaterThanOrEqual(Comparable, Comparable, String)} with a default message.
+     *
+     * @param <T> a type
+     * @param lhs The left hand side of the comparison.
+     * @param rhs The right hand side of the comparison.
+     * @see #assertGreaterThanOrEqual(Comparable, Comparable, String)
+     */
+    public static <T extends Comparable<T>> void assertGreaterThanOrEqual(T lhs, T rhs) {
+        assertGreaterThanOrEqual(lhs, rhs, null);
+    }
+
+    /**
+     * Asserts that {@code lhs} is greater than or equal to {@code rhs}.
+     *
+     * @param <T> a type
+     * @param lhs The left hand side of the comparison.
+     * @param rhs The right hand side of the comparison.
+     * @param msg A description of the assumption; {@code null} for a default message.
+     * @throws RuntimeException if the assertion is not true.
+     */
+    public static <T extends Comparable<T>> void assertGreaterThanOrEqual(T lhs, T rhs, String msg) {
+        if (!(compare(lhs, rhs, msg) >= 0)) {
+            msg = Objects.toString(msg, "assertGreaterThanOrEqual")
+                    + ": expected " + Objects.toString(lhs)
+                    + " >= " + Objects.toString(rhs);
+            fail(msg);
+        }
+    }
+
+    /**
+     * Shorthand for {@link #assertGreaterThan(Comparable, Comparable)}.
+     *
+     * @param <T> a type
+     * @param lhs The left hand side of the comparison.
+     * @param rhs The right hand side of the comparison.
+     * @see #assertGreaterThan(Comparable, Comparable)
+     */
+    public static <T extends Comparable<T>> void assertGT(T lhs, T rhs) {
+        assertGreaterThan(lhs, rhs);
+    }
+
+    /**
+     * Shorthand for {@link #assertGreaterThan(Comparable, Comparable, String)}.
+     *
+     * @param <T> a type
+     * @param lhs the left hand value
+     * @param rhs the right hand value
+     * @param msg A description of the assumption; {@code null} for a default message.
+     * @see #assertGreaterThan(Comparable, Comparable, String)
+     */
+    public static <T extends Comparable<T>> void assertGT(T lhs, T rhs, String msg) {
+        assertGreaterThan(lhs, rhs, msg);
+    }
+
+    /**
+     * Calls {@link #assertGreaterThan(Comparable, Comparable, String)} with a default message.
+     *
+     * @param <T> a type
+     * @param lhs the left hand value
+     * @param rhs the right hand value
+     * @see #assertGreaterThan(Comparable, Comparable, String)
+     */
+    public static <T extends Comparable<T>> void assertGreaterThan(T lhs, T rhs) {
+        assertGreaterThan(lhs, rhs, null);
+    }
+
+    /**
+     * Asserts that {@code lhs} is greater than {@code rhs}.
+     *
+     * @param <T> a type
+     * @param lhs The left hand side of the comparison.
+     * @param rhs The right hand side of the comparison.
+     * @param msg A description of the assumption; {@code null} for a default message.
+     * @throws RuntimeException if the assertion is not true.
+     */
+    public static <T extends Comparable<T>> void assertGreaterThan(T lhs, T rhs, String msg) {
+        if (!(compare(lhs, rhs, msg) > 0)) {
+            msg = Objects.toString(msg, "assertGreaterThan")
+                    + ": expected " + Objects.toString(lhs)
+                    + " > " + Objects.toString(rhs);
+            fail(msg);
+        }
+    }
+
+    /**
+     * Shorthand for {@link #assertNotEquals(Object, Object)}.
+     *
+     * @param lhs The left hand side of the comparison.
+     * @param rhs The right hand side of the comparison.
+     * @see #assertNotEquals(Object, Object)
+     */
+    public static void assertNE(Object lhs, Object rhs) {
+        assertNotEquals(lhs, rhs);
+    }
+
+    /**
+     * Shorthand for {@link #assertNotEquals(Object, Object, String)}.
+     *
+     * @param lhs The left hand side of the comparison.
+     * @param rhs The right hand side of the comparison.
+     * @param msg A description of the assumption; {@code null} for a default message.
+     * @see #assertNotEquals(Object, Object, String)
+     */
+    public static void assertNE(Object lhs, Object rhs, String msg) {
+        assertNotEquals(lhs, rhs, msg);
+    }
+
+    /**
+     * Calls {@link #assertNotEquals(Object, Object, String)} with a default message.
+     *
+     * @param lhs The left hand side of the comparison.
+     * @param rhs The right hand side of the comparison.
+     * @see #assertNotEquals(Object, Object, String)
+     */
+    public static void assertNotEquals(Object lhs, Object rhs) {
+        assertNotEquals(lhs, rhs, null);
+    }
+
+    /**
+     * Asserts that {@code lhs} is not equal to {@code rhs}.
+     *
+     * @param lhs The left hand side of the comparison.
+     * @param rhs The right hand side of the comparison.
+     * @param msg A description of the assumption; {@code null} for a default message.
+     * @throws RuntimeException if the assertion is not true.
+     */
+    public static void assertNotEquals(Object lhs, Object rhs, String msg) {
+        if ((lhs == rhs) || (lhs != null && lhs.equals(rhs))) {
+            msg = Objects.toString(msg, "assertNotEquals")
+                    + ": expected " + Objects.toString(lhs)
+                    + " to not equal " + Objects.toString(rhs);
+            fail(msg);
+        }
+    }
+
+    /**
+     * Calls {@link #assertNull(Object, String)} with a default message.
+     *
+     * @param o The reference assumed to be null.
+     * @see #assertNull(Object, String)
+     */
+    public static void assertNull(Object o) {
+        assertNull(o, null);
+    }
+
+    /**
+     * Asserts that {@code o} is null.
+     *
+     * @param o The reference assumed to be null.
+     * @param msg A description of the assumption; {@code null} for a default message.
+     * @throws RuntimeException if the assertion is not true.
+     */
+    public static void assertNull(Object o, String msg) {
+        assertEquals(o, null, msg);
+    }
+
+    /**
+     * Calls {@link #assertNotNull(Object, String)} with a default message.
+     *
+     * @param o The reference assumed <i>not</i> to be null,
+     * @see #assertNotNull(Object, String)
+     */
+    public static void assertNotNull(Object o) {
+        assertNotNull(o, null);
+    }
+
+    /**
+     * Asserts that {@code o} is <i>not</i> null.
+     *
+     * @param o The reference assumed <i>not</i> to be null,
+     * @param msg A description of the assumption; {@code null} for a default message.
+     * @throws RuntimeException if the assertion is not true.
+     */
+    public static void assertNotNull(Object o, String msg) {
+        assertNotEquals(o, null, msg);
+    }
+
+    /**
+     * Calls {@link #assertFalse(boolean, String)} with a default message.
+     *
+     * @param value The value assumed to be false.
+     * @see #assertFalse(boolean, String)
+     */
+    public static void assertFalse(boolean value) {
+        assertFalse(value, null);
+    }
+
+    /**
+     * Asserts that {@code value} is {@code false}.
+     *
+     * @param value The value assumed to be false.
+     * @param msg A description of the assumption; {@code null} for a default message.
+     * @throws RuntimeException if the assertion is not true.
+     */
+    public static void assertFalse(boolean value, String msg) {
+        if (value) {
+            msg = Objects.toString(msg, "assertFalse")
+                    + ": expected false, was true";
+            fail(msg);
+        }
+    }
+
+    /**
+     * Calls {@link #assertTrue(boolean, String)} with a default message.
+     *
+     * @param value The value assumed to be true.
+     * @see #assertTrue(boolean, String)
+     */
+    public static void assertTrue(boolean value) {
+        assertTrue(value, null);
+    }
+
+    /**
+     * Asserts that {@code value} is {@code true}.
+     *
+     * @param value The value assumed to be true.
+     * @param msg A description of the assumption; {@code null} for a default message.
+     * @throws RuntimeException if the assertion is not true.
+     */
+    public static void assertTrue(boolean value, String msg) {
+        if (!value) {
+            msg = Objects.toString(msg, "assertTrue")
+                    + ": expected true, was false";
+            fail(msg);
+        }
+    }
+
+    private static <T extends Comparable<T>> int compare(T lhs, T rhs, String msg) {
+        if (lhs == null || rhs == null) {
+            fail(lhs, rhs, msg + ": values must be non-null:", ",");
+        }
+        return lhs.compareTo(rhs);
+    }
+
+/**
+     * Asserts that two strings are equal.
+     *
+     * If strings are not equals, then exception message
+     * will contain {@code msg} followed by list of mismatched lines.
+     *
+     * @param str1 First string to compare.
+     * @param str2 Second string to compare.
+     * @param msg A description of the assumption.
+     * @throws RuntimeException if strings are not equal.
+     */
+    public static void assertStringsEqual(String str1, String str2,
+                                          String msg) {
+        String lineSeparator = System.getProperty("line.separator");
+        String str1Lines[] = str1.split(lineSeparator);
+        String str2Lines[] = str2.split(lineSeparator);
+
+        int minLength = Math.min(str1Lines.length, str2Lines.length);
+        String longestStringLines[] = ((str1Lines.length == minLength) ?
+                                       str2Lines : str1Lines);
+
+        boolean stringsAreDifferent = false;
+
+        StringBuilder messageBuilder = new StringBuilder(msg);
+
+        messageBuilder.append("\n");
+
+        for (int line = 0; line < minLength; line++) {
+            if (!str1Lines[line].equals(str2Lines[line])) {
+                messageBuilder.append(String.
+                                      format("[line %d] '%s' differs " +
+                                             "from '%s'\n",
+                                             line,
+                                             str1Lines[line],
+                                             str2Lines[line]));
+                stringsAreDifferent = true;
+            }
+        }
+
+        if (minLength < longestStringLines.length) {
+            String stringName = ((longestStringLines == str1Lines) ?
+                                 "first" : "second");
+            messageBuilder.append(String.format("Only %s string contains " +
+                                                "following lines:\n",
+                                                stringName));
+            stringsAreDifferent = true;
+            for(int line = minLength; line < longestStringLines.length; line++) {
+                messageBuilder.append(String.
+                                      format("[line %d] '%s'", line,
+                                             longestStringLines[line]));
+            }
+        }
+
+        if (stringsAreDifferent) {
+            fail(messageBuilder.toString());
+        }
+    }
+
+    /**
+     * Returns a string formatted with a message and expected and actual values.
+     * @param lhs the actual value
+     * @param rhs  the expected value
+     * @param message the actual value
+     * @param relation the asserted relationship between lhs and rhs
+     * @return a formatted string
+     */
+    public static String format(Object lhs, Object rhs, String message, String relation) {
+        StringBuilder sb = new StringBuilder(80);
+        if (message != null) {
+            sb.append(message);
+            sb.append(' ');
+        }
+        sb.append("<");
+        sb.append(Objects.toString(lhs));
+        sb.append("> ");
+        sb.append(Objects.toString(relation, ","));
+        sb.append(" <");
+        sb.append(Objects.toString(rhs));
+        sb.append(">");
+        return sb.toString();
+    }
+
+    /**
+     * Fail reports a failure with message fail.
+     *
+     * @throws RuntimeException always
+     */
+    public static void fail() {
+        fail("fail");
+    }
+
+    /**
+     * Fail reports a failure with a message.
+     * @param message for the failure
+     * @throws RuntimeException always
+     */
+    public static void fail(String message) {
+        throw new RuntimeException(message);
+    }
+
+    /**
+     * Fail reports a failure with a formatted message.
+     *
+     * @param lhs the actual value
+     * @param rhs the expected value
+     * @param message to be format before the expected and actual values
+     * @param relation the asserted relationship between lhs and rhs
+     * @throws RuntimeException always
+     */
+    public static void fail(Object lhs, Object rhs, String message, String relation) {
+        throw new RuntimeException(format(lhs, rhs, message, relation));
+    }
+
+    /**
+     * Fail reports a failure with a message and a cause.
+     * @param message to be format before the expected and actual values
+     * @param cause the exception that caused this failure
+     * @throws RuntimeException always
+     */
+    public static void fail(String message, Throwable cause) {
+        throw new RuntimeException(message, cause);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/lib/share/classes/jdk/test/lib/JDKToolFinder.java	Mon Dec 07 17:04:33 2015 +0000
@@ -0,0 +1,106 @@
+/*
+ * 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 jdk.test.lib;
+
+import java.io.FileNotFoundException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+public final class JDKToolFinder {
+
+    private JDKToolFinder() {
+    }
+
+    /**
+     * Returns the full path to an executable in jdk/bin based on System
+     * property {@code test.jdk} or {@code compile.jdk} (both are set by the jtreg test suite)
+     *
+     * @return Full path to an executable in jdk/bin
+     */
+    public static String getJDKTool(String tool) {
+
+        // First try to find the executable in test.jdk
+        try {
+            return getTool(tool, "test.jdk");
+        } catch (FileNotFoundException e) {
+
+        }
+
+        // Now see if it's available in compile.jdk
+        try {
+            return getTool(tool, "compile.jdk");
+        } catch (FileNotFoundException e) {
+            throw new RuntimeException("Failed to find " + tool +
+                    ", looked in test.jdk (" + System.getProperty("test.jdk") +
+                    ") and compile.jdk (" + System.getProperty("compile.jdk") + ")");
+        }
+    }
+
+    /**
+     * Returns the full path to an executable in jdk/bin based on System
+     * property {@code compile.jdk}
+     *
+     * @return Full path to an executable in jdk/bin
+     */
+    public static String getCompileJDKTool(String tool) {
+        try {
+            return getTool(tool, "compile.jdk");
+        } catch (FileNotFoundException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Returns the full path to an executable in jdk/bin based on System
+     * property {@code test.jdk}
+     *
+     * @return Full path to an executable in jdk/bin
+     */
+    public static String getTestJDKTool(String tool) {
+        try {
+            return getTool(tool, "test.jdk");
+        } catch (FileNotFoundException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private static String getTool(String tool, String property) throws FileNotFoundException {
+        String jdkPath = System.getProperty(property);
+
+        if (jdkPath == null) {
+            throw new RuntimeException(
+                    "System property '" + property + "' not set. This property is normally set by jtreg. "
+                    + "When running test separately, set this property using '-D" + property + "=/path/to/jdk'.");
+        }
+
+        Path toolName = Paths.get("bin", tool + (Platform.isWindows() ? ".exe" : ""));
+
+        Path jdkTool = Paths.get(jdkPath, toolName.toString());
+        if (!jdkTool.toFile().exists()) {
+            throw new FileNotFoundException("Could not find file " + jdkTool.toAbsolutePath());
+        }
+
+        return jdkTool.toAbsolutePath().toString();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/lib/share/classes/jdk/test/lib/JDKToolLauncher.java	Mon Dec 07 17:04:33 2015 +0000
@@ -0,0 +1,135 @@
+/*
+ * 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 jdk.test.lib;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import jdk.test.lib.process.*;
+
+/**
+ * A utility for constructing command lines for starting JDK tool processes.
+ *
+ * The JDKToolLauncher can in particular be combined with a
+ * java.lang.ProcessBuilder to easily run a JDK tool. For example, the following
+ * code run {@code jmap -heap} against a process with GC logging turned on for
+ * the {@code jmap} process:
+ *
+ * <pre>
+ * {@code
+ * JDKToolLauncher jmap = JDKToolLauncher.create("jmap")
+ *                                       .addVMArg("-XX:+PrintGC");
+ *                                       .addVMArg("-XX:+PrintGCDetails")
+ *                                       .addToolArg("-heap")
+ *                                       .addToolArg(pid);
+ * ProcessBuilder pb = new ProcessBuilder(jmap.getCommand());
+ * Process p = pb.start();
+ * }
+ * </pre>
+ */
+public class JDKToolLauncher {
+    private final String executable;
+    private final List<String> vmArgs = new ArrayList<String>();
+    private final List<String> toolArgs = new ArrayList<String>();
+
+    private JDKToolLauncher(String tool, boolean useCompilerJDK) {
+        if (useCompilerJDK) {
+            executable = JDKToolFinder.getJDKTool(tool);
+        } else {
+            executable = JDKToolFinder.getTestJDKTool(tool);
+        }
+        vmArgs.addAll(Arrays.asList(ProcessTools.getPlatformSpecificVMArgs()));
+    }
+
+    /**
+     * Creates a new JDKToolLauncher for the specified tool. Using tools path
+     * from the compiler JDK.
+     *
+     * @param tool
+     *            The name of the tool
+     * @return A new JDKToolLauncher
+     */
+    public static JDKToolLauncher create(String tool) {
+        return new JDKToolLauncher(tool, true);
+    }
+
+    /**
+     * Creates a new JDKToolLauncher for the specified tool in the Tested JDK.
+     *
+     * @param tool
+     *            The name of the tool
+     *
+     * @return A new JDKToolLauncher
+     */
+    public static JDKToolLauncher createUsingTestJDK(String tool) {
+        return new JDKToolLauncher(tool, false);
+    }
+
+    /**
+     * Adds an argument to the JVM running the tool.
+     *
+     * The JVM arguments are passed to the underlying JVM running the tool.
+     * Arguments will automatically be prepended with "-J".
+     *
+     * Any platform specific arguments required for running the tool are
+     * automatically added.
+     *
+     *
+     * @param arg
+     *            The argument to VM running the tool
+     * @return The JDKToolLauncher instance
+     */
+    public JDKToolLauncher addVMArg(String arg) {
+        vmArgs.add(arg);
+        return this;
+    }
+
+    /**
+     * Adds an argument to the tool.
+     *
+     * @param arg
+     *            The argument to the tool
+     * @return The JDKToolLauncher instance
+     */
+    public JDKToolLauncher addToolArg(String arg) {
+        toolArgs.add(arg);
+        return this;
+    }
+
+    /**
+     * Returns the command that can be used for running the tool.
+     *
+     * @return An array whose elements are the arguments of the command.
+     */
+    public String[] getCommand() {
+        List<String> command = new ArrayList<String>();
+        command.add(executable);
+        // Add -J in front of all vmArgs
+        for (String arg : vmArgs) {
+            command.add("-J" + arg);
+        }
+        command.addAll(toolArgs);
+        return command.toArray(new String[command.size()]);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/lib/share/classes/jdk/test/lib/Platform.java	Mon Dec 07 17:04:33 2015 +0000
@@ -0,0 +1,206 @@
+/*
+ * 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 jdk.test.lib;
+
+import java.util.regex.Pattern;
+
+public class Platform {
+    private static final String osName      = System.getProperty("os.name");
+    private static final String dataModel   = System.getProperty("sun.arch.data.model");
+    private static final String vmVersion   = System.getProperty("java.vm.version");
+    private static final String javaVersion = System.getProperty("java.version");
+    private static final String osArch      = System.getProperty("os.arch");
+    private static final String vmName      = System.getProperty("java.vm.name");
+    private static final String userName    = System.getProperty("user.name");
+    private static final String compiler    = System.getProperty("sun.management.compiler");
+
+    public static boolean isClient() {
+        return vmName.endsWith(" Client VM");
+    }
+
+    public static boolean isServer() {
+        return vmName.endsWith(" Server VM");
+    }
+
+    public static boolean isGraal() {
+        return vmName.endsWith(" Graal VM");
+    }
+
+    public static boolean isZero() {
+        return vmName.endsWith(" Zero VM");
+    }
+
+    public static boolean isMinimal() {
+        return vmName.endsWith(" Minimal VM");
+    }
+
+    public static boolean isEmbedded() {
+        return vmName.contains("Embedded");
+    }
+
+    public static boolean isTieredSupported() {
+        return compiler.contains("Tiered Compilers");
+    }
+
+    public static boolean is32bit() {
+        return dataModel.equals("32");
+    }
+
+    public static boolean is64bit() {
+        return dataModel.equals("64");
+    }
+
+    public static boolean isAix() {
+        return isOs("aix");
+    }
+
+    public static boolean isLinux() {
+        return isOs("linux");
+    }
+
+    public static boolean isOSX() {
+        return isOs("mac");
+    }
+
+    public static boolean isSolaris() {
+        return isOs("sunos");
+    }
+
+    public static boolean isWindows() {
+        return isOs("win");
+    }
+
+    private static boolean isOs(String osname) {
+        return osName.toLowerCase().startsWith(osname.toLowerCase());
+    }
+
+    public static String getOsName() {
+        return osName;
+    }
+
+    public static boolean isDebugBuild() {
+        return (vmVersion.toLowerCase().contains("debug") ||
+                javaVersion.toLowerCase().contains("debug"));
+    }
+
+    public static String getVMVersion() {
+        return vmVersion;
+    }
+
+    // Returns true for sparc and sparcv9.
+    public static boolean isSparc() {
+        return isArch("sparc.*");
+    }
+
+    public static boolean isARM() {
+        return isArch("arm.*");
+    }
+
+    public static boolean isPPC() {
+        return isArch("ppc.*");
+    }
+
+    public static boolean isX86() {
+        // On Linux it's 'i386', Windows 'x86' without '_64' suffix.
+        return isArch("(i386)|(x86(?!_64))");
+    }
+
+    public static boolean isX64() {
+        // On OSX it's 'x86_64' and on other (Linux, Windows and Solaris) platforms it's 'amd64'
+        return isArch("(amd64)|(x86_64)");
+    }
+
+    public static boolean isAArch64() {
+        return isArch("aarch64");
+    }
+
+    private static boolean isArch(String archnameRE) {
+        return Pattern.compile(archnameRE, Pattern.CASE_INSENSITIVE)
+                .matcher(osArch)
+                .matches();
+    }
+
+    public static String getOsArch() {
+        return osArch;
+    }
+
+    /**
+     * Return a boolean for whether we expect to be able to attach
+     * the SA to our own processes on this system.
+     */
+    public static boolean shouldSAAttach() throws Exception {
+
+        if (isAix()) {
+            return false;   // SA not implemented.
+        } else if (isLinux()) {
+            return canPtraceAttachLinux();
+        } else if (isOSX()) {
+            return canAttachOSX();
+        } else {
+            // Other platforms expected to work:
+            return true;
+        }
+    }
+
+    /**
+     * On Linux, first check the SELinux boolean "deny_ptrace" and return false
+     * as we expect to be denied if that is "1".  Then expect permission to attach
+     * if we are root, so return true.  Then return false for an expected denial
+     * if "ptrace_scope" is 1, and true otherwise.
+     */
+    public static boolean canPtraceAttachLinux() throws Exception {
+
+        // SELinux deny_ptrace:
+        String deny_ptrace = Utils.fileAsString("/sys/fs/selinux/booleans/deny_ptrace");
+        if (deny_ptrace != null && deny_ptrace.contains("1")) {
+            // ptrace will be denied:
+            return false;
+        }
+
+        // YAMA enhanced security ptrace_scope:
+        // 0 - a process can PTRACE_ATTACH to any other process running under the same uid
+        // 1 - restricted ptrace: a process must be a children of the inferior or user is root
+        // 2 - only processes with CAP_SYS_PTRACE may use ptrace or user is root
+        // 3 - no attach: no processes may use ptrace with PTRACE_ATTACH
+        String ptrace_scope = Utils.fileAsString("/proc/sys/kernel/yama/ptrace_scope");
+        if (ptrace_scope != null) {
+            if (ptrace_scope.startsWith("3")) {
+                return false;
+            }
+            if (!userName.equals("root") && !ptrace_scope.startsWith("0")) {
+                // ptrace will be denied:
+                return false;
+            }
+        }
+        // Otherwise expect to be permitted:
+        return true;
+    }
+
+    /**
+     * On OSX, expect permission to attach only if we are root.
+     */
+    public static boolean canAttachOSX() throws Exception {
+        return userName.equals("root");
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/lib/share/classes/jdk/test/lib/Utils.java	Mon Dec 07 17:04:33 2015 +0000
@@ -0,0 +1,640 @@
+/*
+ * 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 jdk.test.lib;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.net.InetAddress;
+import java.net.MalformedURLException;
+import java.net.ServerSocket;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.net.UnknownHostException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Random;
+import java.util.function.BooleanSupplier;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import sun.misc.Unsafe;
+
+import jdk.test.lib.process.*;
+import static jdk.test.lib.Asserts.assertTrue;
+
+/**
+ * Common library for various test helper functions.
+ */
+public final class Utils {
+
+    /**
+     * Returns the value of 'test.class.path' system property.
+     */
+    public static final String TEST_CLASS_PATH = System.getProperty("test.class.path", ".");
+
+    /**
+     * Returns the sequence used by operating system to separate lines.
+     */
+    public static final String NEW_LINE = System.getProperty("line.separator");
+
+    /**
+     * Returns the value of 'test.vm.opts' system property.
+     */
+    public static final String VM_OPTIONS = System.getProperty("test.vm.opts", "").trim();
+
+    /**
+     * Returns the value of 'test.java.opts' system property.
+     */
+    public static final String JAVA_OPTIONS = System.getProperty("test.java.opts", "").trim();
+
+    /**
+     * Returns the value of 'test.src' system property.
+     */
+    public static final String TEST_SRC = System.getProperty("test.src", "").trim();
+
+    private static Unsafe unsafe = null;
+
+    /**
+     * Defines property name for seed value.
+     */
+    public static final String SEED_PROPERTY_NAME = "jdk.test.lib.random.seed";
+
+    /* (non-javadoc)
+     * Random generator with (or without) predefined seed. Depends on
+     * "jdk.test.lib.random.seed" property value.
+     */
+    private static volatile Random RANDOM_GENERATOR;
+
+    /**
+     * Contains the seed value used for {@link java.util.Random} creation.
+     */
+    public static final long SEED = Long.getLong(SEED_PROPERTY_NAME, new Random().nextLong());
+    /**
+    * Returns the value of 'test.timeout.factor' system property
+    * converted to {@code double}.
+    */
+    public static final double TIMEOUT_FACTOR;
+    static {
+        String toFactor = System.getProperty("test.timeout.factor", "1.0");
+        TIMEOUT_FACTOR = Double.parseDouble(toFactor);
+    }
+
+    /**
+    * Returns the value of JTREG default test timeout in milliseconds
+    * converted to {@code long}.
+    */
+    public static final long DEFAULT_TEST_TIMEOUT = TimeUnit.SECONDS.toMillis(120);
+
+    private Utils() {
+        // Private constructor to prevent class instantiation
+    }
+
+    /**
+     * Returns the list of VM options.
+     *
+     * @return List of VM options
+     */
+    public static List<String> getVmOptions() {
+        return Arrays.asList(safeSplitString(VM_OPTIONS));
+    }
+
+    /**
+     * Returns the list of VM options with -J prefix.
+     *
+     * @return The list of VM options with -J prefix
+     */
+    public static List<String> getForwardVmOptions() {
+        String[] opts = safeSplitString(VM_OPTIONS);
+        for (int i = 0; i < opts.length; i++) {
+            opts[i] = "-J" + opts[i];
+        }
+        return Arrays.asList(opts);
+    }
+
+    /**
+     * Returns the default JTReg arguments for a jvm running a test.
+     * This is the combination of JTReg arguments test.vm.opts and test.java.opts.
+     * @return An array of options, or an empty array if no options.
+     */
+    public static String[] getTestJavaOpts() {
+        List<String> opts = new ArrayList<String>();
+        Collections.addAll(opts, safeSplitString(VM_OPTIONS));
+        Collections.addAll(opts, safeSplitString(JAVA_OPTIONS));
+        return opts.toArray(new String[0]);
+    }
+
+    /**
+     * Combines given arguments with default JTReg arguments for a jvm running a test.
+     * This is the combination of JTReg arguments test.vm.opts and test.java.opts
+     * @return The combination of JTReg test java options and user args.
+     */
+    public static String[] addTestJavaOpts(String... userArgs) {
+        List<String> opts = new ArrayList<String>();
+        Collections.addAll(opts, getTestJavaOpts());
+        Collections.addAll(opts, userArgs);
+        return opts.toArray(new String[0]);
+    }
+
+    /**
+     * Removes any options specifying which GC to use, for example "-XX:+UseG1GC".
+     * Removes any options matching: -XX:(+/-)Use*GC
+     * Used when a test need to set its own GC version. Then any
+     * GC specified by the framework must first be removed.
+     * @return A copy of given opts with all GC options removed.
+     */
+    private static final Pattern useGcPattern = Pattern.compile(
+            "(?:\\-XX\\:[\\+\\-]Use.+GC)"
+            + "|(?:\\-Xconcgc)");
+    public static List<String> removeGcOpts(List<String> opts) {
+        List<String> optsWithoutGC = new ArrayList<String>();
+        for (String opt : opts) {
+            if (useGcPattern.matcher(opt).matches()) {
+                System.out.println("removeGcOpts: removed " + opt);
+            } else {
+                optsWithoutGC.add(opt);
+            }
+        }
+        return optsWithoutGC;
+    }
+
+    /**
+     * Returns the default JTReg arguments for a jvm running a test without
+     * options that matches regular expressions in {@code filters}.
+     * This is the combination of JTReg arguments test.vm.opts and test.java.opts.
+     * @param filters Regular expressions used to filter out options.
+     * @return An array of options, or an empty array if no options.
+     */
+    public static String[] getFilteredTestJavaOpts(String... filters) {
+        String options[] = getTestJavaOpts();
+
+        if (filters.length == 0) {
+            return options;
+        }
+
+        List<String> filteredOptions = new ArrayList<String>(options.length);
+        Pattern patterns[] = new Pattern[filters.length];
+        for (int i = 0; i < filters.length; i++) {
+            patterns[i] = Pattern.compile(filters[i]);
+        }
+
+        for (String option : options) {
+            boolean matched = false;
+            for (int i = 0; i < patterns.length && !matched; i++) {
+                Matcher matcher = patterns[i].matcher(option);
+                matched = matcher.find();
+            }
+            if (!matched) {
+                filteredOptions.add(option);
+            }
+        }
+
+        return filteredOptions.toArray(new String[filteredOptions.size()]);
+    }
+
+    /**
+     * Splits a string by white space.
+     * Works like String.split(), but returns an empty array
+     * if the string is null or empty.
+     */
+    private static String[] safeSplitString(String s) {
+        if (s == null || s.trim().isEmpty()) {
+            return new String[] {};
+        }
+        return s.trim().split("\\s+");
+    }
+
+    /**
+     * @return The full command line for the ProcessBuilder.
+     */
+    public static String getCommandLine(ProcessBuilder pb) {
+        StringBuilder cmd = new StringBuilder();
+        for (String s : pb.command()) {
+            cmd.append(s).append(" ");
+        }
+        return cmd.toString();
+    }
+
+    /**
+     * Returns the free port on the local host.
+     * The function will spin until a valid port number is found.
+     *
+     * @return The port number
+     * @throws InterruptedException if any thread has interrupted the current thread
+     * @throws IOException if an I/O error occurs when opening the socket
+     */
+    public static int getFreePort() throws InterruptedException, IOException {
+        int port = -1;
+
+        while (port <= 0) {
+            Thread.sleep(100);
+
+            ServerSocket serverSocket = null;
+            try {
+                serverSocket = new ServerSocket(0);
+                port = serverSocket.getLocalPort();
+            } finally {
+                serverSocket.close();
+            }
+        }
+
+        return port;
+    }
+
+    /**
+     * Returns the name of the local host.
+     *
+     * @return The host name
+     * @throws UnknownHostException if IP address of a host could not be determined
+     */
+    public static String getHostname() throws UnknownHostException {
+        InetAddress inetAddress = InetAddress.getLocalHost();
+        String hostName = inetAddress.getHostName();
+
+        assertTrue((hostName != null && !hostName.isEmpty()),
+                "Cannot get hostname");
+
+        return hostName;
+    }
+
+    /**
+     * Uses "jcmd -l" to search for a jvm pid. This function will wait
+     * forever (until jtreg timeout) for the pid to be found.
+     * @param key Regular expression to search for
+     * @return The found pid.
+     */
+    public static int waitForJvmPid(String key) throws Throwable {
+        final long iterationSleepMillis = 250;
+        System.out.println("waitForJvmPid: Waiting for key '" + key + "'");
+        System.out.flush();
+        while (true) {
+            int pid = tryFindJvmPid(key);
+            if (pid >= 0) {
+                return pid;
+            }
+            Thread.sleep(iterationSleepMillis);
+        }
+    }
+
+    /**
+     * Searches for a jvm pid in the output from "jcmd -l".
+     *
+     * Example output from jcmd is:
+     * 12498 sun.tools.jcmd.JCmd -l
+     * 12254 /tmp/jdk8/tl/jdk/JTwork/classes/com/sun/tools/attach/Application.jar
+     *
+     * @param key A regular expression to search for.
+     * @return The found pid, or -1 if not found.
+     * @throws Exception If multiple matching jvms are found.
+     */
+    public static int tryFindJvmPid(String key) throws Throwable {
+        OutputAnalyzer output = null;
+        try {
+            JDKToolLauncher jcmdLauncher = JDKToolLauncher.create("jcmd");
+            jcmdLauncher.addToolArg("-l");
+            output = ProcessTools.executeProcess(jcmdLauncher.getCommand());
+            output.shouldHaveExitValue(0);
+
+            // Search for a line starting with numbers (pid), follwed by the key.
+            Pattern pattern = Pattern.compile("([0-9]+)\\s.*(" + key + ").*\\r?\\n");
+            Matcher matcher = pattern.matcher(output.getStdout());
+
+            int pid = -1;
+            if (matcher.find()) {
+                pid = Integer.parseInt(matcher.group(1));
+                System.out.println("findJvmPid.pid: " + pid);
+                if (matcher.find()) {
+                    throw new Exception("Found multiple JVM pids for key: " + key);
+                }
+            }
+            return pid;
+        } catch (Throwable t) {
+            System.out.println(String.format("Utils.findJvmPid(%s) failed: %s", key, t));
+            throw t;
+        }
+    }
+
+    /**
+     * Adjusts the provided timeout value for the TIMEOUT_FACTOR
+     * @param tOut the timeout value to be adjusted
+     * @return The timeout value adjusted for the value of "test.timeout.factor"
+     *         system property
+     */
+    public static long adjustTimeout(long tOut) {
+        return Math.round(tOut * Utils.TIMEOUT_FACTOR);
+    }
+
+    /**
+     * Return the contents of the named file as a single String,
+     * or null if not found.
+     * @param filename name of the file to read
+     * @return String contents of file, or null if file not found.
+     * @throws  IOException
+     *          if an I/O error occurs reading from the file or a malformed or
+     *          unmappable byte sequence is read
+     */
+    public static String fileAsString(String filename) throws IOException {
+        Path filePath = Paths.get(filename);
+        if (!Files.exists(filePath)) return null;
+        return new String(Files.readAllBytes(filePath));
+    }
+
+    /**
+     * @return Unsafe instance.
+     */
+    public static synchronized Unsafe getUnsafe() {
+        if (unsafe == null) {
+            try {
+                Field f = Unsafe.class.getDeclaredField("theUnsafe");
+                f.setAccessible(true);
+                unsafe = (Unsafe) f.get(null);
+            } catch (NoSuchFieldException | IllegalAccessException e) {
+                throw new RuntimeException("Unable to get Unsafe instance.", e);
+            }
+        }
+        return unsafe;
+    }
+    private static final char[] hexArray = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
+
+    /**
+     * Returns hex view of byte array
+     *
+     * @param bytes byte array to process
+     * @return Space separated hexadecimal string representation of bytes
+     */
+
+    public static String toHexString(byte[] bytes) {
+        char[] hexView = new char[bytes.length * 3];
+        int i = 0;
+        for (byte b : bytes) {
+            hexView[i++] = hexArray[(b >> 4) & 0x0F];
+            hexView[i++] = hexArray[b & 0x0F];
+            hexView[i++] = ' ';
+        }
+        return new String(hexView);
+    }
+
+    /**
+     * Returns {@link java.util.Random} generator initialized with particular seed.
+     * The seed could be provided via system property {@link Utils#SEED_PROPERTY_NAME}
+     * In case no seed is provided, the method uses a random number.
+     * The used seed printed to stdout.
+     * @return {@link java.util.Random} generator with particular seed.
+     */
+    public static Random getRandomInstance() {
+        if (RANDOM_GENERATOR == null) {
+            synchronized (Utils.class) {
+                if (RANDOM_GENERATOR == null) {
+                    RANDOM_GENERATOR = new Random(SEED);
+                    System.out.printf("For random generator using seed: %d%n", SEED);
+                    System.out.printf("To re-run test with same seed value please add \"-D%s=%d\" to command line.%n", SEED_PROPERTY_NAME, SEED);
+                }
+            }
+        }
+        return RANDOM_GENERATOR;
+    }
+
+    /**
+     * Returns random element of non empty collection
+     *
+     * @param <T> a type of collection element
+     * @param collection collection of elements
+     * @return random element of collection
+     * @throws IllegalArgumentException if collection is empty
+     */
+    public static <T> T getRandomElement(Collection<T> collection)
+            throws IllegalArgumentException {
+        if (collection.isEmpty()) {
+            throw new IllegalArgumentException("Empty collection");
+        }
+        Random random = getRandomInstance();
+        int elementIndex = 1 + random.nextInt(collection.size() - 1);
+        Iterator<T> iterator = collection.iterator();
+        while (--elementIndex != 0) {
+            iterator.next();
+        }
+        return iterator.next();
+    }
+
+    /**
+     * Wait for condition to be true
+     *
+     * @param condition, a condition to wait for
+     */
+    public static final void waitForCondition(BooleanSupplier condition) {
+        waitForCondition(condition, -1L, 100L);
+    }
+
+    /**
+     * Wait until timeout for condition to be true
+     *
+     * @param condition, a condition to wait for
+     * @param timeout a time in milliseconds to wait for condition to be true
+     * specifying -1 will wait forever
+     * @return condition value, to determine if wait was successful
+     */
+    public static final boolean waitForCondition(BooleanSupplier condition,
+            long timeout) {
+        return waitForCondition(condition, timeout, 100L);
+    }
+
+    /**
+     * Wait until timeout for condition to be true for specified time
+     *
+     * @param condition, a condition to wait for
+     * @param timeout a time in milliseconds to wait for condition to be true,
+     * specifying -1 will wait forever
+     * @param sleepTime a time to sleep value in milliseconds
+     * @return condition value, to determine if wait was successful
+     */
+    public static final boolean waitForCondition(BooleanSupplier condition,
+            long timeout, long sleepTime) {
+        long startTime = System.currentTimeMillis();
+        while (!(condition.getAsBoolean() || (timeout != -1L
+                && ((System.currentTimeMillis() - startTime) > timeout)))) {
+            try {
+                Thread.sleep(sleepTime);
+            } catch (InterruptedException e) {
+                Thread.currentThread().interrupt();
+                throw new Error(e);
+            }
+        }
+        return condition.getAsBoolean();
+    }
+
+    /**
+     * Interface same as java.lang.Runnable but with
+     * method {@code run()} able to throw any Throwable.
+     */
+    public static interface ThrowingRunnable {
+        void run() throws Throwable;
+    }
+
+    /**
+     * Filters out an exception that may be thrown by the given
+     * test according to the given filter.
+     *
+     * @param test - method that is invoked and checked for exception.
+     * @param filter - function that checks if the thrown exception matches
+     *                 criteria given in the filter's implementation.
+     * @return - exception that matches the filter if it has been thrown or
+     *           {@code null} otherwise.
+     * @throws Throwable - if test has thrown an exception that does not
+     *                     match the filter.
+     */
+    public static Throwable filterException(ThrowingRunnable test,
+            Function<Throwable, Boolean> filter) throws Throwable {
+        try {
+            test.run();
+        } catch (Throwable t) {
+            if (filter.apply(t)) {
+                return t;
+            } else {
+                throw t;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Ensures a requested class is loaded
+     * @param aClass class to load
+     */
+    public static void ensureClassIsLoaded(Class<?> aClass) {
+        if (aClass == null) {
+            throw new Error("Requested null class");
+        }
+        try {
+            Class.forName(aClass.getName(), /* initialize = */ true,
+                    ClassLoader.getSystemClassLoader());
+        } catch (ClassNotFoundException e) {
+            throw new Error("Class not found", e);
+        }
+    }
+    /**
+     * @param parent a class loader to be the parent for the returned one
+     * @return an UrlClassLoader with urls made of the 'test.class.path' jtreg
+     *         property and with the given parent
+     */
+    public static URLClassLoader getTestClassPathURLClassLoader(ClassLoader parent) {
+        URL[] urls = Arrays.stream(TEST_CLASS_PATH.split(File.pathSeparator))
+                .map(Paths::get)
+                .map(Path::toUri)
+                .map(x -> {
+                    try {
+                        return x.toURL();
+                    } catch (MalformedURLException ex) {
+                        throw new Error("Test issue. JTREG property"
+                                + " 'test.class.path'"
+                                + " is not defined correctly", ex);
+                    }
+                }).toArray(URL[]::new);
+        return new URLClassLoader(urls, parent);
+    }
+
+    /**
+     * Runs runnable and checks that it throws expected exception. If exceptionException is null it means
+     * that we expect no exception to be thrown.
+     * @param runnable what we run
+     * @param expectedException expected exception
+     */
+    public static void runAndCheckException(Runnable runnable, Class<? extends Throwable> expectedException) {
+        runAndCheckException(runnable, t -> {
+            if (t == null) {
+                if (expectedException != null) {
+                    throw new AssertionError("Didn't get expected exception " + expectedException.getSimpleName());
+                }
+            } else {
+                String message = "Got unexpected exception " + t.getClass().getSimpleName();
+                if (expectedException == null) {
+                    throw new AssertionError(message, t);
+                } else if (!expectedException.isAssignableFrom(t.getClass())) {
+                    message += " instead of " + expectedException.getSimpleName();
+                    throw new AssertionError(message, t);
+                }
+            }
+        });
+    }
+
+    /**
+     * Runs runnable and makes some checks to ensure that it throws expected exception.
+     * @param runnable what we run
+     * @param checkException a consumer which checks that we got expected exception and raises a new exception otherwise
+     */
+    public static void runAndCheckException(Runnable runnable, Consumer<Throwable> checkException) {
+        try {
+            runnable.run();
+            checkException.accept(null);
+        } catch (Throwable t) {
+            checkException.accept(t);
+        }
+    }
+
+    /**
+     * Converts to VM type signature
+     *
+     * @param type Java type to convert
+     * @return string representation of VM type
+     */
+    public static String toJVMTypeSignature(Class<?> type) {
+        if (type.isPrimitive()) {
+            if (type == boolean.class) {
+                return "Z";
+            } else if (type == byte.class) {
+                return "B";
+            } else if (type == char.class) {
+                return "C";
+            } else if (type == double.class) {
+                return "D";
+            } else if (type == float.class) {
+                return "F";
+            } else if (type == int.class) {
+                return "I";
+            } else if (type == long.class) {
+                return "J";
+            } else if (type == short.class) {
+                return "S";
+            } else if (type == void.class) {
+                return "V";
+            } else {
+                throw new Error("Unsupported type: " + type);
+            }
+        }
+        String result = type.getName().replaceAll("\\.", "/");
+        if (!type.isArray()) {
+            return "L" + result + ";";
+        }
+        return result;
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/lib/share/classes/jdk/test/lib/process/OutputAnalyzer.java	Mon Dec 07 17:04:33 2015 +0000
@@ -0,0 +1,436 @@
+/*
+ * 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 jdk.test.lib.process;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public final class OutputAnalyzer {
+
+  private final String stdout;
+  private final String stderr;
+  private final int exitValue;
+
+  /**
+   * Create an OutputAnalyzer, a utility class for verifying output and exit
+   * value from a Process
+   *
+   * @param process Process to analyze
+   * @throws IOException If an I/O error occurs.
+   */
+  public OutputAnalyzer(Process process) throws IOException {
+    OutputBuffer output = ProcessTools.getOutput(process);
+    exitValue = process.exitValue();
+    this.stdout = output.getStdout();
+    this.stderr = output.getStderr();
+  }
+
+  /**
+   * Create an OutputAnalyzer, a utility class for verifying output
+   *
+   * @param buf String buffer to analyze
+   */
+  public OutputAnalyzer(String buf) {
+    this(buf, buf);
+  }
+
+  /**
+   * Create an OutputAnalyzer, a utility class for verifying output
+   *
+   * @param stdout stdout buffer to analyze
+   * @param stderr stderr buffer to analyze
+   */
+  public OutputAnalyzer(String stdout, String stderr) {
+    this.stdout = stdout;
+    this.stderr = stderr;
+    exitValue = -1;
+  }
+
+  /**
+   * Verify that the stdout contents of output buffer is empty
+   *
+   * @throws RuntimeException
+   *             If stdout was not empty
+   */
+  public void stdoutShouldBeEmpty() {
+    if (!getStdout().isEmpty()) {
+      reportDiagnosticSummary();
+      throw new RuntimeException("stdout was not empty");
+    }
+  }
+
+  /**
+   * Verify that the stderr contents of output buffer is empty
+   *
+   * @throws RuntimeException
+   *             If stderr was not empty
+   */
+  public void stderrShouldBeEmpty() {
+    if (!getStderr().isEmpty()) {
+      reportDiagnosticSummary();
+      throw new RuntimeException("stderr was not empty");
+    }
+  }
+
+  /**
+   * Verify that the stdout contents of output buffer is not empty
+   *
+   * @throws RuntimeException
+   *             If stdout was empty
+   */
+  public void stdoutShouldNotBeEmpty() {
+    if (getStdout().isEmpty()) {
+      reportDiagnosticSummary();
+      throw new RuntimeException("stdout was empty");
+    }
+  }
+
+  /**
+   * Verify that the stderr contents of output buffer is not empty
+   *
+   * @throws RuntimeException
+   *             If stderr was empty
+   */
+  public void stderrShouldNotBeEmpty() {
+    if (getStderr().isEmpty()) {
+      reportDiagnosticSummary();
+      throw new RuntimeException("stderr was empty");
+    }
+  }
+
+    /**
+   * Verify that the stdout and stderr contents of output buffer contains the string
+   *
+   * @param expectedString String that buffer should contain
+   * @throws RuntimeException If the string was not found
+   */
+  public OutputAnalyzer shouldContain(String expectedString) {
+    if (!stdout.contains(expectedString) && !stderr.contains(expectedString)) {
+        reportDiagnosticSummary();
+        throw new RuntimeException("'" + expectedString + "' missing from stdout/stderr \n");
+    }
+    return this;
+  }
+
+  /**
+   * Verify that the stdout contents of output buffer contains the string
+   *
+   * @param expectedString String that buffer should contain
+   * @throws RuntimeException If the string was not found
+   */
+  public OutputAnalyzer stdoutShouldContain(String expectedString) {
+    if (!stdout.contains(expectedString)) {
+        reportDiagnosticSummary();
+        throw new RuntimeException("'" + expectedString + "' missing from stdout \n");
+    }
+    return this;
+  }
+
+  /**
+   * Verify that the stderr contents of output buffer contains the string
+   *
+   * @param expectedString String that buffer should contain
+   * @throws RuntimeException If the string was not found
+   */
+  public OutputAnalyzer stderrShouldContain(String expectedString) {
+    if (!stderr.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 String that the buffer should not contain
+   * @throws RuntimeException If the string was found
+   */
+  public OutputAnalyzer shouldNotContain(String notExpectedString) {
+    if (stdout.contains(notExpectedString)) {
+        reportDiagnosticSummary();
+        throw new RuntimeException("'" + notExpectedString + "' found in stdout \n");
+    }
+    if (stderr.contains(notExpectedString)) {
+        reportDiagnosticSummary();
+        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 String that the buffer should not contain
+   * @throws RuntimeException If the string was found
+   */
+  public OutputAnalyzer stdoutShouldNotContain(String notExpectedString) {
+    if (stdout.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 String that the buffer should not contain
+   * @throws RuntimeException If the string was found
+   */
+  public OutputAnalyzer stderrShouldNotContain(String notExpectedString) {
+    if (stderr.contains(notExpectedString)) {
+        reportDiagnosticSummary();
+        throw new RuntimeException("'" + notExpectedString + "' found in stderr \n");
+    }
+    return this;
+  }
+
+  /**
+   * Verify that the stdout and stderr contents of output buffer matches
+   * the pattern
+   *
+   * @param pattern
+   * @throws RuntimeException If the pattern was not found
+   */
+  public OutputAnalyzer shouldMatch(String pattern) {
+      Matcher stdoutMatcher = Pattern.compile(pattern, Pattern.MULTILINE).matcher(stdout);
+      Matcher stderrMatcher = Pattern.compile(pattern, Pattern.MULTILINE).matcher(stderr);
+      if (!stdoutMatcher.find() && !stderrMatcher.find()) {
+          reportDiagnosticSummary();
+          throw new RuntimeException("'" + pattern
+                + "' missing from stdout/stderr \n");
+      }
+      return this;
+  }
+
+  /**
+   * Verify that the stdout contents of output buffer matches the
+   * pattern
+   *
+   * @param pattern
+   * @throws RuntimeException If the pattern was not found
+   */
+  public OutputAnalyzer stdoutShouldMatch(String pattern) {
+      Matcher matcher = Pattern.compile(pattern, Pattern.MULTILINE).matcher(stdout);
+      if (!matcher.find()) {
+          reportDiagnosticSummary();
+          throw new RuntimeException("'" + pattern
+                + "' missing from stdout \n");
+      }
+      return this;
+  }
+
+  /**
+   * Verify that the stderr contents of output buffer matches the
+   * pattern
+   *
+   * @param pattern
+   * @throws RuntimeException If the pattern was not found
+   */
+  public OutputAnalyzer stderrShouldMatch(String pattern) {
+      Matcher matcher = Pattern.compile(pattern, Pattern.MULTILINE).matcher(stderr);
+      if (!matcher.find()) {
+          reportDiagnosticSummary();
+          throw new RuntimeException("'" + pattern
+                + "' missing from stderr \n");
+      }
+      return this;
+  }
+
+  /**
+   * Verify that the stdout and stderr contents of output buffer does not
+   * match the pattern
+   *
+   * @param pattern
+   * @throws RuntimeException If the pattern was found
+   */
+  public OutputAnalyzer shouldNotMatch(String pattern) {
+      Matcher matcher = Pattern.compile(pattern, Pattern.MULTILINE).matcher(stdout);
+      if (matcher.find()) {
+          reportDiagnosticSummary();
+          throw new RuntimeException("'" + pattern
+                  + "' found in stdout: '" + matcher.group() + "' \n");
+      }
+      matcher = Pattern.compile(pattern, Pattern.MULTILINE).matcher(stderr);
+      if (matcher.find()) {
+          reportDiagnosticSummary();
+          throw new RuntimeException("'" + pattern
+                  + "' found in stderr: '" + matcher.group() + "' \n");
+      }
+      return this;
+  }
+
+  /**
+   * Verify that the stdout contents of output buffer does not match the
+   * pattern
+   *
+   * @param pattern
+   * @throws RuntimeException If the pattern was found
+   */
+  public OutputAnalyzer stdoutShouldNotMatch(String pattern) {
+      Matcher matcher = Pattern.compile(pattern, Pattern.MULTILINE).matcher(stdout);
+      if (matcher.find()) {
+          reportDiagnosticSummary();
+          throw new RuntimeException("'" + pattern
+                  + "' found in stdout \n");
+      }
+      return this;
+  }
+
+  /**
+   * Verify that the stderr contents of output buffer does not match the
+   * pattern
+   *
+   * @param pattern
+   * @throws RuntimeException If the pattern was found
+   */
+  public OutputAnalyzer stderrShouldNotMatch(String pattern) {
+      Matcher matcher = Pattern.compile(pattern, Pattern.MULTILINE).matcher(stderr);
+      if (matcher.find()) {
+          reportDiagnosticSummary();
+          throw new RuntimeException("'" + pattern
+                  + "' found in stderr \n");
+      }
+      return this;
+  }
+
+  /**
+   * Get the captured group of the first string matching the pattern.
+   * stderr is searched before stdout.
+   *
+   * @param pattern The multi-line pattern to match
+   * @param group The group to capture
+   * @return The matched string or null if no match was found
+   */
+  public String firstMatch(String pattern, int group) {
+    Matcher stderrMatcher = Pattern.compile(pattern, Pattern.MULTILINE).matcher(stderr);
+    Matcher stdoutMatcher = Pattern.compile(pattern, Pattern.MULTILINE).matcher(stdout);
+    if (stderrMatcher.find()) {
+      return stderrMatcher.group(group);
+    }
+    if (stdoutMatcher.find()) {
+      return stdoutMatcher.group(group);
+    }
+    return null;
+  }
+
+  /**
+   * Get the first string matching the pattern.
+   * stderr is searched before stdout.
+   *
+   * @param pattern The multi-line pattern to match
+   * @return The matched string or null if no match was found
+   */
+  public String firstMatch(String pattern) {
+    return firstMatch(pattern, 0);
+  }
+
+  /**
+   * Verify the exit value of the process
+   *
+   * @param expectedExitValue Expected exit value from process
+   * @throws RuntimeException If the exit value from the process did not match the expected value
+   */
+  public OutputAnalyzer shouldHaveExitValue(int expectedExitValue) {
+      if (getExitValue() != expectedExitValue) {
+          reportDiagnosticSummary();
+          throw new RuntimeException("Expected to get exit value of ["
+                  + expectedExitValue + "]\n");
+      }
+      return this;
+  }
+
+
+  /**
+   * Report summary that will help to diagnose the problem
+   * Currently includes:
+   *  - standard input produced by the process under test
+   *  - standard output
+   *  - exit code
+   *  Note: the command line is printed by the ProcessTools
+   */
+    private void reportDiagnosticSummary() {
+        String msg =
+            " stdout: [" + stdout + "];\n" +
+            " stderr: [" + stderr + "]\n" +
+            " exitValue = " + getExitValue() + "\n";
+
+        System.err.println(msg);
+    }
+
+
+  /**
+   * Get the contents of the output buffer (stdout and stderr)
+   *
+   * @return Content of the output buffer
+   */
+  public String getOutput() {
+    return stdout + stderr;
+  }
+
+  /**
+   * Get the contents of the stdout buffer
+   *
+   * @return Content of the stdout buffer
+   */
+  public String getStdout() {
+    return stdout;
+  }
+
+  /**
+   * Get the contents of the stderr buffer
+   *
+   * @return Content of the stderr buffer
+   */
+  public String getStderr() {
+    return stderr;
+  }
+
+  /**
+   * Get the process exit value
+   *
+   * @return Process exit value
+   */
+  public int getExitValue() {
+    return exitValue;
+  }
+
+  /**
+   * Get the contents of the output buffer (stdout and stderr) as list of strings.
+   * Output will be split by newlines.
+   *
+   * @return Contents of the output buffer as list of strings
+   */
+  public List<String> asLines() {
+    return asLines(getOutput());
+  }
+
+  private List<String> asLines(String buffer) {
+    return Arrays.asList(buffer.split("(\\r\\n|\\n|\\r)"));
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/lib/share/classes/jdk/test/lib/process/OutputBuffer.java	Mon Dec 07 17:04:33 2015 +0000
@@ -0,0 +1,59 @@
+/*
+ * 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 jdk.test.lib.process;
+
+public class OutputBuffer {
+  private final String stdout;
+  private final String stderr;
+
+  /**
+   * Create an OutputBuffer, a class for storing and managing stdout and stderr
+   * results separately
+   *
+   * @param stdout stdout result
+   * @param stderr stderr result
+   */
+  public OutputBuffer(String stdout, String stderr) {
+    this.stdout = stdout;
+    this.stderr = stderr;
+  }
+
+  /**
+   * Returns the stdout result
+   *
+   * @return stdout result
+   */
+  public String getStdout() {
+    return stdout;
+  }
+
+  /**
+   * Returns the stderr result
+   *
+   * @return stderr result
+   */
+  public String getStderr() {
+    return stderr;
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/lib/share/classes/jdk/test/lib/process/ProcessTools.java	Mon Dec 07 17:04:33 2015 +0000
@@ -0,0 +1,590 @@
+/*
+ * 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 jdk.test.lib.process;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.concurrent.CountDownLatch;
+import java.util.Map;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.function.Predicate;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+
+import jdk.test.lib.JDKToolFinder;
+import jdk.test.lib.Platform;
+import jdk.test.lib.Utils;
+
+public final class ProcessTools {
+    private static final class LineForwarder extends StreamPumper.LinePump {
+        private final PrintStream ps;
+        private final String prefix;
+        LineForwarder(String prefix, PrintStream os) {
+            this.ps = os;
+            this.prefix = prefix;
+        }
+        @Override
+        protected void processLine(String line) {
+            ps.println("[" + prefix + "] " + line);
+        }
+    }
+
+    private ProcessTools() {
+    }
+
+    /**
+    * Pumps stdout and stderr from running the process into a String.
+    *
+    * @param processHandler ProcessHandler to run.
+    * @return Output from process.
+    * @throws IOException If an I/O error occurs.
+    */
+    public static OutputBuffer getOutput(ProcessBuilder processBuilder) throws IOException {
+        return getOutput(processBuilder.start());
+    }
+
+    /**
+    * Pumps stdout and stderr the running process into a String.
+    *
+    * @param process Process to pump.
+    * @return Output from process.
+    * @throws IOException If an I/O error occurs.
+    */
+    public static OutputBuffer getOutput(Process process) throws IOException {
+        ByteArrayOutputStream stderrBuffer = new ByteArrayOutputStream();
+        ByteArrayOutputStream stdoutBuffer = new ByteArrayOutputStream();
+        StreamPumper outPumper = new StreamPumper(process.getInputStream(), stdoutBuffer);
+        StreamPumper errPumper = new StreamPumper(process.getErrorStream(), stderrBuffer);
+        Thread outPumperThread = new Thread(outPumper);
+        Thread errPumperThread = new Thread(errPumper);
+
+        outPumperThread.setDaemon(true);
+        errPumperThread.setDaemon(true);
+
+        outPumperThread.start();
+        errPumperThread.start();
+
+        try {
+            process.waitFor();
+            outPumperThread.join();
+            errPumperThread.join();
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+            return null;
+        }
+
+        return new OutputBuffer(stdoutBuffer.toString(), stderrBuffer.toString());
+    }
+
+    /**
+     * <p>Starts a process from its builder.</p>
+     * <span>The default redirects of STDOUT and STDERR are started</span>
+     * @param name The process name
+     * @param processBuilder The process builder
+     * @return Returns the initialized process
+     * @throws IOException
+     */
+    public static Process startProcess(String name,
+                                       ProcessBuilder processBuilder)
+    throws IOException {
+        return startProcess(name, processBuilder, (Consumer<String>)null);
+    }
+
+    /**
+     * <p>Starts a process from its builder.</p>
+     * <span>The default redirects of STDOUT and STDERR are started</span>
+     * <p>It is possible to monitor the in-streams via the provided {@code consumer}
+     * @param name The process name
+     * @param consumer {@linkplain Consumer} instance to process the in-streams
+     * @param processBuilder The process builder
+     * @return Returns the initialized process
+     * @throws IOException
+     */
+    @SuppressWarnings("overloads")
+    public static Process startProcess(String name,
+                                       ProcessBuilder processBuilder,
+                                       Consumer<String> consumer)
+    throws IOException {
+        try {
+            return startProcess(name, processBuilder, consumer, null, -1, TimeUnit.NANOSECONDS);
+        } catch (InterruptedException | TimeoutException e) {
+            // will never happen
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * <p>Starts a process from its builder.</p>
+     * <span>The default redirects of STDOUT and STDERR are started</span>
+     * <p>
+     * It is possible to wait for the process to get to a warmed-up state
+     * via {@linkplain Predicate} condition on the STDOUT
+     * </p>
+     * @param name The process name
+     * @param processBuilder The process builder
+     * @param linePredicate The {@linkplain Predicate} to use on the STDOUT
+     *                      Used to determine the moment the target app is
+     *                      properly warmed-up.
+     *                      It can be null - in that case the warmup is skipped.
+     * @param timeout The timeout for the warmup waiting; -1 = no wait; 0 = wait forever
+     * @param unit The timeout {@linkplain TimeUnit}
+     * @return Returns the initialized {@linkplain Process}
+     * @throws IOException
+     * @throws InterruptedException
+     * @throws TimeoutException
+     */
+    public static Process startProcess(String name,
+                                       ProcessBuilder processBuilder,
+                                       final Predicate<String> linePredicate,
+                                       long timeout,
+                                       TimeUnit unit)
+    throws IOException, InterruptedException, TimeoutException {
+        return startProcess(name, processBuilder, null, linePredicate, timeout, unit);
+    }
+
+    /**
+     * <p>Starts a process from its builder.</p>
+     * <span>The default redirects of STDOUT and STDERR are started</span>
+     * <p>
+     * It is possible to wait for the process to get to a warmed-up state
+     * via {@linkplain Predicate} condition on the STDOUT and monitor the
+     * in-streams via the provided {@linkplain Consumer}
+     * </p>
+     * @param name The process name
+     * @param processBuilder The process builder
+     * @param lineConsumer  The {@linkplain Consumer} the lines will be forwarded to
+     * @param linePredicate The {@linkplain Predicate} to use on the STDOUT
+     *                      Used to determine the moment the target app is
+     *                      properly warmed-up.
+     *                      It can be null - in that case the warmup is skipped.
+     * @param timeout The timeout for the warmup waiting; -1 = no wait; 0 = wait forever
+     * @param unit The timeout {@linkplain TimeUnit}
+     * @return Returns the initialized {@linkplain Process}
+     * @throws IOException
+     * @throws InterruptedException
+     * @throws TimeoutException
+     */
+    public static Process startProcess(String name,
+                                       ProcessBuilder processBuilder,
+                                       final Consumer<String> lineConsumer,
+                                       final Predicate<String> linePredicate,
+                                       long timeout,
+                                       TimeUnit unit)
+    throws IOException, InterruptedException, TimeoutException {
+        System.out.println("["+name+"]:" + processBuilder.command().stream().collect(Collectors.joining(" ")));
+        Process p = processBuilder.start();
+        StreamPumper stdout = new StreamPumper(p.getInputStream());
+        StreamPumper stderr = new StreamPumper(p.getErrorStream());
+
+        stdout.addPump(new LineForwarder(name, System.out));
+        stderr.addPump(new LineForwarder(name, System.err));
+        if (lineConsumer != null) {
+            StreamPumper.LinePump pump = new StreamPumper.LinePump() {
+                @Override
+                protected void processLine(String line) {
+                    lineConsumer.accept(line);
+                }
+            };
+            stdout.addPump(pump);
+            stderr.addPump(pump);
+        }
+
+
+        CountDownLatch latch = new CountDownLatch(1);
+        if (linePredicate != null) {
+            StreamPumper.LinePump pump = new StreamPumper.LinePump() {
+                @Override
+                protected void processLine(String line) {
+                    if (latch.getCount() > 0 && linePredicate.test(line)) {
+                        latch.countDown();
+                    }
+                }
+            };
+            stdout.addPump(pump);
+            stderr.addPump(pump);
+        } else {
+            latch.countDown();
+        }
+        final Future<Void> stdoutTask = stdout.process();
+        final Future<Void> stderrTask = stderr.process();
+
+        try {
+            if (timeout > -1) {
+                if (timeout == 0) {
+                    latch.await();
+                } else {
+                    if (!latch.await(Utils.adjustTimeout(timeout), unit)) {
+                        throw new TimeoutException();
+                    }
+                }
+            }
+        } catch (TimeoutException | InterruptedException e) {
+            System.err.println("Failed to start a process (thread dump follows)");
+            for(Map.Entry<Thread, StackTraceElement[]> s : Thread.getAllStackTraces().entrySet()) {
+                printStack(s.getKey(), s.getValue());
+            }
+
+            if (p.isAlive()) {
+                p.destroyForcibly();
+            }
+
+            stdoutTask.cancel(true);
+            stderrTask.cancel(true);
+            throw e;
+        }
+
+        return new ProcessImpl(p, stdoutTask, stderrTask);
+    }
+
+    /**
+     * <p>Starts a process from its builder.</p>
+     * <span>The default redirects of STDOUT and STDERR are started</span>
+     * <p>
+     * It is possible to wait for the process to get to a warmed-up state
+     * via {@linkplain Predicate} condition on the STDOUT. The warm-up will
+     * wait indefinitely.
+     * </p>
+     * @param name The process name
+     * @param processBuilder The process builder
+     * @param linePredicate The {@linkplain Predicate} to use on the STDOUT
+     *                      Used to determine the moment the target app is
+     *                      properly warmed-up.
+     *                      It can be null - in that case the warmup is skipped.
+     * @return Returns the initialized {@linkplain Process}
+     * @throws IOException
+     * @throws InterruptedException
+     * @throws TimeoutException
+     */
+    @SuppressWarnings("overloads")
+    public static Process startProcess(String name,
+                                       ProcessBuilder processBuilder,
+                                       final Predicate<String> linePredicate)
+    throws IOException, InterruptedException, TimeoutException {
+        return startProcess(name, processBuilder, linePredicate, 0, TimeUnit.SECONDS);
+    }
+
+    /**
+     * Get the process id of the current running Java process
+     *
+     * @return Process id
+     */
+    public static long getProcessId() throws Exception {
+        return ProcessHandle.current().getPid();
+    }
+
+    /**
+     * Get platform specific VM arguments (e.g. -d64 on 64bit Solaris)
+     *
+     * @return String[] with platform specific arguments, empty if there are
+     *         none
+     */
+    public static String[] getPlatformSpecificVMArgs() {
+
+    if (Platform.is64bit() && Platform.isSolaris()) {
+            return new String[] { "-d64" };
+        }
+
+        return new String[] {};
+    }
+
+
+    /**
+     * Create ProcessBuilder using the java launcher from the jdk to be tested and
+     * with any platform specific arguments prepended
+     */
+    public static ProcessBuilder createJavaProcessBuilder(String... command) throws Exception {
+        return createJavaProcessBuilder(false, command);
+    }
+
+    /**
+     * Create ProcessBuilder using the java launcher from the jdk to be tested,
+     * and with any platform specific arguments prepended.
+     *
+     * @param addTestVmAndJavaOptions If true, adds test.vm.opts and test.java.opts
+     *        to the java arguments.
+     * @param command Arguments to pass to the java command.
+     * @return The ProcessBuilder instance representing the java command.
+     */
+    public static ProcessBuilder createJavaProcessBuilder(boolean addTestVmAndJavaOptions, String... command) throws Exception {
+        String javapath = JDKToolFinder.getJDKTool("java");
+
+        ArrayList<String> args = new ArrayList<>();
+        args.add(javapath);
+        Collections.addAll(args, getPlatformSpecificVMArgs());
+
+        if (addTestVmAndJavaOptions) {
+            // -cp is needed to make sure the same classpath is used whether the test is
+            // run in AgentVM mode or OtherVM mode. It was added to the hotspot version
+            // of this API as part of 8077608. However, for the jdk version it is only
+            // added when addTestVmAndJavaOptions is true in order to minimize
+            // disruption to existing JDK tests, which have yet to be tested with -cp
+            // being added. At some point -cp should always be added to be consistent
+            // with what the hotspot version does.
+            args.add("-cp");
+            args.add(System.getProperty("java.class.path"));
+            Collections.addAll(args, Utils.getTestJavaOpts());
+        }
+
+        Collections.addAll(args, command);
+
+        // Reporting
+        StringBuilder cmdLine = new StringBuilder();
+        for (String cmd : args)
+            cmdLine.append(cmd).append(' ');
+        System.out.println("Command line: [" + cmdLine.toString() + "]");
+
+        return new ProcessBuilder(args.toArray(new String[args.size()]));
+    }
+
+    private static void printStack(Thread t, StackTraceElement[] stack) {
+        System.out.println("\t" +  t +
+                           " stack: (length = " + stack.length + ")");
+        if (t != null) {
+            for (StackTraceElement stack1 : stack) {
+                System.out.println("\t" + stack1);
+            }
+            System.out.println();
+        }
+    }
+
+    /**
+     * Executes a test jvm process, waits for it to finish and returns the process output.
+     * The default jvm options from jtreg, test.vm.opts and test.java.opts, are added.
+     * The java from the test.jdk is used to execute the command.
+     *
+     * The command line will be like:
+     * {test.jdk}/bin/java {test.vm.opts} {test.java.opts} cmds
+     *
+     * The jvm process will have exited before this method returns.
+     *
+     * @param cmds User specifed arguments.
+     * @return The output from the process.
+     */
+    public static OutputAnalyzer executeTestJvm(String... cmds) throws Exception {
+        ProcessBuilder pb = createJavaProcessBuilder(Utils.addTestJavaOpts(cmds));
+        return executeProcess(pb);
+    }
+
+    /**
+     * Executes a process, 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.
+     * @return The {@linkplain OutputAnalyzer} instance wrapping the process.
+     */
+    public static OutputAnalyzer executeProcess(ProcessBuilder pb) throws Exception {
+        OutputAnalyzer output = null;
+        Process p = null;
+        boolean failed = false;
+        try {
+            p = pb.start();
+            output = new OutputAnalyzer(p);
+            p.waitFor();
+
+            return output;
+        } catch (Throwable t) {
+            if (p != null) {
+                p.destroyForcibly().waitFor();
+            }
+
+            failed = true;
+            System.out.println("executeProcess() failed: " + t);
+            throw t;
+        } finally {
+            if (failed) {
+                System.err.println(getProcessLog(pb, output));
+            }
+        }
+    }
+
+    /**
+     * Executes a process, waits for it to finish and returns the process output.
+     *
+     * The process will have exited before this method returns.
+     *
+     * @param cmds The command line to execute.
+     * @return The output from the process.
+     */
+    public static OutputAnalyzer executeProcess(String... cmds) throws Throwable {
+        return executeProcess(new ProcessBuilder(cmds));
+    }
+
+    /**
+     * Used to log command line, stdout, stderr and exit code from an executed process.
+     * @param pb The executed process.
+     * @param output The output from the process.
+     */
+    public static String getProcessLog(ProcessBuilder pb, OutputAnalyzer output) {
+        String stderr = output == null ? "null" : output.getStderr();
+        String stdout = output == null ? "null" : output.getStdout();
+        String exitValue = output == null ? "null": Integer.toString(output.getExitValue());
+        StringBuilder logMsg = new StringBuilder();
+        final String nl = System.getProperty("line.separator");
+        logMsg.append("--- ProcessLog ---" + nl);
+        logMsg.append("cmd: " + getCommandLine(pb) + nl);
+        logMsg.append("exitvalue: " + exitValue + nl);
+        logMsg.append("stderr: " + stderr + nl);
+        logMsg.append("stdout: " + stdout + nl);
+
+        return logMsg.toString();
+    }
+
+    /**
+     * @return The full command line for the ProcessBuilder.
+     */
+    public static String getCommandLine(ProcessBuilder pb) {
+        if (pb == null) {
+            return "null";
+        }
+        StringBuilder cmd = new StringBuilder();
+        for (String s : pb.command()) {
+            cmd.append(s).append(" ");
+        }
+        return cmd.toString().trim();
+    }
+
+    /**
+     * Executes a process, waits for it to finish, prints the process output
+     * to stdout, and returns the process output.
+     *
+     * The process will have exited before this method returns.
+     *
+     * @param cmds The command line to execute.
+     * @return The {@linkplain OutputAnalyzer} instance wrapping the process.
+     */
+    public static OutputAnalyzer executeCommand(String... cmds)
+            throws Throwable {
+        String cmdLine = Arrays.stream(cmds).collect(Collectors.joining(" "));
+        System.out.println("Command line: [" + cmdLine + "]");
+        OutputAnalyzer analyzer = ProcessTools.executeProcess(cmds);
+        System.out.println(analyzer.getOutput());
+        return analyzer;
+    }
+
+    /**
+     * Executes a process, waits for it to finish, prints the process output
+     * to stdout and returns the process output.
+     *
+     * The process will have exited before this method returns.
+     *
+     * @param pb The ProcessBuilder to execute.
+     * @return The {@linkplain OutputAnalyzer} instance wrapping the process.
+     */
+    public static OutputAnalyzer executeCommand(ProcessBuilder pb)
+            throws Throwable {
+        String cmdLine = pb.command().stream().collect(Collectors.joining(" "));
+        System.out.println("Command line: [" + cmdLine + "]");
+        OutputAnalyzer analyzer = ProcessTools.executeProcess(pb);
+        System.out.println(analyzer.getOutput());
+        return analyzer;
+    }
+
+    private static class ProcessImpl extends Process {
+
+        private final Process p;
+        private final Future<Void> stdoutTask;
+        private final Future<Void> stderrTask;
+
+        public ProcessImpl(Process p, Future<Void> stdoutTask, Future<Void> stderrTask) {
+            this.p = p;
+            this.stdoutTask = stdoutTask;
+            this.stderrTask = stderrTask;
+        }
+
+        @Override
+        public OutputStream getOutputStream() {
+            return p.getOutputStream();
+        }
+
+        @Override
+        public InputStream getInputStream() {
+            return p.getInputStream();
+        }
+
+        @Override
+        public InputStream getErrorStream() {
+            return p.getErrorStream();
+        }
+
+        @Override
+        public int waitFor() throws InterruptedException {
+            int rslt = p.waitFor();
+            waitForStreams();
+            return rslt;
+        }
+
+        @Override
+        public int exitValue() {
+            return p.exitValue();
+        }
+
+        @Override
+        public void destroy() {
+            p.destroy();
+        }
+
+        @Override
+        public long getPid() {
+            return p.getPid();
+        }
+
+        @Override
+        public boolean isAlive() {
+            return p.isAlive();
+        }
+
+        @Override
+        public Process destroyForcibly() {
+            return p.destroyForcibly();
+        }
+
+        @Override
+        public boolean waitFor(long timeout, TimeUnit unit) throws InterruptedException {
+            boolean rslt = p.waitFor(timeout, unit);
+            if (rslt) {
+                waitForStreams();
+            }
+            return rslt;
+        }
+
+        private void waitForStreams() throws InterruptedException {
+            try {
+                stdoutTask.get();
+            } catch (ExecutionException e) {
+            }
+            try {
+                stderrTask.get();
+            } catch (ExecutionException e) {
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/lib/share/classes/jdk/test/lib/process/StreamPumper.java	Mon Dec 07 17:04:33 2015 +0000
@@ -0,0 +1,197 @@
+/*
+ * 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 jdk.test.lib.process;
+
+import java.io.BufferedInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.OutputStream;
+import java.io.InputStream;
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.Future;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+public final class StreamPumper implements Runnable {
+
+    private static final int BUF_SIZE = 256;
+
+    /**
+     * Pump will be called by the StreamPumper to process the incoming data
+     */
+    abstract public static class Pump {
+        abstract void register(StreamPumper d);
+    }
+
+    /**
+     * OutputStream -> Pump adapter
+     */
+    final public static class StreamPump extends Pump {
+        private final OutputStream out;
+        public StreamPump(OutputStream out) {
+            this.out = out;
+        }
+
+        @Override
+        void register(StreamPumper sp) {
+            sp.addOutputStream(out);
+        }
+    }
+
+    /**
+     * Used to process the incoming data line-by-line
+     */
+    abstract public static class LinePump extends Pump {
+        @Override
+        final void register(StreamPumper sp) {
+            sp.addLineProcessor(this);
+        }
+
+        abstract protected void processLine(String line);
+    }
+
+    private final InputStream in;
+    private final Set<OutputStream> outStreams = new HashSet<>();
+    private final Set<LinePump> linePumps = new HashSet<>();
+
+    private final AtomicBoolean processing = new AtomicBoolean(false);
+    private final FutureTask<Void> processingTask = new FutureTask<>(this, null);
+
+    public StreamPumper(InputStream in) {
+        this.in = in;
+    }
+
+    /**
+     * Create a StreamPumper that reads from in and writes to out.
+     *
+     * @param in The stream to read from.
+     * @param out The stream to write to.
+     */
+    public StreamPumper(InputStream in, OutputStream out) {
+        this(in);
+        this.addOutputStream(out);
+    }
+
+    /**
+     * Implements Thread.run(). Continuously read from {@code in} and write to
+     * {@code out} until {@code in} has reached end of stream. Abort on
+     * interruption. Abort on IOExceptions.
+     */
+    @Override
+    public void run() {
+        try (BufferedInputStream is = new BufferedInputStream(in)) {
+            ByteArrayOutputStream lineBos = new ByteArrayOutputStream();
+            byte[] buf = new byte[BUF_SIZE];
+            int len = 0;
+            int linelen = 0;
+
+            while ((len = is.read(buf)) > 0 && !Thread.interrupted()) {
+                for(OutputStream out : outStreams) {
+                    out.write(buf, 0, len);
+                }
+                if (!linePumps.isEmpty()) {
+                    int i = 0;
+                    int lastcrlf = -1;
+                    while (i < len) {
+                        if (buf[i] == '\n' || buf[i] == '\r') {
+                            int bufLinelen = i - lastcrlf - 1;
+                            if (bufLinelen > 0) {
+                                lineBos.write(buf, lastcrlf + 1, bufLinelen);
+                            }
+                            linelen += bufLinelen;
+
+                            if (linelen > 0) {
+                                lineBos.flush();
+                                final String line = lineBos.toString();
+                                linePumps.stream().forEach((lp) -> {
+                                    lp.processLine(line);
+                                });
+                                lineBos.reset();
+                                linelen = 0;
+                            }
+                            lastcrlf = i;
+                        }
+
+                        i++;
+                    }
+                    if (lastcrlf == -1) {
+                        lineBos.write(buf, 0, len);
+                        linelen += len;
+                    } else if (lastcrlf < len - 1) {
+                        lineBos.write(buf, lastcrlf + 1, len - lastcrlf - 1);
+                        linelen += len - lastcrlf - 1;
+                    }
+                }
+            }
+
+        } catch (IOException e) {
+            e.printStackTrace();
+        } finally {
+            for(OutputStream out : outStreams) {
+                try {
+                    out.flush();
+                } catch (IOException e) {}
+            }
+            try {
+                in.close();
+            } catch (IOException e) {}
+        }
+    }
+
+    final void addOutputStream(OutputStream out) {
+        outStreams.add(out);
+    }
+
+    final void addLineProcessor(LinePump lp) {
+        linePumps.add(lp);
+    }
+
+    final public StreamPumper addPump(Pump ... pump) {
+        if (processing.get()) {
+            throw new IllegalStateException("Can not modify pumper while " +
+                                            "processing is in progress");
+        }
+        for(Pump p : pump) {
+            p.register(this);
+        }
+        return this;
+    }
+
+    final public Future<Void> process() {
+        if (!processing.compareAndSet(false, true)) {
+            throw new IllegalStateException("Can not re-run the processing");
+        }
+        Thread t = new Thread(new Runnable() {
+            @Override
+            public void run() {
+                processingTask.run();
+            }
+        });
+        t.setDaemon(true);
+        t.start();
+
+        return processingTask;
+    }
+}