8077350: JEP 102 Process API Updates Implementation
authorrriggs
Fri, 29 May 2015 14:04:12 -0400
changeset 30899 d2408e757489
parent 30898 6027a68229dc
child 30900 bef45ee9a95e
8077350: JEP 102 Process API Updates Implementation Reviewed-by: chegar, plevart, psandoz, darcy, martin, alanb
jdk/make/mapfiles/libjava/mapfile-vers
jdk/src/java.base/macosx/native/libjava/ProcessHandleImpl_macosx.c
jdk/src/java.base/share/classes/java/lang/Process.java
jdk/src/java.base/share/classes/java/lang/ProcessHandle.java
jdk/src/java.base/share/classes/java/lang/ProcessHandleImpl.java
jdk/src/java.base/share/classes/java/lang/RuntimePermission.java
jdk/src/java.base/solaris/native/libjava/ProcessHandleImpl_solaris.c
jdk/src/java.base/unix/classes/java/lang/ProcessImpl.java
jdk/src/java.base/unix/native/libjava/ProcessHandleImpl_unix.c
jdk/src/java.base/unix/native/libjava/ProcessImpl_md.c
jdk/src/java.base/windows/classes/java/lang/ProcessImpl.java
jdk/src/java.base/windows/native/libjava/ProcessHandleImpl_win.c
jdk/test/TEST.ROOT
jdk/test/java/lang/ProcessBuilder/Basic.java
jdk/test/java/lang/ProcessHandle/Basic.java
jdk/test/java/lang/ProcessHandle/InfoTest.java
jdk/test/java/lang/ProcessHandle/JavaChild.java
jdk/test/java/lang/ProcessHandle/OnExitTest.java
jdk/test/java/lang/ProcessHandle/PermissionTest.java
jdk/test/java/lang/ProcessHandle/ProcessUtil.java
jdk/test/java/lang/ProcessHandle/ScaleTest.java
jdk/test/java/lang/ProcessHandle/TEST.properties
jdk/test/java/lang/ProcessHandle/TreeTest.java
--- a/jdk/make/mapfiles/libjava/mapfile-vers	Fri May 29 10:28:28 2015 -0700
+++ b/jdk/make/mapfiles/libjava/mapfile-vers	Fri May 29 14:04:12 2015 -0400
@@ -166,6 +166,16 @@
 		Java_java_lang_Package_getSystemPackage0;
 		Java_java_lang_Package_getSystemPackages0;
 		Java_java_lang_ProcessEnvironment_environ;
+		Java_java_lang_ProcessHandleImpl_getCurrentPid0;
+		Java_java_lang_ProcessHandleImpl_parent0;
+		Java_java_lang_ProcessHandleImpl_isAlive0;
+		Java_java_lang_ProcessHandleImpl_getProcessPids0;
+		Java_java_lang_ProcessHandleImpl_destroy0;
+		Java_java_lang_ProcessHandleImpl_waitForProcessExit0;
+		Java_java_lang_ProcessHandleImpl_00024Info_initIDs;
+		Java_java_lang_ProcessHandleImpl_00024Info_info0;
+		Java_java_lang_ProcessImpl_init;
+		Java_java_lang_ProcessImpl_forkAndExec;
 		Java_java_lang_reflect_Array_get;
 		Java_java_lang_reflect_Array_getBoolean;
 		Java_java_lang_reflect_Array_getByte;
@@ -214,10 +224,6 @@
 		Java_java_lang_Throwable_fillInStackTrace;
                 Java_java_lang_Throwable_getStackTraceDepth;
                 Java_java_lang_Throwable_getStackTraceElement;
-		Java_java_lang_ProcessImpl_init;
-		Java_java_lang_ProcessImpl_waitForProcessExit;
-		Java_java_lang_ProcessImpl_forkAndExec;
-		Java_java_lang_ProcessImpl_destroyProcess;
                 Java_java_nio_Bits_copyFromShortArray;
                 Java_java_nio_Bits_copyToShortArray;
                 Java_java_nio_Bits_copyFromIntArray;
@@ -277,7 +283,7 @@
 
                 Java_jdk_internal_jimage_concurrent_ConcurrentPReader_initIDs;
                 Java_jdk_internal_jimage_concurrent_ConcurrentPReader_pread;
-		
+
                 # ZipFile.c needs this one
 		throwFileNotFoundException;
                 # zip_util.c needs this one
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/macosx/native/libjava/ProcessHandleImpl_macosx.c	Fri May 29 14:04:12 2015 -0400
@@ -0,0 +1,401 @@
+/*
+ * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "jni.h"
+#include "jni_util.h"
+#include "java_lang_ProcessHandleImpl.h"
+#include "java_lang_ProcessHandleImpl_Info.h"
+
+#include <stdio.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <sys/sysctl.h>
+
+/**
+ * Implementations of ProcessHandleImpl functions for MAC OS X;
+ * are NOT common to all Unix variants.
+ */
+
+static void getStatInfo(JNIEnv *env, jobject jinfo, pid_t pid);
+static void getCmdlineInfo(JNIEnv *env, jobject jinfo, pid_t pid);
+
+/*
+ * Common Unix function to lookup the uid and return the user name.
+ */
+extern jstring uidToUser(JNIEnv* env, uid_t uid);
+
+/* Field id for jString 'command' in java.lang.ProcessHandle.Info */
+static jfieldID ProcessHandleImpl_Info_commandID;
+
+/* Field id for jString[] 'arguments' in java.lang.ProcessHandle.Info */
+static jfieldID ProcessHandleImpl_Info_argumentsID;
+
+/* Field id for jlong 'totalTime' in java.lang.ProcessHandle.Info */
+static jfieldID ProcessHandleImpl_Info_totalTimeID;
+
+/* Field id for jlong 'startTime' in java.lang.ProcessHandle.Info */
+static jfieldID ProcessHandleImpl_Info_startTimeID;
+
+/* Field id for jString 'user' in java.lang.ProcessHandleImpl.Info */
+static jfieldID ProcessHandleImpl_Info_userID;
+
+/* static value for clock ticks per second. */
+static long clock_ticks_per_second;
+
+/**************************************************************
+ * Static method to initialize field IDs and the ticks per second rate.
+ *
+ * Class:     java_lang_ProcessHandleImpl_Info
+ * Method:    initIDs
+ * Signature: ()V
+ */
+JNIEXPORT void JNICALL Java_java_lang_ProcessHandleImpl_00024Info_initIDs
+  (JNIEnv *env, jclass clazz) {
+
+    CHECK_NULL(ProcessHandleImpl_Info_commandID =
+            (*env)->GetFieldID(env, clazz, "command", "Ljava/lang/String;"));
+    CHECK_NULL(ProcessHandleImpl_Info_argumentsID =
+            (*env)->GetFieldID(env, clazz, "arguments", "[Ljava/lang/String;"));
+    CHECK_NULL(ProcessHandleImpl_Info_totalTimeID =
+            (*env)->GetFieldID(env, clazz, "totalTime", "J"));
+    CHECK_NULL(ProcessHandleImpl_Info_startTimeID =
+            (*env)->GetFieldID(env, clazz, "startTime", "J"));
+    CHECK_NULL(ProcessHandleImpl_Info_userID =
+            (*env)->GetFieldID(env, clazz, "user", "Ljava/lang/String;"));
+    clock_ticks_per_second = sysconf(_SC_CLK_TCK);
+}
+
+/*
+ * Returns the parent pid of the requested pid.
+ *
+ * Class:     java_lang_ProcessHandleImpl
+ * Method:    parent0
+ * Signature: (J)J
+ */
+JNIEXPORT jlong JNICALL Java_java_lang_ProcessHandleImpl_parent0
+(JNIEnv *env, jobject obj, jlong jpid) {
+    pid_t pid = (pid_t) jpid;
+    pid_t ppid = -1;
+
+    if (pid == getpid()) {
+        ppid = getppid();
+    } else {
+        const pid_t pid = (pid_t) jpid;
+        struct kinfo_proc kp;
+        size_t bufSize = sizeof kp;
+
+        // Read the process info for the specific pid
+        int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
+        if (sysctl(mib, 4, &kp, &bufSize, NULL, 0) < 0) {
+            JNU_ThrowByNameWithLastError(env,
+                "java/lang/RuntimeException", "sysctl failed");
+            return -1;
+        }
+        ppid = (bufSize > 0 && kp.kp_proc.p_pid == pid) ? kp.kp_eproc.e_ppid : -1;
+    }
+    return (jlong) ppid;
+}
+
+/*
+ * Returns the children of the requested pid and optionally each parent.
+ *
+ * Class:     java_lang_ProcessHandleImpl
+ * Method:    getProcessPids0
+ * Signature: (J[J[J)I
+ *
+ * Use sysctl to accumulate any process whose parent pid is zero or matches.
+ * The resulting pids are stored into the array of longs.
+ * The number of pids is returned if they all fit.
+ * If the parentArray is non-null, store the parent pid.
+ * If the array is too short, excess pids are not stored and
+ * the desired length is returned.
+ */
+JNIEXPORT jint JNICALL Java_java_lang_ProcessHandleImpl_getProcessPids0
+(JNIEnv *env, jclass clazz, jlong jpid,
+    jlongArray jarray, jlongArray jparentArray)
+{
+    size_t count = 0;
+    jlong* pids = NULL;
+    jlong* ppids = NULL;
+    size_t parentArraySize = 0;
+    size_t arraySize = 0;
+    size_t bufSize = 0;
+    pid_t pid = (pid_t) jpid;
+
+    arraySize = (*env)->GetArrayLength(env, jarray);
+    JNU_CHECK_EXCEPTION_RETURN(env, -1);
+    if (jparentArray != NULL) {
+        parentArraySize = (*env)->GetArrayLength(env, jparentArray);
+        JNU_CHECK_EXCEPTION_RETURN(env, -1);
+
+        if (arraySize != parentArraySize) {
+            JNU_ThrowIllegalArgumentException(env, "array sizes not equal");
+            return 0;
+        }
+    }
+
+    // Get buffer size needed to read all processes
+    int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0};
+    if (sysctl(mib, 4, NULL, &bufSize, NULL, 0) < 0) {
+        JNU_ThrowByNameWithLastError(env,
+            "java/lang/RuntimeException", "sysctl failed");
+        return -1;
+    }
+
+    // Allocate buffer big enough for all processes
+    void *buffer = malloc(bufSize);
+    if (buffer == NULL) {
+        JNU_ThrowOutOfMemoryError(env, "malloc failed");
+        return -1;
+    }
+
+    // Read process info for all processes
+    if (sysctl(mib, 4, buffer, &bufSize, NULL, 0) < 0) {
+        JNU_ThrowByNameWithLastError(env,
+            "java/lang/RuntimeException", "sysctl failed");
+        free(buffer);
+        return -1;
+    }
+
+    do { // Block to break out of on Exception
+        struct kinfo_proc *kp = (struct kinfo_proc *) buffer;
+        unsigned long nentries = bufSize / sizeof (struct kinfo_proc);
+        long i;
+
+        pids = (*env)->GetLongArrayElements(env, jarray, NULL);
+        if (pids == NULL) {
+            break;
+        }
+        if (jparentArray != NULL) {
+            ppids  = (*env)->GetLongArrayElements(env, jparentArray, NULL);
+            if (ppids == NULL) {
+                break;
+            }
+        }
+
+        // Process each entry in the buffer
+        for (i = nentries; --i >= 0; ++kp) {
+            if (pid == 0 || kp->kp_eproc.e_ppid == pid) {
+                if (count < arraySize) {
+                    // Only store if it fits
+                    pids[count] = (jlong) kp->kp_proc.p_pid;
+                    if (ppids != NULL) {
+                        // Store the parentPid
+                        ppids[count] = (jlong) kp->kp_eproc.e_ppid;
+                    }
+                }
+                count++; // Count to tabulate size needed
+            }
+        }
+    } while (0);
+
+    if (pids != NULL) {
+        (*env)->ReleaseLongArrayElements(env, jarray, pids, 0);
+    }
+    if (ppids != NULL) {
+        (*env)->ReleaseLongArrayElements(env, jparentArray, ppids, 0);
+    }
+
+    free(buffer);
+    // If more pids than array had size for; count will be greater than array size
+    return count;
+}
+
+/**************************************************************
+ * Implementation of ProcessHandleImpl_Info native methods.
+ */
+
+/*
+ * Fill in the Info object from the OS information about the process.
+ *
+ * Class:     java_lang_ProcessHandleImpl
+ * Method:    info0
+ * Signature: (J)I
+ */
+JNIEXPORT void JNICALL Java_java_lang_ProcessHandleImpl_00024Info_info0
+  (JNIEnv *env, jobject jinfo, jlong jpid) {
+    pid_t pid = (pid_t) jpid;
+    getStatInfo(env, jinfo, pid);
+    getCmdlineInfo(env, jinfo, pid);
+}
+
+/**
+ * Read /proc/<pid>/stat and fill in the fields of the Info object.
+ * The executable name, plus the user, system, and start times are gathered.
+ */
+static void getStatInfo(JNIEnv *env, jobject jinfo, pid_t jpid) {
+    jlong totalTime;                    // nanoseconds
+    unsigned long long startTime;       // microseconds
+
+    const pid_t pid = (pid_t) jpid;
+    struct kinfo_proc kp;
+    size_t bufSize = sizeof kp;
+
+    // Read the process info for the specific pid
+    int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
+
+    if (sysctl(mib, 4, &kp, &bufSize, NULL, 0) < 0) {
+        if (errno == EINVAL) {
+            return;
+        } else {
+            JNU_ThrowByNameWithLastError(env,
+                "java/lang/RuntimeException", "sysctl failed");
+        }
+        return;
+    }
+
+    // Convert the UID to the username
+    jstring name = NULL;
+    CHECK_NULL((name = uidToUser(env, kp.kp_eproc.e_ucred.cr_uid)));
+    (*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_userID, name);
+    JNU_CHECK_EXCEPTION(env);
+
+    startTime = kp.kp_proc.p_starttime.tv_sec * 1000 +
+                kp.kp_proc.p_starttime.tv_usec / 1000;
+
+    (*env)->SetLongField(env, jinfo, ProcessHandleImpl_Info_startTimeID, startTime);
+    JNU_CHECK_EXCEPTION(env);
+
+    // Get cputime if for current process
+    if (pid == getpid()) {
+        struct rusage usage;
+        if (getrusage(RUSAGE_SELF, &usage) != 0) {
+            return;
+        }
+        jlong microsecs =
+            usage.ru_utime.tv_sec * 1000 * 1000 + usage.ru_utime.tv_usec +
+            usage.ru_stime.tv_sec * 1000 * 1000 + usage.ru_stime.tv_usec;
+        totalTime = microsecs * 1000;
+        (*env)->SetLongField(env, jinfo, ProcessHandleImpl_Info_totalTimeID, totalTime);
+        JNU_CHECK_EXCEPTION(env);
+    }
+}
+
+/**
+ * Construct the argument array by parsing the arguments from the sequence of arguments.
+ */
+static int fillArgArray(JNIEnv *env, jobject jinfo, int nargs,
+                        const char *cp, const char *argsEnd) {
+    jstring str = NULL;
+    jobject argsArray;
+    int i;
+
+    if (nargs < 1) {
+        return 0;
+    }
+    // Create a String array for nargs-1 elements
+    CHECK_NULL_RETURN((argsArray = (*env)->NewObjectArray(env,
+            nargs - 1, JNU_ClassString(env), NULL)), -1);
+
+    for (i = 0; i < nargs - 1; i++) {
+        // skip to the next argument; omits arg[0]
+        cp += strnlen(cp, (argsEnd - cp)) + 1;
+
+        if (cp > argsEnd || *cp == '\0') {
+            return -2;  // Off the end pointer or an empty argument is an error
+        }
+
+        CHECK_NULL_RETURN((str = JNU_NewStringPlatform(env, cp)), -1);
+
+        (*env)->SetObjectArrayElement(env, argsArray, i, str);
+        JNU_CHECK_EXCEPTION_RETURN(env, -3);
+    }
+    (*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_argumentsID, argsArray);
+    JNU_CHECK_EXCEPTION_RETURN(env, -4);
+    return 0;
+}
+
+/**
+ * Retrieve the command and arguments for the process and store them
+ * into the Info object.
+ */
+static void getCmdlineInfo(JNIEnv *env, jobject jinfo, pid_t pid) {
+    int mib[3], maxargs, nargs, i;
+    size_t size;
+    char *args, *cp, *sp, *np;
+
+    // Get the maximum size of the arguments
+    mib[0] = CTL_KERN;
+    mib[1] = KERN_ARGMAX;
+    size = sizeof(maxargs);
+    if (sysctl(mib, 2, &maxargs, &size, NULL, 0) == -1) {
+            JNU_ThrowByNameWithLastError(env,
+                    "java/lang/RuntimeException", "sysctl failed");
+        return;
+    }
+
+    // Allocate an args buffer and get the arguments
+    args = (char *)malloc(maxargs);
+    if (args == NULL) {
+        JNU_ThrowOutOfMemoryError(env, "malloc failed");
+        return;
+    }
+
+    do {            // a block to break out of on error
+        char *argsEnd;
+        jstring str = NULL;
+
+        mib[0] = CTL_KERN;
+        mib[1] = KERN_PROCARGS2;
+        mib[2] = pid;
+        size = (size_t) maxargs;
+        if (sysctl(mib, 3, args, &size, NULL, 0) == -1) {
+            if (errno != EINVAL) {
+            JNU_ThrowByNameWithLastError(env,
+                    "java/lang/RuntimeException", "sysctl failed");
+            }
+            break;
+        }
+        memcpy(&nargs, args, sizeof(nargs));
+
+        cp = &args[sizeof(nargs)];      // Strings start after nargs
+        argsEnd = &args[size];
+
+        // Store the command executable path
+        if ((str = JNU_NewStringPlatform(env, cp)) == NULL) {
+            break;
+        }
+        (*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_commandID, str);
+        if ((*env)->ExceptionCheck(env)) {
+            break;
+        }
+
+        // Skip trailing nulls after the executable path
+        for (cp = cp + strnlen(cp, argsEnd - cp); cp < argsEnd; cp++) {
+            if (*cp != '\0') {
+                break;
+            }
+        }
+
+        fillArgArray(env, jinfo, nargs, cp, argsEnd);
+    } while (0);
+    // Free the arg buffer
+    free(args);
+}
+
--- a/jdk/src/java.base/share/classes/java/lang/Process.java	Fri May 29 10:28:28 2015 -0700
+++ b/jdk/src/java.base/share/classes/java/lang/Process.java	Fri May 29 14:04:12 2015 -0400
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1995, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1995, 2015, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -26,25 +26,31 @@
 package java.lang;
 
 import java.io.*;
+import java.lang.ProcessBuilder.Redirect;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ForkJoinPool;
 import java.util.concurrent.TimeUnit;
+import java.util.stream.Stream;
 
 /**
+ * {@code Process} provides control of native processes started by
+ * ProcessBuilder.start and Runtime.exec.
+ * The class provides methods for performing input from the process, performing
+ * output to the process, waiting for the process to complete,
+ * checking the exit status of the process, and destroying (killing)
+ * the process.
  * The {@link ProcessBuilder#start()} and
  * {@link Runtime#exec(String[],String[],File) Runtime.exec}
  * methods create a native process and return an instance of a
  * subclass of {@code Process} that can be used to control the process
- * and obtain information about it.  The class {@code Process}
- * provides methods for performing input from the process, performing
- * output to the process, waiting for the process to complete,
- * checking the exit status of the process, and destroying (killing)
- * the process.
+ * and obtain information about it.
  *
  * <p>The methods that create processes may not work well for special
  * processes on certain native platforms, such as native windowing
  * processes, daemon processes, Win16/DOS processes on Microsoft
  * Windows, or shell scripts.
  *
- * <p>By default, the created subprocess does not have its own terminal
+ * <p>By default, the created process does not have its own terminal
  * or console.  All its standard I/O (i.e. stdin, stdout, stderr)
  * operations will be redirected to the parent process, where they can
  * be accessed via the streams obtained using the methods
@@ -52,35 +58,49 @@
  * {@link #getInputStream()}, and
  * {@link #getErrorStream()}.
  * The parent process uses these streams to feed input to and get output
- * from the subprocess.  Because some native platforms only provide
+ * from the process.  Because some native platforms only provide
  * limited buffer size for standard input and output streams, failure
  * to promptly write the input stream or read the output stream of
- * the subprocess may cause the subprocess to block, or even deadlock.
+ * the process may cause the process to block, or even deadlock.
  *
  * <p>Where desired, <a href="ProcessBuilder.html#redirect-input">
- * subprocess I/O can also be redirected</a>
+ * process I/O can also be redirected</a>
  * using methods of the {@link ProcessBuilder} class.
  *
- * <p>The subprocess is not killed when there are no more references to
- * the {@code Process} object, but rather the subprocess
+ * <p>The process is not killed when there are no more references to
+ * the {@code Process} object, but rather the process
  * continues executing asynchronously.
  *
- * <p>There is no requirement that a process represented by a {@code
+ * <p>There is no requirement that the process represented by a {@code
  * Process} object execute asynchronously or concurrently with respect
  * to the Java process that owns the {@code Process} object.
  *
  * <p>As of 1.5, {@link ProcessBuilder#start()} is the preferred way
  * to create a {@code Process}.
  *
+ * <p>Subclasses of Process should override the {@link #onExit()} and
+ * {@link #toHandle()} methods to provide a fully functional Process including the
+ * {@link #getPid() process id},
+ * {@link #info() information about the process},
+ * {@link #children() direct children}, and
+ * {@link #allChildren() direct and indirect children} of the process.
+ * Delegating to the underlying Process or ProcessHandle is typically
+ * easiest and most efficient.
+ *
  * @since   1.0
  */
 public abstract class Process {
     /**
+     * Default constructor for Process.
+     */
+    public Process() {}
+
+    /**
      * Returns the output stream connected to the normal input of the
-     * subprocess.  Output to the stream is piped into the standard
+     * process.  Output to the stream is piped into the standard
      * input of the process represented by this {@code Process} object.
      *
-     * <p>If the standard input of the subprocess has been redirected using
+     * <p>If the standard input of the process has been redirected using
      * {@link ProcessBuilder#redirectInput(Redirect)
      * ProcessBuilder.redirectInput}
      * then this method will return a
@@ -90,42 +110,42 @@
      * output stream to be buffered.
      *
      * @return the output stream connected to the normal input of the
-     *         subprocess
+     *         process
      */
     public abstract OutputStream getOutputStream();
 
     /**
      * Returns the input stream connected to the normal output of the
-     * subprocess.  The stream obtains data piped from the standard
+     * process.  The stream obtains data piped from the standard
      * output of the process represented by this {@code Process} object.
      *
-     * <p>If the standard output of the subprocess has been redirected using
+     * <p>If the standard output of the process has been redirected using
      * {@link ProcessBuilder#redirectOutput(Redirect)
      * ProcessBuilder.redirectOutput}
      * then this method will return a
      * <a href="ProcessBuilder.html#redirect-output">null input stream</a>.
      *
-     * <p>Otherwise, if the standard error of the subprocess has been
+     * <p>Otherwise, if the standard error of the process has been
      * redirected using
      * {@link ProcessBuilder#redirectErrorStream(boolean)
      * ProcessBuilder.redirectErrorStream}
      * then the input stream returned by this method will receive the
-     * merged standard output and the standard error of the subprocess.
+     * merged standard output and the standard error of the process.
      *
      * <p>Implementation note: It is a good idea for the returned
      * input stream to be buffered.
      *
      * @return the input stream connected to the normal output of the
-     *         subprocess
+     *         process
      */
     public abstract InputStream getInputStream();
 
     /**
      * Returns the input stream connected to the error output of the
-     * subprocess.  The stream obtains data piped from the error output
+     * process.  The stream obtains data piped from the error output
      * of the process represented by this {@code Process} object.
      *
-     * <p>If the standard error of the subprocess has been redirected using
+     * <p>If the standard error of the process has been redirected using
      * {@link ProcessBuilder#redirectError(Redirect)
      * ProcessBuilder.redirectError} or
      * {@link ProcessBuilder#redirectErrorStream(boolean)
@@ -137,19 +157,19 @@
      * input stream to be buffered.
      *
      * @return the input stream connected to the error output of
-     *         the subprocess
+     *         the process
      */
     public abstract InputStream getErrorStream();
 
     /**
      * Causes the current thread to wait, if necessary, until the
      * process represented by this {@code Process} object has
-     * terminated.  This method returns immediately if the subprocess
-     * has already terminated.  If the subprocess has not yet
+     * terminated.  This method returns immediately if the process
+     * has already terminated.  If the process has not yet
      * terminated, the calling thread will be blocked until the
-     * subprocess exits.
+     * process exits.
      *
-     * @return the exit value of the subprocess represented by this
+     * @return the exit value of the process represented by this
      *         {@code Process} object.  By convention, the value
      *         {@code 0} indicates normal termination.
      * @throws InterruptedException if the current thread is
@@ -161,10 +181,10 @@
 
     /**
      * Causes the current thread to wait, if necessary, until the
-     * subprocess represented by this {@code Process} object has
+     * process represented by this {@code Process} object has
      * terminated, or the specified waiting time elapses.
      *
-     * <p>If the subprocess has already terminated then this method returns
+     * <p>If the process has already terminated then this method returns
      * immediately with the value {@code true}.  If the process has not
      * terminated and the timeout value is less than, or equal to, zero, then
      * this method returns immediately with the value {@code false}.
@@ -176,8 +196,8 @@
      *
      * @param timeout the maximum time to wait
      * @param unit the time unit of the {@code timeout} argument
-     * @return {@code true} if the subprocess has exited and {@code false} if
-     *         the waiting time elapsed before the subprocess has exited.
+     * @return {@code true} if the process has exited and {@code false} if
+     *         the waiting time elapsed before the process has exited.
      * @throws InterruptedException if the current thread is interrupted
      *         while waiting.
      * @throws NullPointerException if unit is null
@@ -204,41 +224,60 @@
     }
 
     /**
-     * Returns the exit value for the subprocess.
+     * Returns the exit value for the process.
      *
-     * @return the exit value of the subprocess represented by this
+     * @return the exit value of the process represented by this
      *         {@code Process} object.  By convention, the value
      *         {@code 0} indicates normal termination.
-     * @throws IllegalThreadStateException if the subprocess represented
+     * @throws IllegalThreadStateException if the process represented
      *         by this {@code Process} object has not yet terminated
      */
     public abstract int exitValue();
 
     /**
-     * Kills the subprocess. Whether the subprocess represented by this
-     * {@code Process} object is forcibly terminated or not is
+     * Kills the process.
+     * Whether the process represented by this {@code Process} object is
+     * {@link #supportsNormalTermination normally terminated} or not is
      * implementation dependent.
+     * Forcible process destruction is defined as the immediate termination of a
+     * process, whereas normal termination allows the process to shut down cleanly.
+     * If the process is not alive, no action is taken.
+     * <p>
+     * The {@link java.util.concurrent.CompletableFuture} from {@link #onExit} is
+     * {@link java.util.concurrent.CompletableFuture#complete completed}
+     * when the process has terminated.
      */
     public abstract void destroy();
 
     /**
-     * Kills the subprocess. The subprocess represented by this
+     * Kills the process forcibly. The process represented by this
      * {@code Process} object is forcibly terminated.
+     * Forcible process destruction is defined as the immediate termination of a
+     * process, whereas normal termination allows the process to shut down cleanly.
+     * If the process is not alive, no action is taken.
+     * <p>
+     * The {@link java.util.concurrent.CompletableFuture} from {@link #onExit} is
+     * {@link java.util.concurrent.CompletableFuture#complete completed}
+     * when the process has terminated.
+     * <p>
+     * Invoking this method on {@code Process} objects returned by
+     * {@link ProcessBuilder#start} and {@link Runtime#exec} forcibly terminate
+     * the process.
      *
-     * <p>The default implementation of this method invokes {@link #destroy}
-     * and so may not forcibly terminate the process. Concrete implementations
-     * of this class are strongly encouraged to override this method with a
-     * compliant implementation.  Invoking this method on {@code Process}
-     * objects returned by {@link ProcessBuilder#start} and
-     * {@link Runtime#exec} will forcibly terminate the process.
-     *
-     * <p>Note: The subprocess may not terminate immediately.
+     * @implSpec
+     * The default implementation of this method invokes {@link #destroy}
+     * and so may not forcibly terminate the process.
+     * @implNote
+     * Concrete implementations of this class are strongly encouraged to override
+     * this method with a compliant implementation.
+     * @apiNote
+     * The process may not terminate immediately.
      * i.e. {@code isAlive()} may return true for a brief period
      * after {@code destroyForcibly()} is called. This method
      * may be chained to {@code waitFor()} if needed.
      *
      * @return the {@code Process} object representing the
-     *         subprocess to be forcibly destroyed.
+     *         process forcibly destroyed
      * @since 1.8
      */
     public Process destroyForcibly() {
@@ -247,10 +286,36 @@
     }
 
     /**
-     * Tests whether the subprocess represented by this {@code Process} is
+     * Returns {@code true} if the implementation of {@link #destroy} is to
+     * normally terminate the process,
+     * Returns {@code false} if the implementation of {@code destroy}
+     * forcibly and immediately terminates the process.
+     * <p>
+     * Invoking this method on {@code Process} objects returned by
+     * {@link ProcessBuilder#start} and {@link Runtime#exec} return
+     * {@code true} or {@code false} depending on the platform implementation.
+     *
+     * @implSpec
+     * This implementation throws an instance of
+     * {@link java.lang.UnsupportedOperationException} and performs no other action.
+     *
+     * @return {@code true} if the implementation of {@link #destroy} is to
+     *         normally terminate the process;
+     *         otherwise, {@link #destroy} forcibly terminates the process
+     * @throws UnsupportedOperationException if the Process implementation
+     *         does not support this operation
+     * @since 1.9
+     */
+    public boolean supportsNormalTermination() {
+        throw new UnsupportedOperationException(this.getClass()
+                + ".supportsNormalTermination() not supported" );
+    }
+
+    /**
+     * Tests whether the process represented by this {@code Process} is
      * alive.
      *
-     * @return {@code true} if the subprocess represented by this
+     * @return {@code true} if the process represented by this
      *         {@code Process} object has not yet terminated.
      * @since 1.8
      */
@@ -264,16 +329,222 @@
     }
 
     /**
-     * Returns the native process id of the subprocess.
-     * The native process id is an identification number that the operating
+     * Returns the native process ID of the process.
+     * The native process ID is an identification number that the operating
      * system assigns to the process.
      *
-     * @return the native process id of the subprocess
+     * @implSpec
+     * The implementation of this method returns the process id as:
+     * {@link #toHandle toHandle().getPid()}.
+     *
+     * @return the native process id of the process
      * @throws UnsupportedOperationException if the Process implementation
-     *     does not support this operation
+     *         does not support this operation
      * @since 1.9
      */
     public long getPid() {
-        throw new UnsupportedOperationException();
+        return toHandle().getPid();
+    }
+
+    /**
+     * Returns a {@code CompletableFuture<Process>} for the termination of the Process.
+     * The {@link java.util.concurrent.CompletableFuture} provides the ability
+     * to trigger dependent functions or actions that may be run synchronously
+     * or asynchronously upon process termination.
+     * When the process terminates the CompletableFuture is
+     * {@link java.util.concurrent.CompletableFuture#complete completed} regardless
+     * of the exit status of the process.
+     * <p>
+     * Calling {@code onExit().get()} waits for the process to terminate and returns
+     * the Process. The future can be used to check if the process is
+     * {@link java.util.concurrent.CompletableFuture#isDone done} or to
+     * {@link java.util.concurrent.CompletableFuture#get() wait} for it to terminate.
+     * {@link java.util.concurrent.CompletableFuture#cancel(boolean) Cancelling}
+     * the CompletableFuture does not affect the Process.
+     * <p>
+     * If the process is {@link #isAlive not alive} the {@link CompletableFuture}
+     * returned has been {@link java.util.concurrent.CompletableFuture#complete completed}.
+     * <p>
+     * Processes returned from {@link ProcessBuilder#start} override the
+     * default implementation to provide an efficient mechanism to wait
+     * for process exit.
+     * <p>
+     * @apiNote
+     * Using {@link #onExit() onExit} is an alternative to
+     * {@link #waitFor() waitFor} that enables both additional concurrency
+     * and convenient access to the result of the Process.
+     * Lambda expressions can be used to evaluate the result of the Process
+     * execution.
+     * If there is other processing to be done before the value is used
+     * then {@linkplain #onExit onExit} is a convenient mechanism to
+     * free the current thread and block only if and when the value is needed.
+     * <br>
+     * For example, launching a process to compare two files and get a boolean if they are identical:
+     * <pre> {@code   Process p = new ProcessBuilder("cmp", "f1", "f2").start();
+     *    Future<Boolean> identical = p.onExit().thenApply(p1 -> p1.exitValue() == 0);
+     *    ...
+     *    if (identical.get()) { ... }
+     * }</pre>
+     *
+     * @implSpec
+     * This implementation executes {@link #waitFor()} in a separate thread
+     * repeatedly until it returns successfully. If the execution of
+     * {@code waitFor} is interrupted, the thread's interrupt status is preserved.
+     * <p>
+     * When {@link #waitFor()} returns successfully the CompletableFuture is
+     * {@link java.util.concurrent.CompletableFuture#complete completed} regardless
+     * of the exit status of the process.
+     *
+     * This implementation may consume a lot of memory for thread stacks if a
+     * large number of processes are waited for concurrently.
+     * <p>
+     * External implementations should override this method and provide
+     * a more efficient implementation. For example, to delegate to the underlying
+     * process, it can do the following:
+     * <pre>{@code
+     *    public CompletableFuture<Process> onExit() {
+     *       return delegate.onExit().thenApply(p -> this);
+     *    }
+     * }</pre>
+     *
+     * @return a new {@code CompletableFuture<Process>} for the Process
+     *
+     * @since 1.9
+     */
+    public CompletableFuture<Process> onExit() {
+        return CompletableFuture.supplyAsync(this::waitForInternal);
     }
+
+    /**
+     * Wait for the process to exit by calling {@code waitFor}.
+     * If the thread is interrupted, remember the interrupted state to
+     * be restored before returning. Use ForkJoinPool.ManagedBlocker
+     * so that the number of workers in case ForkJoinPool is used is
+     * compensated when the thread blocks in waitFor().
+     *
+     * @return the Process
+     */
+    private Process waitForInternal() {
+        boolean interrupted = false;
+        while (true) {
+            try {
+                ForkJoinPool.managedBlock(new ForkJoinPool.ManagedBlocker() {
+                    @Override
+                    public boolean block() throws InterruptedException {
+                        waitFor();
+                        return true;
+                    }
+
+                    @Override
+                    public boolean isReleasable() {
+                        return !isAlive();
+                    }
+                });
+                break;
+            } catch (InterruptedException x) {
+                interrupted = true;
+            }
+        }
+        if (interrupted) {
+            Thread.currentThread().interrupt();
+        }
+        return this;
+    }
+
+    /**
+     * Returns a ProcessHandle for the Process.
+     *
+     * {@code Process} objects returned by {@link ProcessBuilder#start} and
+     * {@link Runtime#exec} implement {@code toHandle} as the equivalent of
+     * {@link ProcessHandle#of(long) ProcessHandle.of(pid)} including the
+     * check for a SecurityManager and {@code RuntimePermission("manageProcess")}.
+     *
+     * @implSpec
+     * This implementation throws an instance of
+     * {@link java.lang.UnsupportedOperationException} and performs no other action.
+     * Subclasses should override this method to provide a ProcessHandle for the
+     * process.  The methods {@link #getPid}, {@link #info}, {@link #children},
+     * and {@link #allChildren}, unless overridden, operate on the ProcessHandle.
+     *
+     * @return Returns a ProcessHandle for the Process
+     * @throws UnsupportedOperationException if the Process implementation
+     *         does not support this operation
+     * @throws SecurityException if a security manager has been installed and
+     *         it denies RuntimePermission("manageProcess")
+     * @since 1.9
+     */
+    public ProcessHandle toHandle() {
+        throw new UnsupportedOperationException(this.getClass()
+                + ".toHandle() not supported");
+    }
+
+    /**
+     * Returns a snapshot of information about the process.
+     *
+     * <p> An {@link ProcessHandle.Info} instance has various accessor methods
+     * that return information about the process, if the process is alive and
+     * the information is available, otherwise {@code null} is returned.
+     *
+     * @implSpec
+     * This implementation returns information about the process as:
+     * {@link #toHandle toHandle().info()}.
+     *
+     * @return a snapshot of information about the process, always non-null
+     * @throws UnsupportedOperationException if the Process implementation
+     *         does not support this operation
+     * @since 1.9
+     */
+    public ProcessHandle.Info info() {
+        return toHandle().info();
+    }
+
+    /**
+     * Returns a snapshot of the direct children of the process.
+     * A process that is {@link #isAlive not alive} has zero children.
+     * <p>
+     * <em>Note that processes are created and terminate asynchronously.
+     * There is no guarantee that a process is {@link #isAlive alive}.
+     * </em>
+     *
+     * @implSpec
+     * This implementation returns the direct children as:
+     * {@link #toHandle toHandle().children()}.
+     *
+     * @return a Stream of ProcessHandles for processes that are direct children
+     *         of the process
+     * @throws UnsupportedOperationException if the Process implementation
+     *         does not support this operation
+     * @throws SecurityException if a security manager has been installed and
+     *         it denies RuntimePermission("manageProcess")
+     * @since 1.9
+     */
+    public Stream<ProcessHandle> children() {
+        return toHandle().children();
+    }
+
+    /**
+     * Returns a snapshot of the direct and indirect children of the process.
+     * A process that is {@link #isAlive not alive} has zero children.
+     * <p>
+     * <em>Note that processes are created and terminate asynchronously.
+     * There is no guarantee that a process is {@link #isAlive alive}.
+     * </em>
+     *
+     * @implSpec
+     * This implementation returns all children as:
+     * {@link #toHandle toHandle().allChildren()}.
+     *
+     * @return a Stream of ProcessHandles for processes that are direct and
+     *         indirect children of the process
+     * @throws UnsupportedOperationException if the Process implementation
+     *         does not support this operation
+     * @throws SecurityException if a security manager has been installed and
+     *         it denies RuntimePermission("manageProcess")
+     * @since 1.9
+     */
+    public Stream<ProcessHandle> allChildren() {
+        return toHandle().allChildren();
+    }
+
+
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/java/lang/ProcessHandle.java	Fri May 29 14:04:12 2015 -0400
@@ -0,0 +1,361 @@
+/*
+ * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package java.lang;
+
+import java.time.Duration;
+import java.time.Instant;
+import java.util.Optional;
+import java.util.concurrent.CompletableFuture;
+import java.util.stream.Stream;
+
+/**
+ * ProcessHandle identifies and provides control of native processes. Each
+ * individual process can be monitored for liveness, list its children,
+ * get information about the process or destroy it.
+ * By comparison, {@link java.lang.Process Process} instances were started
+ * by the current process and additionally provide access to the process
+ * input, output, and error streams.
+ * <p>
+ * The native process ID is an identification number that the
+ * operating system assigns to the process.
+ * The range for process id values is dependent on the operating system.
+ * For example, an embedded system might use a 16-bit value.
+ * Status information about a process is retrieved from the native system
+ * and may change asynchronously; processes may be created or terminate
+ * spontaneously.
+ * The time between when a process terminates and the process id
+ * is reused for a new process is unpredictable.
+ * Race conditions can exist between checking the status of a process and
+ * acting upon it. When using ProcessHandles avoid assumptions
+ * about the liveness or identity of the underlying process.
+ * <p>
+ * Each ProcessHandle identifies and allows control of a process in the native
+ * system. ProcessHandles are returned from the factory methods {@link #current()},
+ * {@link #of(long)},
+ * {@link #children}, {@link #allChildren}, {@link #parent()} and
+ * {@link #allProcesses()}.
+ * <p>
+ * The {@link Process} instances created by {@link ProcessBuilder} can be queried
+ * for a ProcessHandle that provides information about the Process.
+ * ProcessHandle references should not be freely distributed.
+ *
+ * <p>
+ * A {@link java.util.concurrent.CompletableFuture} available from {@link #onExit}
+ * can be used to wait for process termination, and possibly trigger dependent
+ * actions.
+ * <p>
+ * The factory methods limit access to ProcessHandles using the
+ * SecurityManager checking the {@link RuntimePermission RuntimePermission("manageProcess")}.
+ * The ability to control processes is also restricted by the native system,
+ * ProcessHandle provides no more access to, or control over, the native process
+ * than would be allowed by a native application.
+ * <p>
+ * @implSpec
+ * In the case where ProcessHandles cannot be supported then the factory
+ * methods must consistently throw {@link java.lang.UnsupportedOperationException}.
+ * The methods of this class throw {@link java.lang.UnsupportedOperationException}
+ * if the operating system does not allow access to query or kill a process.
+ *
+ * @see Process
+ * @since 1.9
+ */
+public interface ProcessHandle extends Comparable<ProcessHandle> {
+
+    /**
+     * Returns the native process ID of the process. The native process ID is an
+     * identification number that the operating system assigns to the process.
+     *
+     * @return the native process ID of the process
+     * @throws UnsupportedOperationException if the implementation
+     *         does not support this operation
+     */
+    long getPid();
+
+    /**
+     * Returns an {@code Optional<ProcessHandle>} for an existing native process.
+     *
+     * @param pid a native process ID
+     * @return an {@code Optional<ProcessHandle>} of the PID for the process;
+     *         the {@code Optional} is empty if the process does not exist
+     * @throws SecurityException if a security manager has been installed and
+     *         it denies RuntimePermission("manageProcess")
+     * @throws UnsupportedOperationException if the implementation
+     *         does not support this operation
+     */
+    public static Optional<ProcessHandle> of(long pid) {
+        return ProcessHandleImpl.get(pid);
+    }
+
+    /**
+     * Returns a ProcessHandle for the current process. The ProcessHandle cannot be
+     * used to destroy the current process, use {@link System#exit System.exit} instead.
+     *
+     * @return a ProcessHandle for the current process
+     * @throws SecurityException if a security manager has been installed and
+     *         it denies RuntimePermission("manageProcess")
+     * @throws UnsupportedOperationException if the implementation
+     *         does not support this operation
+     */
+    public static ProcessHandle current() {
+        return ProcessHandleImpl.current();
+    }
+
+    /**
+     * Returns an {@code Optional<ProcessHandle>} for the parent process.
+     * Note that Processes in a zombie state usually don't have a parent.
+     *
+     * @return an {@code Optional<ProcessHandle>} of the parent process;
+     *         the {@code Optional} is empty if the child process does not have a parent
+     *         or if the parent is not available, possibly due to operating system limitations
+     * @throws SecurityException if a security manager has been installed and
+     *         it denies RuntimePermission("manageProcess")
+     */
+    Optional<ProcessHandle> parent();
+
+    /**
+     * Returns a snapshot of the current direct children of the process.
+     * A process that is {@link #isAlive not alive} has zero children.
+     * <p>
+     * <em>Note that processes are created and terminate asynchronously.
+     * There is no guarantee that a process is {@link #isAlive alive}.
+     * </em>
+     *
+     * @return a Stream of ProcessHandles for processes that are direct children
+     *         of the process
+     * @throws SecurityException if a security manager has been installed and
+     *         it denies RuntimePermission("manageProcess")
+     */
+    Stream<ProcessHandle> children();
+
+    /**
+     * Returns a snapshot of the current direct and indirect children of the process.
+     * A process that is {@link #isAlive not alive} has zero children.
+     * <p>
+     * <em>Note that processes are created and terminate asynchronously.
+     * There is no guarantee that a process is {@link #isAlive alive}.
+     * </em>
+     *
+     * @return a Stream of ProcessHandles for processes that are direct and
+     *         indirect children of the process
+     * @throws SecurityException if a security manager has been installed and
+     *         it denies RuntimePermission("manageProcess")
+     */
+    Stream<ProcessHandle> allChildren();
+
+    /**
+     * Returns a snapshot of all processes visible to the current process.
+     * <p>
+     * <em>Note that processes are created and terminate asynchronously. There
+     * is no guarantee that a process in the stream is alive or that no other
+     * processes may have been created since the inception of the snapshot.
+     * </em>
+     *
+     * @return a Stream of ProcessHandles for all processes
+     * @throws SecurityException if a security manager has been installed and
+     *         it denies RuntimePermission("manageProcess")
+     * @throws UnsupportedOperationException if the implementation
+     *         does not support this operation
+     */
+    static Stream<ProcessHandle> allProcesses() {
+        return ProcessHandleImpl.children(0);
+    }
+
+    /**
+     * Returns a snapshot of information about the process.
+     *
+     * <p> An {@code Info} instance has various accessor methods that return
+     * information about the process, if the process is alive and the
+     * information is available.
+     *
+     * @return a snapshot of information about the process, always non-null
+     */
+    Info info();
+
+    /**
+     * Information snapshot about the process.
+     * The attributes of a process vary by operating system and are not available
+     * in all implementations.  Information about processes is limited
+     * by the operating system privileges of the process making the request.
+     * The return types are {@code Optional<T>} allowing explicit tests
+     * and actions if the value is available.
+     * @since 1.9
+     */
+    public interface Info {
+        /**
+         * Returns the executable pathname of the process.
+         *
+         * @return an {@code Optional<String>} of the executable pathname
+         *         of the process
+         */
+        public Optional<String> command();
+
+        /**
+         * Returns an array of Strings of the arguments of the process.
+         *
+         * @return an {@code Optional<String[]>} of the arguments of the process
+         */
+        public Optional<String[]> arguments();
+
+        /**
+         * Returns the start time of the process.
+         *
+         * @return an {@code Optional<Instant>} of the start time of the process
+         */
+        public Optional<Instant> startInstant();
+
+        /**
+         * Returns the total cputime accumulated of the process.
+         *
+         * @return an {@code Optional<Duration>} for the accumulated total cputime
+         */
+        public Optional<Duration> totalCpuDuration();
+
+        /**
+         * Return the user of the process.
+         *
+         * @return an {@code Optional<String>} for the user of the process
+         */
+        public Optional<String> user();
+    }
+
+    /**
+     * Returns a {@code CompletableFuture<ProcessHandle>} for the termination
+     * of the process.
+     * The {@link java.util.concurrent.CompletableFuture} provides the ability
+     * to trigger dependent functions or actions that may be run synchronously
+     * or asynchronously upon process termination.
+     * When the process terminates the CompletableFuture is
+     * {@link java.util.concurrent.CompletableFuture#complete completed} regardless
+     * of the exit status of the process.
+     * The {@code onExit} method can be called multiple times to invoke
+     * independent actions when the process exits.
+     * <p>
+     * Calling {@code onExit().get()} waits for the process to terminate and returns
+     * the ProcessHandle. The future can be used to check if the process is
+     * {@link java.util.concurrent.CompletableFuture#isDone done} or to
+     * {@link java.util.concurrent.Future#get() wait} for it to terminate.
+     * {@link java.util.concurrent.Future#cancel(boolean) Cancelling}
+     * the CompleteableFuture does not affect the Process.
+     * <p>
+     * If the process is {@link #isAlive not alive} the {@link CompletableFuture}
+     * returned has been {@link java.util.concurrent.CompletableFuture#complete completed}.
+     *
+     * @return a new {@code CompletableFuture<ProcessHandle>} for the ProcessHandle
+     *
+     * @throws IllegalStateException if the process is the current process
+     */
+    CompletableFuture<ProcessHandle> onExit();
+
+    /**
+     * Returns {@code true} if the implementation of {@link #destroy}
+     * normally terminates the process.
+     * Returns {@code false} if the implementation of {@code destroy}
+     * forcibly and immediately terminates the process.
+     *
+     * @return {@code true} if the implementation of {@link #destroy}
+     *         normally terminates the process;
+     *         otherwise, {@link #destroy} forcibly terminates the process
+     */
+    boolean supportsNormalTermination();
+
+    /**
+     * Requests the process to be killed.
+     * Whether the process represented by this {@code ProcessHandle} object is
+     * {@link #supportsNormalTermination normally terminated} or not is
+     * implementation dependent.
+     * Forcible process destruction is defined as the immediate termination of the
+     * process, whereas normal termination allows the process to shut down cleanly.
+     * If the process is not alive, no action is taken.
+     * The operating system access controls may prevent the process
+     * from being killed.
+     * <p>
+     * The {@link java.util.concurrent.CompletableFuture} from {@link #onExit} is
+     * {@link java.util.concurrent.CompletableFuture#complete completed}
+     * when the process has terminated.
+     * <p>
+     * Note: The process may not terminate immediately.
+     * For example, {@code isAlive()} may return true for a brief period
+     * after {@code destroy()} is called.
+     *
+     * @return {@code true} if termination was successfully requested,
+     *         otherwise {@code false}
+     * @throws IllegalStateException if the process is the current process
+     */
+    boolean destroy();
+
+    /**
+     * Requests the process to be killed forcibly.
+     * The process represented by this {@code ProcessHandle} object is
+     * forcibly terminated.
+     * Forcible process destruction is defined as the immediate termination of the
+     * process, whereas normal termination allows the process to shut down cleanly.
+     * If the process is not alive, no action is taken.
+     * The operating system access controls may prevent the process
+     * from being killed.
+     * <p>
+     * The {@link java.util.concurrent.CompletableFuture} from {@link #onExit} is
+     * {@link java.util.concurrent.CompletableFuture#complete completed}
+     * when the process has terminated.
+     * <p>
+     * Note: The process may not terminate immediately.
+     * For example, {@code isAlive()} may return true for a brief period
+     * after {@code destroyForcibly()} is called.
+     *
+     * @return {@code true} if termination was successfully requested,
+     *         otherwise {@code false}
+     * @throws IllegalStateException if the process is the current process
+     */
+    boolean destroyForcibly();
+
+    /**
+     * Tests whether the process represented by this {@code ProcessHandle} is alive.
+     * Process termination is implementation and operating system specific.
+     * The process is considered alive as long as the PID is valid.
+     *
+     * @return {@code true} if the process represented by this
+     *         {@code ProcessHandle} object has not yet terminated
+     */
+   boolean isAlive();
+
+    /**
+     * Compares this ProcessHandle with the specified ProcessHandle for order.
+     * The order is not specified, but is consistent with {@link Object#equals},
+     * which returns {@code true} if and only if two instances of ProcessHandle
+     * are of the same implementation and represent the same system process.
+     * Comparison is only supported among objects of same implementation.
+     * If attempt is made to mutually compare two different implementations
+     * of {@link ProcessHandle}s, {@link ClassCastException} is thrown.
+     *
+     * @param other the ProcessHandle to be compared
+     * @return a negative integer, zero, or a positive integer as this object
+     * is less than, equal to, or greater than the specified object.
+     * @throws NullPointerException if the specified object is null
+     * @throws ClassCastException if the specified object is not of same class
+     *         as this object
+     */
+    @Override
+    int compareTo(ProcessHandle other);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/java/lang/ProcessHandleImpl.java	Fri May 29 14:04:12 2015 -0400
@@ -0,0 +1,528 @@
+/*
+ * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package java.lang;
+
+import java.security.PrivilegedAction;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.Arrays;
+import java.util.Optional;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ForkJoinPool;
+import java.util.concurrent.SynchronousQueue;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Stream;
+
+import sun.misc.InnocuousThread;
+
+import static java.security.AccessController.doPrivileged;
+
+/**
+ * ProcessHandleImpl is the implementation of ProcessHandle.
+ *
+ * @see Process
+ * @since 1.9
+ */
+final class ProcessHandleImpl implements ProcessHandle {
+
+    /**
+     * The thread pool of "process reaper" daemon threads.
+     */
+    private static final Executor processReaperExecutor =
+            doPrivileged((PrivilegedAction<Executor>) () -> {
+
+                ThreadGroup tg = Thread.currentThread().getThreadGroup();
+                while (tg.getParent() != null) tg = tg.getParent();
+                ThreadGroup systemThreadGroup = tg;
+
+                ThreadFactory threadFactory = grimReaper -> {
+                    // Our thread stack requirement is quite modest.
+                    Thread t = new Thread(systemThreadGroup, grimReaper,
+                            "process reaper", 32768);
+                    t.setDaemon(true);
+                    // A small attempt (probably futile) to avoid priority inversion
+                    t.setPriority(Thread.MAX_PRIORITY);
+                    return t;
+                };
+
+                return Executors.newCachedThreadPool(threadFactory);
+            });
+
+    private static class ExitCompletion extends CompletableFuture<Integer> {
+        final boolean isReaping;
+
+        ExitCompletion(boolean isReaping) {
+            this.isReaping = isReaping;
+        }
+    }
+
+    private static final ConcurrentMap<Long, ExitCompletion>
+        completions = new ConcurrentHashMap<>();
+
+    /**
+     * Returns a CompletableFuture that completes with process exit status when
+     * the process completes.
+     *
+     * @param shouldReap true if the exit value should be reaped
+     */
+    static CompletableFuture<Integer> completion(long pid, boolean shouldReap) {
+        // check canonicalizing cache 1st
+        ExitCompletion completion = completions.get(pid);
+        // re-try until we get a completion that shouldReap => isReaping
+        while (completion == null || (shouldReap && !completion.isReaping)) {
+            ExitCompletion newCompletion = new ExitCompletion(shouldReap);
+            if (completion == null) {
+                completion = completions.putIfAbsent(pid, newCompletion);
+            } else {
+                completion = completions.replace(pid, completion, newCompletion)
+                    ? null : completions.get(pid);
+            }
+            if (completion == null) {
+                // newCompletion has just been installed successfully
+                completion = newCompletion;
+                // spawn a thread to wait for and deliver the exit value
+                processReaperExecutor.execute(() -> {
+                    int exitValue = waitForProcessExit0(pid, shouldReap);
+                    newCompletion.complete(exitValue);
+                    // remove from cache afterwards
+                    completions.remove(pid, newCompletion);
+                });
+            }
+        }
+        return completion;
+    }
+
+    @Override
+    public CompletableFuture<ProcessHandle> onExit() {
+        if (this.equals(current)) {
+            throw new IllegalStateException("onExit for current process not allowed");
+        }
+
+        return ProcessHandleImpl.completion(getPid(), false)
+                .handleAsync((exitStatus, unusedThrowable) -> this);
+    }
+
+    /**
+     * Wait for the process to exit, return the value.
+     * Conditionally reap the value if requested
+     * @param pid the processId
+     * @param reapvalue if true, the value is retrieved,
+     *                   else return the value and leave the process waitable
+     *
+     * @return the value or -1 if an error occurs
+     */
+    private static native int waitForProcessExit0(long pid, boolean reapvalue);
+
+    /**
+     * Cache the ProcessHandle of this process.
+     */
+    private static final ProcessHandleImpl current =
+            new ProcessHandleImpl(getCurrentPid0());
+
+    /**
+     * The pid of this ProcessHandle.
+     */
+    private final long pid;
+
+    /**
+     * Private constructor.  Instances are created by the {@code get(long)} factory.
+     * @param pid the pid for this instance
+     */
+    private ProcessHandleImpl(long pid) {
+        this.pid = pid;
+    }
+
+    /**
+     * Returns a ProcessHandle for an existing native process.
+     *
+     * @param pid the native process identifier
+     * @return The ProcessHandle for the pid if the process is alive;
+     *      or {@code null} if the process ID does not exist in the native system.
+     * @throws SecurityException if RuntimePermission("manageProcess") is not granted
+     */
+    static Optional<ProcessHandle> get(long pid) {
+        SecurityManager sm = System.getSecurityManager();
+        if (sm != null) {
+            sm.checkPermission(new RuntimePermission("manageProcess"));
+        }
+        return Optional.ofNullable(isAlive0(pid) ? new ProcessHandleImpl(pid) : null);
+    }
+
+    /**
+     * Returns a ProcessHandle corresponding known to exist pid.
+     * Called from ProcessImpl, it does not perform a security check or check if the process is alive.
+     * @param pid of the known to exist process
+     * @return a ProcessHandle corresponding to an existing Process instance
+     */
+    static ProcessHandle getUnchecked(long pid) {
+        return new ProcessHandleImpl(pid);
+    }
+
+    /**
+     * Returns the native process ID.
+     * A {@code long} is used to be able to fit the system specific binary values
+     * for the process.
+     *
+     * @return the native process ID
+     */
+    @Override
+    public long getPid() {
+        return pid;
+    }
+
+    /**
+     * Returns the ProcessHandle for the current native process.
+     *
+     * @return The ProcessHandle for the OS process.
+     * @throws SecurityException if RuntimePermission("manageProcess") is not granted
+     */
+    public static ProcessHandleImpl current() {
+        SecurityManager sm = System.getSecurityManager();
+        if (sm != null) {
+            sm.checkPermission(new RuntimePermission("manageProcess"));
+        }
+        return current;
+    }
+
+    /**
+     * Return the pid of the current process.
+     *
+     * @return the pid of the  current process
+     */
+    private static native long getCurrentPid0();
+
+    /**
+     * Returns a ProcessHandle for the parent process.
+     *
+     * @return a ProcessHandle of the parent process; {@code null} is returned
+     *         if the child process does not have a parent
+     * @throws SecurityException           if permission is not granted by the
+     *                                     security policy
+     */
+    static Optional<ProcessHandle> parent(long pid) {
+        SecurityManager sm = System.getSecurityManager();
+        if (sm != null) {
+            sm.checkPermission(new RuntimePermission("manageProcess"));
+        }
+        long ppid = parent0(pid);
+        if (ppid <= 0) {
+            return Optional.empty();
+        }
+        return get(ppid);
+    }
+
+    /**
+     * Returns the parent of the native pid argument.
+     *
+     * @return the parent of the native pid; if any, otherwise -1
+     */
+    private static native long parent0(long pid);
+
+    /**
+     * Returns the number of pids filled in to the array.
+     * @param pid if {@code pid} equals zero, then all known processes are returned;
+     *      otherwise only direct child process pids are returned
+     * @param pids an allocated long array to receive the pids
+     * @param ppids an allocated long array to receive the parent pids; may be null
+     * @return if greater than or equals to zero is the number of pids in the array;
+     *      if greater than the length of the arrays, the arrays are too small
+     */
+    private static native int getProcessPids0(long pid, long[] pids, long[] ppids);
+
+    /**
+     * Destroy the process for this ProcessHandle.
+     * @param pid the processs ID to destroy
+     * @param force {@code true} if the process should be terminated forcibly;
+     *     else {@code false} for a normal termination
+     */
+    static void destroyProcess(long pid, boolean force) {
+        destroy0(pid, force);
+    }
+
+    private static native boolean destroy0(long pid, boolean forcibly);
+
+    @Override
+    public boolean destroy() {
+        if (this.equals(current)) {
+            throw new IllegalStateException("destroy of current process not allowed");
+        }
+        return destroy0(getPid(), false);
+    }
+
+    @Override
+    public boolean destroyForcibly() {
+        if (this.equals(current)) {
+            throw new IllegalStateException("destroy of current process not allowed");
+        }
+        return destroy0(getPid(), true);
+    }
+
+
+    @Override
+    public boolean supportsNormalTermination() {
+        return ProcessImpl.SUPPORTS_NORMAL_TERMINATION;
+    }
+
+    /**
+     * Tests whether the process represented by this {@code ProcessHandle} is alive.
+     *
+     * @return {@code true} if the process represented by this
+     * {@code ProcessHandle} object has not yet terminated.
+     * @since 1.9
+     */
+    @Override
+    public boolean isAlive() {
+        return isAlive0(pid);
+    }
+
+    /**
+     * Returns true or false depending on whether the pid is alive.
+     * This must not reap the exitValue like the isAlive method above.
+     *
+     * @param pid the pid to check
+     * @return true or false
+     */
+    private static native boolean isAlive0(long pid);
+
+    @Override
+    public Optional<ProcessHandle> parent() {
+        return parent(pid);
+    }
+
+    @Override
+    public Stream<ProcessHandle> children() {
+        return children(pid);
+    }
+
+    /**
+     * Returns a Stream of the children of a process or all processes.
+     *
+     * @param pid the pid of the process for which to find the children;
+     *            0 for all processes
+     * @return a stream of ProcessHandles
+     */
+    static Stream<ProcessHandle> children(long pid) {
+        SecurityManager sm = System.getSecurityManager();
+        if (sm != null) {
+            sm.checkPermission(new RuntimePermission("manageProcess"));
+        }
+        int size = 100;
+        long[] childpids = null;
+        while (childpids == null || size > childpids.length) {
+            childpids = new long[size];
+            size = getProcessPids0(pid, childpids, null);
+        }
+        return Arrays.stream(childpids, 0, size).mapToObj((id) -> new ProcessHandleImpl(id));
+    }
+
+    @Override
+    public Stream<ProcessHandle> allChildren() {
+        SecurityManager sm = System.getSecurityManager();
+        if (sm != null) {
+            sm.checkPermission(new RuntimePermission("manageProcess"));
+        }
+        int size = 100;
+        long[] pids = null;
+        long[] ppids = null;
+        while (pids == null || size > pids.length) {
+            pids = new long[size];
+            ppids = new long[size];
+            size = getProcessPids0(0, pids, ppids);
+        }
+
+        int next = 0;       // index of next process to check
+        int count = -1;     // count of subprocesses scanned
+        long ppid = pid;    // start looking for this parent
+        do {
+            // Scan from next to size looking for ppid
+            // if found, exchange it to index next
+            for (int i = next; i < size; i++) {
+                if (ppids[i] == ppid) {
+                    swap(pids, i, next);
+                    swap(ppids, i, next);
+                    next++;
+                }
+            }
+            ppid = pids[++count];   // pick up the next pid to scan for
+        } while (count < next);
+
+        return Arrays.stream(pids, 0, count).mapToObj((id) -> new ProcessHandleImpl(id));
+    }
+
+    // Swap two elements in an array
+    private static void swap(long[] array, int x, int y) {
+        long v = array[x];
+        array[x] = array[y];
+        array[y] = v;
+    }
+
+    @Override
+    public ProcessHandle.Info info() {
+        return ProcessHandleImpl.Info.info(pid);
+    }
+
+    @Override
+    public int compareTo(ProcessHandle other) {
+        return Long.compare(pid, ((ProcessHandleImpl) other).pid);
+    }
+
+    @Override
+    public String toString() {
+        return Long.toString(pid);
+    }
+
+    @Override
+    public int hashCode() {
+        return Long.hashCode(pid);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        return (obj instanceof ProcessHandleImpl) &&
+            (pid == ((ProcessHandleImpl) obj).pid);
+    }
+
+    /**
+     * Implementation of ProcessHandle.Info.
+     * Information snapshot about a process.
+     * The attributes of a process vary by operating system and not available
+     * in all implementations.  Additionally, information about other processes
+     * is limited by the operating system privileges of the process making the request.
+     * If a value is not available, either a {@code null} or {@code -1} is stored.
+     * The accessor methods return {@code null} if the value is not available.
+     */
+    static class Info implements ProcessHandle.Info {
+        static {
+            initIDs();
+        }
+
+        /**
+         * Initialization of JNI fieldIDs.
+         */
+        private static native void initIDs();
+
+        /**
+         * Fill in this Info instance with information about the native process.
+         * If values are not available the native code does not modify the field.
+         * @param pid  of the native process
+         */
+        private native void info0(long pid);
+
+        String command;
+        String[] arguments;
+        long startTime;
+        long totalTime;
+        String user;
+
+        Info() {
+            command = null;
+            arguments = null;
+            startTime = -1L;
+            totalTime = -1L;
+            user = null;
+        }
+
+        /**
+         * Returns the Info object with the fields from the process.
+         * Whatever fields are provided by native are returned.
+         *
+         * @param pid the native process identifier
+         * @return ProcessHandle.Info non-null; individual fields may be null
+         *          or -1 if not available.
+         */
+        public static ProcessHandle.Info info(long pid) {
+            Info info = new Info();
+            info.info0(pid);
+            return info;
+        }
+
+        @Override
+        public Optional<String> command() {
+            return Optional.ofNullable(command);
+        }
+
+        @Override
+        public Optional<String[]> arguments() {
+            return Optional.ofNullable(arguments);
+        }
+
+        @Override
+        public Optional<Instant> startInstant() {
+            return (startTime > 0)
+                    ? Optional.of(Instant.ofEpochMilli(startTime))
+                    : Optional.empty();
+        }
+
+        @Override
+        public Optional<Duration> totalCpuDuration() {
+            return (totalTime != -1)
+                    ? Optional.of(Duration.ofNanos(totalTime))
+                    : Optional.empty();
+        }
+
+        @Override
+        public Optional<String> user() {
+            return Optional.ofNullable(user);
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder sb = new StringBuilder(60);
+            sb.append('[');
+            if (user != null) {
+                sb.append("user: ");
+                sb.append(user());
+            }
+            if (command != null) {
+                if (sb.length() != 0) sb.append(", ");
+                sb.append("cmd: ");
+                sb.append(command);
+            }
+            if (arguments != null && arguments.length > 0) {
+                if (sb.length() != 0) sb.append(", ");
+                sb.append("args: ");
+                sb.append(Arrays.toString(arguments));
+            }
+            if (startTime != -1) {
+                if (sb.length() != 0) sb.append(", ");
+                sb.append("startTime: ");
+                sb.append(startInstant());
+            }
+            if (totalTime != -1) {
+                if (sb.length() != 0) sb.append(", ");
+                sb.append("totalTime: ");
+                sb.append(totalCpuDuration().toString());
+            }
+            sb.append(']');
+            return sb.toString();
+        }
+    }
+}
--- a/jdk/src/java.base/share/classes/java/lang/RuntimePermission.java	Fri May 29 10:28:28 2015 -0700
+++ b/jdk/src/java.base/share/classes/java/lang/RuntimePermission.java	Fri May 29 14:04:12 2015 -0400
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 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
@@ -333,6 +333,12 @@
  *   "../../../technotes/guides/plugin/developer_guide/rsa_how.html#use">
  *   usePolicy Permission</a>.</td>
  * </tr>
+ * <tr>
+ *   <td>manageProcess</td>
+ *   <td>Native process termination and information about processes
+ *       {@link ProcessHandle}.</td>
+ *   <td>Allows code to identify and terminate processes that it did not create.</td>
+ * </tr>
  *
  * <tr>
  *   <td>localeServiceProvider</td>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/solaris/native/libjava/ProcessHandleImpl_solaris.c	Fri May 29 14:04:12 2015 -0400
@@ -0,0 +1,371 @@
+/*
+ * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "jni.h"
+#include "jni_util.h"
+#include "java_lang_ProcessHandleImpl.h"
+#include "java_lang_ProcessHandleImpl_Info.h"
+
+
+#include <stdio.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <procfs.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <limits.h>
+
+/**
+ * Implementations of ProcessHandleImpl functions that are
+ * NOT common to all Unix variants:
+ * - getProcessPids0(pid, pidArray)
+ *
+ * Implementations of ProcessHandleImpl_Info
+ * - totalTime, startTime
+ * - Command
+ * - Arguments
+ */
+
+/*
+ * Signatures for internal OS specific functions.
+ */
+static pid_t parentPid(JNIEnv *env, pid_t pid);
+static void getStatInfo(JNIEnv *env, jobject jinfo, pid_t pid);
+static void getCmdlineInfo(JNIEnv *env, jobject jinfo, pid_t pid);
+
+extern jstring uidToUser(JNIEnv* env, uid_t uid);
+
+/* Field id for jString 'command' in java.lang.ProcessHandle.Info */
+static jfieldID ProcessHandleImpl_Info_commandID;
+
+/* Field id for jString[] 'arguments' in java.lang.ProcessHandle.Info */
+static jfieldID ProcessHandleImpl_Info_argumentsID;
+
+/* Field id for jlong 'totalTime' in java.lang.ProcessHandle.Info */
+static jfieldID ProcessHandleImpl_Info_totalTimeID;
+
+/* Field id for jlong 'startTime' in java.lang.ProcessHandle.Info */
+static jfieldID ProcessHandleImpl_Info_startTimeID;
+
+/* Field id for jString 'user' in java.lang.ProcessHandleImpl.Info */
+static jfieldID ProcessHandleImpl_Info_userID;
+
+/* static value for clock ticks per second. */
+static long clock_ticks_per_second;
+
+/**************************************************************
+ * Static method to initialize field IDs and the ticks per second rate.
+ *
+ * Class:     java_lang_ProcessHandleImpl_Info
+ * Method:    initIDs
+ * Signature: ()V
+ */
+JNIEXPORT void JNICALL Java_java_lang_ProcessHandleImpl_00024Info_initIDs
+  (JNIEnv *env, jclass clazz) {
+
+    CHECK_NULL(ProcessHandleImpl_Info_commandID = (*env)->GetFieldID(env,
+        clazz, "command", "Ljava/lang/String;"));
+    CHECK_NULL(ProcessHandleImpl_Info_argumentsID = (*env)->GetFieldID(env,
+        clazz, "arguments", "[Ljava/lang/String;"));
+    CHECK_NULL(ProcessHandleImpl_Info_totalTimeID = (*env)->GetFieldID(env,
+        clazz, "totalTime", "J"));
+    CHECK_NULL(ProcessHandleImpl_Info_startTimeID = (*env)->GetFieldID(env,
+        clazz, "startTime", "J"));
+    CHECK_NULL(ProcessHandleImpl_Info_userID = (*env)->GetFieldID(env,
+        clazz, "user", "Ljava/lang/String;"));
+    clock_ticks_per_second = sysconf(_SC_CLK_TCK);
+}
+
+/*
+ * Returns the parent pid of the requested pid.
+ *
+ * Class:     java_lang_ProcessHandleImpl
+ * Method:    parent0
+ * Signature: (J)J
+ */
+JNIEXPORT jlong JNICALL Java_java_lang_ProcessHandleImpl_parent0
+(JNIEnv *env, jobject obj, jlong jpid) {
+    pid_t pid = (pid_t) jpid;
+    pid_t ppid = -1;
+
+    if (pid == getpid()) {
+        ppid = getppid();
+    } else {
+        ppid = parentPid(env, pid);
+    }
+    return (jlong) ppid;
+}
+
+/*
+ * Returns the children of the requested pid and optionally each parent.
+ *
+ * Class:     java_lang_ProcessHandleImpl
+ * Method:    getChildPids
+ * Signature: (J[J)I
+ *
+ * Reads /proc and accumulates any process who parent pid matches.
+ * The resulting pids are stored into the array of longs.
+ * The number of pids is returned if they all fit.
+ * If the array is too short, the desired length is returned.
+ */
+JNIEXPORT jint JNICALL Java_java_lang_ProcessHandleImpl_getProcessPids0
+(JNIEnv *env, jclass clazz, jlong jpid,
+    jlongArray jarray, jlongArray jparentArray)
+{
+    DIR* dir;
+    struct dirent* ptr;
+    pid_t pid = (pid_t) jpid;
+    size_t count = 0;
+    jlong* pids = NULL;
+    jlong* ppids = NULL;
+    size_t parentArraySize = 0;
+    size_t arraySize = 0;
+
+    arraySize = (*env)->GetArrayLength(env, jarray);
+    JNU_CHECK_EXCEPTION_RETURN(env, 0);
+    if (jparentArray != NULL) {
+        parentArraySize = (*env)->GetArrayLength(env, jparentArray);
+        JNU_CHECK_EXCEPTION_RETURN(env, 0);
+
+        if (arraySize != parentArraySize) {
+            JNU_ThrowIllegalArgumentException(env, "array sizes not equal");
+            return 0;
+        }
+    }
+
+    /*
+     * To locate the children we scan /proc looking for files that have a
+     * positive integer as a filename.
+     */
+    if ((dir = opendir("/proc")) == NULL) {
+        JNU_ThrowByNameWithLastError(env,
+            "java/lang/Runtime", "Unable to open /proc");
+        return 0;
+    }
+
+    do { // Block to break out of on Exception
+        pids = (*env)->GetLongArrayElements(env, jarray, NULL);
+        if (pids == NULL) {
+            break;
+        }
+        if (jparentArray != NULL) {
+            ppids  = (*env)->GetLongArrayElements(env, jparentArray, NULL);
+            if (ppids == NULL) {
+                break;
+            }
+        }
+
+        while ((ptr = readdir(dir)) != NULL) {
+            pid_t ppid;
+
+            /* skip files that aren't numbers */
+            pid_t childpid = (pid_t) atoi(ptr->d_name);
+            if ((int) childpid <= 0) {
+                continue;
+            }
+
+            ppid = 0;
+            if (pid != 0 || jparentArray != NULL) {
+                // parentPid opens and reads /proc/pid/stat
+                ppid = parentPid(env, childpid);
+            }
+            if (pid == 0 || ppid == pid) {
+                if (count < arraySize) {
+                    // Only store if it fits
+                    pids[count] = (jlong) childpid;
+
+                    if (ppids != NULL) {
+                        // Store the parentPid
+                        ppids[count] = (jlong) ppid;
+                    }
+                }
+                count++; // Count to tabulate size needed
+            }
+        }
+    } while (0);
+
+    if (pids != NULL) {
+        (*env)->ReleaseLongArrayElements(env, jarray, pids, 0);
+    }
+    if (ppids != NULL) {
+        (*env)->ReleaseLongArrayElements(env, jparentArray, ppids, 0);
+    }
+
+    closedir(dir);
+    // If more pids than array had size for; count will be greater than array size
+    return count;
+}
+
+/*
+ * Returns the parent pid of a given pid, or -1 if not found
+ */
+static pid_t parentPid(JNIEnv *env, pid_t pid) {
+    FILE* fp;
+    pstatus_t pstatus;
+    int statlen;
+    char fn[32];
+    int i, p;
+    char* s;
+
+    /*
+     * Try to open /proc/%d/status
+     */
+    snprintf(fn, sizeof fn, "/proc/%d/status", pid);
+    fp = fopen(fn, "r");
+    if (fp == NULL) {
+        return -1;
+    }
+
+    /*
+     * The format is: pid (command) state ppid ...
+     * As the command could be anything we must find the right most
+     * ")" and then skip the white spaces that follow it.
+     */
+    statlen = fread(&pstatus, 1, (sizeof pstatus), fp);
+    fclose(fp);
+    if (statlen < 0) {
+        return -1;
+    }
+    return (pid_t) pstatus.pr_ppid;
+}
+
+/**************************************************************
+ * Implementation of ProcessHandleImpl_Info native methods.
+ */
+
+/*
+ * Fill in the Info object from the OS information about the process.
+ *
+ * Class:     java_lang_ProcessHandleImpl_Info
+ * Method:    info0
+ * Signature: (J)V
+ */
+JNIEXPORT void JNICALL Java_java_lang_ProcessHandleImpl_00024Info_info0
+  (JNIEnv *env, jobject jinfo, jlong jpid) {
+    pid_t pid = (pid_t) jpid;
+    getStatInfo(env, jinfo, pid);
+    getCmdlineInfo(env, jinfo, pid);
+}
+
+/**
+ * Read /proc/<pid>/stat and fill in the fields of the Info object.
+ * Gather the user and system times.
+ */
+static void getStatInfo(JNIEnv *env, jobject jinfo, pid_t pid) {
+    FILE* fp;
+    pstatus_t pstatus;
+    struct stat stat_buf;
+    int ret;
+    char fn[32];
+    int i, p;
+    char* s;
+    jlong totalTime;
+
+    /*
+     * Try to open /proc/%d/status
+     */
+    snprintf(fn, sizeof fn, "/proc/%d/status", pid);
+
+    if (stat(fn, &stat_buf) < 0) {
+        return;
+    }
+
+    fp = fopen(fn, "r");
+    if (fp == NULL) {
+        return;
+    }
+
+    ret = fread(&pstatus, 1, (sizeof pstatus), fp);
+    fclose(fp);
+    if (ret < 0) {
+        return;
+    }
+
+    totalTime = pstatus.pr_utime.tv_sec * 1000000000L + pstatus.pr_utime.tv_nsec +
+                pstatus.pr_stime.tv_sec * 1000000000L + pstatus.pr_stime.tv_nsec;
+
+    (*env)->SetLongField(env, jinfo, ProcessHandleImpl_Info_totalTimeID, totalTime);
+    JNU_CHECK_EXCEPTION(env);
+}
+
+static void getCmdlineInfo(JNIEnv *env, jobject jinfo, pid_t pid) {
+    FILE* fp;
+    psinfo_t psinfo;
+    int ret;
+    char fn[32];
+    char exePath[PATH_MAX];
+    int i, p;
+    jlong startTime;
+    jobjectArray cmdArray;
+    jstring str = NULL;
+
+    /*
+     * try to open /proc/%d/psinfo
+     */
+    snprintf(fn, sizeof fn, "/proc/%d/psinfo", pid);
+    fp = fopen(fn, "r");
+    if (fp == NULL) {
+        return;
+    }
+
+    /*
+     * The format is: pid (command) state ppid ...
+     * As the command could be anything we must find the right most
+     * ")" and then skip the white spaces that follow it.
+     */
+    ret = fread(&psinfo, 1, (sizeof psinfo), fp);
+    fclose(fp);
+    if (ret < 0) {
+        return;
+    }
+
+    CHECK_NULL((str = uidToUser(env, psinfo.pr_uid)));
+    (*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_userID, str);
+    JNU_CHECK_EXCEPTION(env);
+
+    startTime = (jlong)psinfo.pr_start.tv_sec * (jlong)1000 +
+                (jlong)psinfo.pr_start.tv_nsec / 1000000;
+    (*env)->SetLongField(env, jinfo, ProcessHandleImpl_Info_startTimeID, startTime);
+    JNU_CHECK_EXCEPTION(env);
+
+    /*
+     * The path to the executable command is the link in /proc/<pid>/paths/a.out.
+     */
+    snprintf(fn, sizeof fn, "/proc/%d/path/a.out", pid);
+    if ((ret = readlink(fn, exePath, PATH_MAX - 1)) < 0) {
+        return;
+    }
+
+    // null terminate and create String to store for command
+    exePath[ret] = '\0';
+    CHECK_NULL(str = JNU_NewStringPlatform(env, exePath));
+    (*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_commandID, str);
+    JNU_CHECK_EXCEPTION(env);
+}
+
--- a/jdk/src/java.base/unix/classes/java/lang/ProcessImpl.java	Fri May 29 10:28:28 2015 -0700
+++ b/jdk/src/java.base/unix/classes/java/lang/ProcessImpl.java	Fri May 29 14:04:12 2015 -0400
@@ -39,9 +39,7 @@
 import java.util.EnumSet;
 import java.util.Locale;
 import java.util.Set;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Executor;
-import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.TimeUnit;
 import java.security.AccessController;
 import static java.security.AccessController.doPrivileged;
@@ -50,8 +48,7 @@
 import java.security.PrivilegedExceptionAction;
 
 /**
- * This java.lang.Process subclass in the UNIX environment is for the exclusive use of
- * ProcessBuilder.start() to create new processes.
+ * java.lang.Process subclass in the UNIX environment.
  *
  * @author Mario Wolczko and Ross Knippel.
  * @author Konstantin Kladko (ported to Linux and Bsd)
@@ -63,12 +60,16 @@
     private static final sun.misc.JavaIOFileDescriptorAccess fdAccess
         = sun.misc.SharedSecrets.getJavaIOFileDescriptorAccess();
 
+    // Linux platforms support a normal (non-forcible) kill signal.
+    static final boolean SUPPORTS_NORMAL_TERMINATION = true;
+
     private final int pid;
+    private final ProcessHandle processHandle;
     private int exitcode;
     private boolean hasExited;
 
     private /* final */ OutputStream stdin;
-    private /* final */ InputStream stdout;
+    private /* final */ InputStream  stdout;
     private /* final */ InputStream  stderr;
 
     // only used on Solaris
@@ -97,7 +98,7 @@
         Platform(LaunchMechanism ... launchMechanisms) {
             this.defaultLaunchMechanism = launchMechanisms[0];
             this.validLaunchMechanisms =
-                    EnumSet.copyOf(Arrays.asList(launchMechanisms));
+                EnumSet.copyOf(Arrays.asList(launchMechanisms));
         }
 
         @SuppressWarnings("fallthrough")
@@ -121,43 +122,43 @@
 
         String helperPath() {
             return AccessController.doPrivileged(
-                    (PrivilegedAction<String>) () ->
-                            helperPath(System.getProperty("java.home"),
-                                    System.getProperty("os.arch"))
+                (PrivilegedAction<String>) () ->
+                    helperPath(System.getProperty("java.home"),
+                               System.getProperty("os.arch"))
             );
         }
 
         LaunchMechanism launchMechanism() {
             return AccessController.doPrivileged(
-                    (PrivilegedAction<LaunchMechanism>) () -> {
-                        String s = System.getProperty(
-                                "jdk.lang.Process.launchMechanism");
-                        LaunchMechanism lm;
-                        if (s == null) {
-                            lm = defaultLaunchMechanism;
-                            s = lm.name().toLowerCase(Locale.ENGLISH);
-                        } else {
-                            try {
-                                lm = LaunchMechanism.valueOf(
-                                        s.toUpperCase(Locale.ENGLISH));
-                            } catch (IllegalArgumentException e) {
-                                lm = null;
-                            }
+                (PrivilegedAction<LaunchMechanism>) () -> {
+                    String s = System.getProperty(
+                        "jdk.lang.Process.launchMechanism");
+                    LaunchMechanism lm;
+                    if (s == null) {
+                        lm = defaultLaunchMechanism;
+                        s = lm.name().toLowerCase(Locale.ENGLISH);
+                    } else {
+                        try {
+                            lm = LaunchMechanism.valueOf(
+                                s.toUpperCase(Locale.ENGLISH));
+                        } catch (IllegalArgumentException e) {
+                            lm = null;
                         }
-                        if (lm == null || !validLaunchMechanisms.contains(lm)) {
-                            throw new Error(
-                                    s + " is not a supported " +
-                                            "process launch mechanism on this platform."
-                            );
-                        }
-                        return lm;
                     }
+                    if (lm == null || !validLaunchMechanisms.contains(lm)) {
+                        throw new Error(
+                            s + " is not a supported " +
+                            "process launch mechanism on this platform."
+                        );
+                    }
+                    return lm;
+                }
             );
         }
 
         static Platform get() {
             String osName = AccessController.doPrivileged(
-                    (PrivilegedAction<String>) () -> System.getProperty("os.name")
+                (PrivilegedAction<String>) () -> System.getProperty("os.name")
             );
 
             if (osName.equals("Linux")) { return LINUX; }
@@ -173,17 +174,14 @@
     private static final LaunchMechanism launchMechanism = platform.launchMechanism();
     private static final byte[] helperpath = toCString(platform.helperPath());
 
-    /* this is for the reaping thread */
-    private native int waitForProcessExit(int pid);
-
     private static byte[] toCString(String s) {
         if (s == null)
             return null;
         byte[] bytes = s.getBytes();
         byte[] result = new byte[bytes.length + 1];
         System.arraycopy(bytes, 0,
-                result, 0,
-                bytes.length);
+                         result, 0,
+                         bytes.length);
         result[result.length-1] = (byte)0;
         return result;
     }
@@ -304,30 +302,7 @@
                                    byte[] dir,
                                    int[] fds,
                                    boolean redirectErrorStream)
-            throws IOException;
-
-    /**
-     * The thread pool of "process reaper" daemon threads.
-     */
-    private static final Executor processReaperExecutor =
-            doPrivileged((PrivilegedAction<Executor>) () -> {
-
-                ThreadGroup tg = Thread.currentThread().getThreadGroup();
-                while (tg.getParent() != null) tg = tg.getParent();
-                ThreadGroup systemThreadGroup = tg;
-
-                ThreadFactory threadFactory = grimReaper -> {
-                    // Our thread stack requirement is quite modest.
-                    Thread t = new Thread(systemThreadGroup, grimReaper,
-                            "process reaper", 32768);
-                    t.setDaemon(true);
-                    // A small attempt (probably futile) to avoid priority inversion
-                    t.setPriority(Thread.MAX_PRIORITY);
-                    return t;
-                };
-
-                return Executors.newCachedThreadPool(threadFactory);
-            });
+        throws IOException;
 
     private ProcessImpl(final byte[] prog,
                 final byte[] argBlock, final int argc,
@@ -338,13 +313,14 @@
             throws IOException {
 
         pid = forkAndExec(launchMechanism.ordinal() + 1,
-                helperpath,
-                prog,
-                argBlock, argc,
-                envBlock, envc,
-                dir,
-                fds,
-                redirectErrorStream);
+                          helperpath,
+                          prog,
+                          argBlock, argc,
+                          envBlock, envc,
+                          dir,
+                          fds,
+                          redirectErrorStream);
+        processHandle = ProcessHandleImpl.getUnchecked(pid);
 
         try {
             doPrivileged((PrivilegedExceptionAction<Void>) () -> {
@@ -371,18 +347,16 @@
                         new ProcessPipeOutputStream(fds[0]);
 
                 stdout = (fds[1] == -1) ?
-                        ProcessBuilder.NullInputStream.INSTANCE :
-                        new ProcessPipeInputStream(fds[1]);
+                         ProcessBuilder.NullInputStream.INSTANCE :
+                         new ProcessPipeInputStream(fds[1]);
 
                 stderr = (fds[2] == -1) ?
-                        ProcessBuilder.NullInputStream.INSTANCE :
-                        new ProcessPipeInputStream(fds[2]);
+                         ProcessBuilder.NullInputStream.INSTANCE :
+                         new ProcessPipeInputStream(fds[2]);
 
-                processReaperExecutor.execute(() -> {
-                    int exitcode = waitForProcessExit(pid);
-
+                ProcessHandleImpl.completion(pid, true).handle((exitcode, throwable) -> {
                     synchronized (this) {
-                        this.exitcode = exitcode;
+                        this.exitcode = (exitcode == null) ? -1 : exitcode.intValue();
                         this.hasExited = true;
                         this.notifyAll();
                     }
@@ -395,6 +369,8 @@
 
                     if (stdin instanceof ProcessPipeOutputStream)
                         ((ProcessPipeOutputStream) stdin).processExited();
+
+                    return null;
                 });
                 break;
 
@@ -402,18 +378,18 @@
                 stdin = (fds[0] == -1) ?
                         ProcessBuilder.NullOutputStream.INSTANCE :
                         new BufferedOutputStream(
-                                new FileOutputStream(newFileDescriptor(fds[0])));
+                            new FileOutputStream(newFileDescriptor(fds[0])));
 
                 stdout = (fds[1] == -1) ?
-                        ProcessBuilder.NullInputStream.INSTANCE :
-                        new BufferedInputStream(
-                                stdout_inner_stream =
-                                        new DeferredCloseInputStream(
-                                                newFileDescriptor(fds[1])));
+                         ProcessBuilder.NullInputStream.INSTANCE :
+                         new BufferedInputStream(
+                             stdout_inner_stream =
+                                 new DeferredCloseInputStream(
+                                     newFileDescriptor(fds[1])));
 
                 stderr = (fds[2] == -1) ?
-                        ProcessBuilder.NullInputStream.INSTANCE :
-                        new DeferredCloseInputStream(newFileDescriptor(fds[2]));
+                         ProcessBuilder.NullInputStream.INSTANCE :
+                         new DeferredCloseInputStream(newFileDescriptor(fds[2]));
 
                 /*
                  * For each subprocess forked a corresponding reaper task
@@ -423,14 +399,13 @@
                  * exitStatus() to be safely executed in parallel (and they
                  * need no native code).
                  */
-                processReaperExecutor.execute(() -> {
-                    int exitcode = waitForProcessExit(pid);
-
+                ProcessHandleImpl.completion(pid, true).handle((exitcode, throwable) -> {
                     synchronized (this) {
-                        this.exitcode = exitcode;
+                        this.exitcode = (exitcode == null) ? -1 : exitcode.intValue();
                         this.hasExited = true;
                         this.notifyAll();
                     }
+                    return null;
                 });
                 break;
 
@@ -440,18 +415,16 @@
                         new ProcessPipeOutputStream(fds[0]);
 
                 stdout = (fds[1] == -1) ?
-                        ProcessBuilder.NullInputStream.INSTANCE :
-                        new DeferredCloseProcessPipeInputStream(fds[1]);
+                         ProcessBuilder.NullInputStream.INSTANCE :
+                         new DeferredCloseProcessPipeInputStream(fds[1]);
 
                 stderr = (fds[2] == -1) ?
-                        ProcessBuilder.NullInputStream.INSTANCE :
-                        new DeferredCloseProcessPipeInputStream(fds[2]);
+                         ProcessBuilder.NullInputStream.INSTANCE :
+                         new DeferredCloseProcessPipeInputStream(fds[2]);
 
-                processReaperExecutor.execute(() -> {
-                    int exitcode = waitForProcessExit(pid);
-
+                ProcessHandleImpl.completion(pid, true).handle((exitcode, throwable) -> {
                     synchronized (this) {
-                        this.exitcode = exitcode;
+                        this.exitcode = (exitcode == null) ? -1 : exitcode.intValue();
                         this.hasExited = true;
                         this.notifyAll();
                     }
@@ -464,6 +437,8 @@
 
                     if (stdin instanceof ProcessPipeOutputStream)
                         ((ProcessPipeOutputStream) stdin).processExited();
+
+                    return null;
                 });
                 break;
 
@@ -492,7 +467,7 @@
 
     @Override
     public synchronized boolean waitFor(long timeout, TimeUnit unit)
-            throws InterruptedException
+        throws InterruptedException
     {
         long remainingNanos = unit.toNanos(timeout);    // throw NPE before other conditions
         if (hasExited) return true;
@@ -517,8 +492,6 @@
         return exitcode;
     }
 
-    private static native void destroyProcess(int pid, boolean force);
-
     private void destroy(boolean force) {
         switch (platform) {
             case LINUX:
@@ -532,7 +505,7 @@
                 // soon, so this is quite safe.
                 synchronized (this) {
                     if (!hasExited)
-                        destroyProcess(pid, force);
+                        ProcessHandleImpl.destroyProcess(pid, force);
                 }
                 try { stdin.close();  } catch (IOException ignored) {}
                 try { stdout.close(); } catch (IOException ignored) {}
@@ -548,14 +521,14 @@
                 // soon, so this is quite safe.
                 synchronized (this) {
                     if (!hasExited)
-                        destroyProcess(pid, force);
+                        ProcessHandleImpl.destroyProcess(pid, force);
                     try {
                         stdin.close();
                         if (stdout_inner_stream != null)
                             stdout_inner_stream.closeDeferred(stdout);
                         if (stderr instanceof DeferredCloseInputStream)
                             ((DeferredCloseInputStream) stderr)
-                                    .closeDeferred(stderr);
+                                .closeDeferred(stderr);
                     } catch (IOException e) {
                         // ignore
                     }
@@ -566,6 +539,27 @@
         }
     }
 
+    @Override
+    public CompletableFuture<Process> onExit() {
+        return ProcessHandleImpl.completion(pid, false)
+                .handleAsync((exitStatus, unusedThrowable) -> this);
+    }
+
+    @Override
+    public ProcessHandle toHandle() {
+        SecurityManager sm = System.getSecurityManager();
+        if (sm != null) {
+            sm.checkPermission(new RuntimePermission("manageProcess"));
+        }
+        return processHandle;
+    }
+
+    @Override
+    public boolean supportsNormalTermination() {
+        return ProcessImpl.SUPPORTS_NORMAL_TERMINATION;
+    }
+
+    @Override
     public void destroy() {
         destroy(false);
     }
@@ -629,8 +623,8 @@
                         byte[] stragglers = drainInputStream(in);
                         in.close();
                         this.in = (stragglers == null) ?
-                                ProcessBuilder.NullInputStream.INSTANCE :
-                                new ByteArrayInputStream(stragglers);
+                            ProcessBuilder.NullInputStream.INSTANCE :
+                            new ByteArrayInputStream(stragglers);
                     }
                 } catch (IOException ignored) {}
             }
@@ -797,7 +791,7 @@
      *
      */
     private static class DeferredCloseProcessPipeInputStream
-            extends BufferedInputStream {
+        extends BufferedInputStream {
 
         private final Object closeLock = new Object();
         private int useCount = 0;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/unix/native/libjava/ProcessHandleImpl_unix.c	Fri May 29 14:04:12 2015 -0400
@@ -0,0 +1,769 @@
+/*
+ * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include "jni.h"
+#include "jni_util.h"
+#include "java_lang_ProcessHandleImpl.h"
+#include "java_lang_ProcessHandleImpl_Info.h"
+
+
+#include <stdio.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include <string.h>
+#include <dirent.h>
+#include <ctype.h>
+
+/**
+ * Implementations of ProcessHandleImpl functions that are common to all
+ * Unix variants:
+ * - waitForProcessExit0(pid, reap)
+ * - getCurrentPid0()
+ * - destroy0(pid, force)
+ */
+
+
+#ifndef WIFEXITED
+#define WIFEXITED(status) (((status)&0xFF) == 0)
+#endif
+
+#ifndef WEXITSTATUS
+#define WEXITSTATUS(status) (((status)>>8)&0xFF)
+#endif
+
+#ifndef WIFSIGNALED
+#define WIFSIGNALED(status) (((status)&0xFF) > 0 && ((status)&0xFF00) == 0)
+#endif
+
+#ifndef WTERMSIG
+#define WTERMSIG(status) ((status)&0x7F)
+#endif
+
+#define RESTARTABLE(_cmd, _result) do { \
+  do { \
+    _result = _cmd; \
+  } while((_result == -1) && (errno == EINTR)); \
+} while(0)
+
+#define RESTARTABLE_RETURN_PTR(_cmd, _result) do { \
+  do { \
+    _result = _cmd; \
+  } while((_result == NULL) && (errno == EINTR)); \
+} while(0)
+
+
+/* Block until a child process exits and return its exit code.
+ * Note, can only be called once for any given pid if reapStatus = true.
+ */
+JNIEXPORT jint JNICALL
+Java_java_lang_ProcessHandleImpl_waitForProcessExit0(JNIEnv* env,
+                                              jclass junk,
+                                              jlong jpid,
+                                              jboolean reapStatus)
+{
+    pid_t pid = (pid_t)jpid;
+    errno = 0;
+
+    if (reapStatus != JNI_FALSE) {
+        /* Wait for the child process to exit.
+         * waitpid() is standard, so use it on all POSIX platforms.
+         * It is known to work when blocking to wait for the pid
+         * This returns immediately if the child has already exited.
+         */
+        int status;
+        while (waitpid(pid, &status, 0) < 0) {
+            switch (errno) {
+                case ECHILD: return 0;
+                case EINTR: break;
+                default: return -1;
+            }
+        }
+
+        if (WIFEXITED(status)) {
+            return WEXITSTATUS(status);
+        } else if (WIFSIGNALED(status)) {
+            /* The child exited because of a signal.
+             * The best value to return is 0x80 + signal number,
+             * because that is what all Unix shells do, and because
+             * it allows callers to distinguish between process exit and
+             * process death by signal.
+             * Unfortunately, the historical behavior on Solaris is to return
+             * the signal number, and we preserve this for compatibility. */
+#ifdef __solaris__
+            return WTERMSIG(status);
+#else
+            return 0x80 + WTERMSIG(status);
+#endif
+        } else {
+            return status;
+        }
+     } else {
+        /*
+         * Wait for the child process to exit without reaping the exitValue.
+         * waitid() is standard on all POSIX platforms.
+         * Note: waitid on Mac OS X 10.7 seems to be broken;
+         * it does not return the exit status consistently.
+         */
+        siginfo_t siginfo;
+        int options = WEXITED |  WNOWAIT;
+        memset(&siginfo, 0, sizeof siginfo);
+        while (waitid(P_PID, pid, &siginfo, options) < 0) {
+            switch (errno) {
+            case ECHILD: return 0;
+            case EINTR: break;
+            default: return -1;
+            }
+        }
+
+        if (siginfo.si_code == CLD_EXITED) {
+             /*
+              * The child exited normally; get its exit code.
+              */
+             return siginfo.si_status;
+        } else if (siginfo.si_code == CLD_KILLED || siginfo.si_code == CLD_DUMPED) {
+             /* The child exited because of a signal.
+              * The best value to return is 0x80 + signal number,
+              * because that is what all Unix shells do, and because
+              * it allows callers to distinguish between process exit and
+              * process death by signal.
+              * Unfortunately, the historical behavior on Solaris is to return
+              * the signal number, and we preserve this for compatibility. */
+ #ifdef __solaris__
+             return WTERMSIG(siginfo.si_status);
+ #else
+             return 0x80 + WTERMSIG(siginfo.si_status);
+ #endif
+        } else {
+             /*
+              * Unknown exit code; pass it through.
+              */
+             return siginfo.si_status;
+        }
+    }
+}
+
+/*
+ * Class:     java_lang_ProcessHandleImpl
+ * Method:    getCurrentPid0
+ * Signature: ()J
+ */
+JNIEXPORT jlong JNICALL Java_java_lang_ProcessHandleImpl_getCurrentPid0
+(JNIEnv *env, jclass clazz) {
+    pid_t pid = getpid();
+    return (jlong) pid;
+}
+
+/*
+ * Class:     java_lang_ProcessHandleImpl
+ * Method:    isAlive0
+ * Signature: (J)Z
+ */
+JNIEXPORT jboolean JNICALL Java_java_lang_ProcessHandleImpl_isAlive0
+(JNIEnv *env, jobject obj, jlong jpid) {
+    pid_t pid = (pid_t) jpid;
+    return (kill(pid, 0) < 0) ? JNI_FALSE : JNI_TRUE;
+}
+
+/*
+ * Class:     java_lang_ProcessHandleImpl
+ * Method:    destroy0
+ * Signature: (Z)Z
+ */
+JNIEXPORT jboolean JNICALL Java_java_lang_ProcessHandleImpl_destroy0
+(JNIEnv *env, jobject obj, jlong jpid, jboolean force) {
+    pid_t pid = (pid_t) jpid;
+    int sig = (force == JNI_TRUE) ? SIGKILL : SIGTERM;
+    return (kill(pid, sig) >= 0);
+
+}
+
+/**
+ * Size of password or group entry when not available via sysconf
+ */
+#define ENT_BUF_SIZE   1024
+
+/**
+ * Return a strong username for the uid_t or null.
+ */
+jstring uidToUser(JNIEnv* env, uid_t uid) {
+    int result = 0;
+    int buflen;
+    char* pwbuf;
+    jstring name = NULL;
+
+    /* allocate buffer for password record */
+    buflen = (int)sysconf(_SC_GETPW_R_SIZE_MAX);
+    if (buflen == -1)
+        buflen = ENT_BUF_SIZE;
+    pwbuf = (char*)malloc(buflen);
+    if (pwbuf == NULL) {
+        JNU_ThrowOutOfMemoryError(env, "Unable to open getpwent");
+    } else {
+        struct passwd pwent;
+        struct passwd* p = NULL;
+
+#ifdef __solaris__
+        RESTARTABLE_RETURN_PTR(getpwuid_r(uid, &pwent, pwbuf, (size_t)buflen), p);
+#else
+        RESTARTABLE(getpwuid_r(uid, &pwent, pwbuf, (size_t)buflen, &p), result);
+#endif
+
+        // Return the Java String if a name was found
+        if (result == 0 && p != NULL &&
+            p->pw_name != NULL && *(p->pw_name) != '\0') {
+            name = JNU_NewStringPlatform(env, p->pw_name);
+        }
+        free(pwbuf);
+    }
+    return name;
+}
+
+/**
+ * Implementations of ProcessHandleImpl functions that are common to
+ * (some) Unix variants:
+ * - getProcessPids0(pid, pidArray, parentArray)
+ */
+
+#if defined(__linux__) || defined(__AIX__)
+
+/*
+ * Signatures for internal OS specific functions.
+ */
+static pid_t parentPid(JNIEnv *env, pid_t pid);
+static jint getChildren(JNIEnv *env, jlong jpid,
+                        jlongArray array, jlongArray jparentArray);
+
+static void getStatInfo(JNIEnv *env, jobject jinfo, pid_t pid);
+static void getCmdlineInfo(JNIEnv *env, pid_t pid, jobject jinfo);
+static long long getBoottime(JNIEnv *env);
+
+jstring uidToUser(JNIEnv* env, uid_t uid);
+
+/* Field id for jString 'command' in java.lang.ProcessHandleImpl.Info */
+static jfieldID ProcessHandleImpl_Info_commandID;
+
+/* Field id for jString[] 'arguments' in java.lang.ProcessHandleImpl.Info */
+static jfieldID ProcessHandleImpl_Info_argumentsID;
+
+/* Field id for jlong 'totalTime' in java.lang.ProcessHandleImpl.Info */
+static jfieldID ProcessHandleImpl_Info_totalTimeID;
+
+/* Field id for jlong 'startTime' in java.lang.ProcessHandleImpl.Info */
+static jfieldID ProcessHandleImpl_Info_startTimeID;
+
+/* Field id for jString 'user' in java.lang.ProcessHandleImpl.Info */
+static jfieldID ProcessHandleImpl_Info_userID;
+
+/* static value for clock ticks per second. */
+static long clock_ticks_per_second;
+
+/* A static offset in milliseconds since boot. */
+static long long bootTime_ms;
+
+/**************************************************************
+ * Static method to initialize field IDs and the ticks per second rate.
+ *
+ * Class:     java_lang_ProcessHandleImpl_Info
+ * Method:    initIDs
+ * Signature: ()V
+ */
+JNIEXPORT void JNICALL Java_java_lang_ProcessHandleImpl_00024Info_initIDs
+  (JNIEnv *env, jclass clazz) {
+
+    CHECK_NULL(ProcessHandleImpl_Info_commandID = (*env)->GetFieldID(env,
+        clazz, "command", "Ljava/lang/String;"));
+    CHECK_NULL(ProcessHandleImpl_Info_argumentsID = (*env)->GetFieldID(env,
+        clazz, "arguments", "[Ljava/lang/String;"));
+    CHECK_NULL(ProcessHandleImpl_Info_totalTimeID = (*env)->GetFieldID(env,
+        clazz, "totalTime", "J"));
+    CHECK_NULL(ProcessHandleImpl_Info_startTimeID = (*env)->GetFieldID(env,
+        clazz, "startTime", "J"));
+    CHECK_NULL(ProcessHandleImpl_Info_userID = (*env)->GetFieldID(env,
+        clazz, "user", "Ljava/lang/String;"));
+    clock_ticks_per_second = sysconf(_SC_CLK_TCK);
+    bootTime_ms = getBoottime(env);
+}
+
+/*
+ * Returns the parent pid of the requested pid.
+ *
+ * Class:     java_lang_ProcessHandleImpl
+ * Method:    parent0
+ * Signature: (J)J
+ */
+JNIEXPORT jlong JNICALL Java_java_lang_ProcessHandleImpl_parent0
+(JNIEnv *env, jobject obj, jlong jpid) {
+    pid_t pid = (pid_t) jpid;
+    pid_t ppid = -1;
+
+    pid_t mypid = getpid();
+    if (pid == mypid) {
+        ppid = getppid();
+    } else {
+        ppid = parentPid(env, pid);
+    }
+    return (jlong) ppid;
+}
+
+/*
+ * Returns the children of the requested pid and optionally each parent.
+ *
+ * Class:     java_lang_ProcessHandleImpl
+ * Method:    getChildPids
+ * Signature: (J[J[J)I
+ */
+JNIEXPORT jint JNICALL Java_java_lang_ProcessHandleImpl_getProcessPids0
+(JNIEnv *env, jclass clazz, jlong jpid,
+    jlongArray jarray, jlongArray jparentArray) {
+    return getChildren(env, jpid, jarray, jparentArray);
+}
+
+/*
+ * Reads /proc and accumulates any process who parent pid matches.
+ * The resulting pids are stored into the array of longs.
+ * The number of pids is returned if they all fit.
+ * If the array is too short, the negative of the desired length is returned.
+ */
+static jint getChildren(JNIEnv *env, jlong jpid,
+    jlongArray jarray, jlongArray jparentArray) {
+    DIR* dir;
+    struct dirent* ptr;
+    pid_t pid = (pid_t) jpid;
+    pid_t ppid = 0;
+    size_t count = 0;
+    jlong* pids = NULL;
+    jlong* ppids = NULL;
+    size_t parentArraySize = 0;
+    size_t arraySize = 0;
+
+    arraySize = (*env)->GetArrayLength(env, jarray);
+    JNU_CHECK_EXCEPTION_RETURN(env, -1);
+    if (jparentArray != NULL) {
+        parentArraySize = (*env)->GetArrayLength(env, jparentArray);
+        JNU_CHECK_EXCEPTION_RETURN(env, -1);
+
+        if (arraySize != parentArraySize) {
+            JNU_ThrowIllegalArgumentException(env, "array sizes not equal");
+            return 0;
+        }
+    }
+
+    /*
+     * To locate the children we scan /proc looking for files that have a
+     * position integer as a filename.
+     */
+    if ((dir = opendir("/proc")) == NULL) {
+        JNU_ThrowByNameWithLastError(env,
+            "java/lang/Runtime", "Unable to open /proc");
+        return -1;
+    }
+
+    do { // Block to break out of on Exception
+        pids = (*env)->GetLongArrayElements(env, jarray, NULL);
+        if (pids == NULL) {
+            break;
+        }
+        if (jparentArray != NULL) {
+            ppids  = (*env)->GetLongArrayElements(env, jparentArray, NULL);
+            if (ppids == NULL) {
+                break;
+            }
+        }
+
+        while ((ptr = readdir(dir)) != NULL) {
+            /* skip files that aren't numbers */
+            pid_t childpid = (pid_t) atoi(ptr->d_name);
+            if ((int) childpid <= 0) {
+                continue;
+            }
+
+            ppid = 0;
+            if (pid != 0 || jparentArray != NULL) {
+                // parentPid opens and reads /proc/pid/stat
+                ppid = parentPid(env, childpid);
+            }
+            if (pid == 0 || ppid == pid) {
+                if (count < arraySize) {
+                    // Only store if it fits
+                    pids[count] = (jlong) childpid;
+
+                    if (ppids != NULL) {
+                        // Store the parentPid
+                        ppids[count] = (jlong) ppid;
+                    }
+                }
+                count++; // Count to tabulate size needed
+            }
+        }
+    } while (0);
+
+    if (pids != NULL) {
+        (*env)->ReleaseLongArrayElements(env, jarray, pids, 0);
+    }
+    if (ppids != NULL) {
+        (*env)->ReleaseLongArrayElements(env, jparentArray, ppids, 0);
+    }
+
+    closedir(dir);
+    // If more pids than array had size for; count will be greater than array size
+    return count;
+}
+
+/*
+ * Returns the parent pid of a given pid, or -1 if not found
+ */
+static pid_t parentPid(JNIEnv *env, pid_t pid) {
+    char state;
+    FILE* fp;
+    char stat[2048];
+    int statlen;
+    char fn[32];
+    int i, p;
+    char* s;
+
+    /*
+     * try to open /proc/%d/stat
+     */
+    snprintf(fn, sizeof fn, "/proc/%d/stat", pid);
+    fp = fopen(fn, "r");
+    if (fp == NULL) {
+        return -1;
+    }
+
+    /*
+     * The format is: pid (command) state ppid ...
+     * As the command could be anything we must find the right most
+     * ")" and then skip the white spaces that follow it.
+     */
+    statlen = fread(stat, 1, (sizeof stat - 1), fp);
+    fclose(fp);
+    if (statlen < 0) {
+        return -1;
+    }
+
+    stat[statlen] = '\0';
+    s = strrchr(stat, ')');
+    if (s == NULL) {
+        return -1;
+    }
+    do s++; while (isspace(*s));
+    i = sscanf(s, "%c %d", &state, &p);
+    if (i != 2) {
+        return (pid_t)-1;
+    }
+    return (pid_t) p;
+}
+
+/**************************************************************
+ * Implementation of ProcessHandleImpl_Info native methods.
+ */
+
+/*
+ * Fill in the Info object from the OS information about the process.
+ *
+ * Class:     java_lang_ProcessHandleImpl_Info
+ * Method:    info0
+ * Signature: (JLjava/lang/ProcessHandle/Info;)I
+ */
+JNIEXPORT void JNICALL Java_java_lang_ProcessHandleImpl_00024Info_info0
+  (JNIEnv *env, jobject jinfo, jlong jpid) {
+    pid_t pid = (pid_t) jpid;
+    getStatInfo(env, jinfo, (pid_t)pid);
+    getCmdlineInfo(env, pid, jinfo);
+}
+
+/**
+ * Read /proc/<pid>/stat and fill in the fields of the Info object.
+ * The executable name, plus the user, system, and start times are gathered.
+ */
+static void getStatInfo(JNIEnv *env, jobject jinfo, pid_t pid) {
+    char state;
+    FILE* fp;
+    char buffer[2048];
+    struct stat stat_buf;
+    int statlen;
+    char fn[32];
+    int i, ppid = -2;
+    char* s;
+    char *cmd;
+    jstring name = NULL;
+    unsigned long userTime = 0;             // clock tics
+    unsigned long totalTime = 0;            // clock tics
+    jlong total = 0;                        // nano seconds
+    unsigned long long startTime = 0;       // microseconds
+
+    /*
+     * Try to stat and then open /proc/%d/stat
+     */
+    snprintf(fn, sizeof fn, "/proc/%d/stat", pid);
+
+    if (stat(fn, &stat_buf) < 0) {
+        return;
+    }
+
+    CHECK_NULL((name = uidToUser(env, stat_buf.st_uid)));
+    (*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_userID, name);
+    JNU_CHECK_EXCEPTION(env);
+
+    fp = fopen(fn, "r");
+    if (fp == NULL) {
+        return;
+    }
+
+    /*
+     * The format is: pid (command) state ppid ...
+     * As the command could be anything we must find the right most
+     * ")" and then skip the white spaces that follow it.
+     */
+    statlen = fread(buffer, 1, (sizeof buffer - 1), fp);
+    fclose(fp);
+    if (statlen < 0) {
+        return;
+    }
+
+    buffer[statlen] = '\0';
+    s = strchr(buffer, '(');
+    if (s == NULL) {
+        return;
+    }
+    // Found start of command, skip to end
+    s++;
+    s = strrchr(s, ')');
+    if (s == NULL) {
+        return;
+    }
+    s++;
+
+    // Scan the needed fields from status, retaining only ppid(4),
+    // utime (14), stime(15), starttime(22)
+    i = sscanf(s, " %c %d %*d %*d %*d %*d %*d %*u %*u %*u %*u %lu %lu %*d %*d %*d %*d %*d %*d %llu",
+            &state, &ppid, &userTime, &totalTime, &startTime);
+    if (i != 5) {
+        return;              // not all values parsed; return error
+    }
+
+    total = (userTime + totalTime) * (jlong)(1000000000 / clock_ticks_per_second);
+
+    startTime = bootTime_ms + ((startTime * 1000) / clock_ticks_per_second);
+
+    (*env)->SetLongField(env, jinfo, ProcessHandleImpl_Info_totalTimeID, total);
+    JNU_CHECK_EXCEPTION(env);
+    (*env)->SetLongField(env, jinfo, ProcessHandleImpl_Info_startTimeID, startTime);
+    JNU_CHECK_EXCEPTION(env);
+}
+
+/**
+ * Construct the argument array by parsing the arguments from the sequence
+ * of arguments. The zero'th arg is the command executable
+ */
+static int fillArgArray(JNIEnv *env, jobject jinfo,
+                        int nargs, char *cp, char *argsEnd, jstring cmdexe) {
+    jobject argsArray;
+    int i;
+
+    if (nargs < 1) {
+        return 0;
+    }
+
+    if (cmdexe == NULL) {
+        // Create a string from arg[0]
+        CHECK_NULL_RETURN((cmdexe = JNU_NewStringPlatform(env, cp)), -1);
+    }
+    (*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_commandID, cmdexe);
+    JNU_CHECK_EXCEPTION_RETURN(env, -3);
+
+    // Create a String array for nargs-1 elements
+    argsArray = (*env)->NewObjectArray(env, nargs - 1, JNU_ClassString(env), NULL);
+    CHECK_NULL_RETURN(argsArray, -1);
+
+    for (i = 0; i < nargs - 1; i++) {
+        jstring str = NULL;
+
+        cp += strnlen(cp, (argsEnd - cp)) + 1;
+        if (cp > argsEnd || *cp == '\0') {
+            return -2;  // Off the end pointer or an empty argument is an error
+        }
+
+        CHECK_NULL_RETURN((str = JNU_NewStringPlatform(env, cp)), -1);
+
+        (*env)->SetObjectArrayElement(env, argsArray, i, str);
+        JNU_CHECK_EXCEPTION_RETURN(env, -3);
+    }
+    (*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_argumentsID, argsArray);
+    JNU_CHECK_EXCEPTION_RETURN(env, -4);
+    return 0;
+}
+
+
+static void getCmdlineInfo(JNIEnv *env, pid_t pid, jobject jinfo) {
+    int fd;
+    int cmdlen = 0;
+    char *cmdline = NULL, *cmdEnd;  // used for command line args and exe
+    jstring cmdexe = NULL;
+    char fn[32];
+
+    /*
+     * Try to open /proc/%d/cmdline
+     */
+    snprintf(fn, sizeof fn, "/proc/%d/cmdline", pid);
+    if ((fd = open(fn, O_RDONLY)) < 0) {
+        return;
+    }
+
+    do {                // Block to break out of on errors
+        int i;
+        char *s;
+
+        cmdline = (char*)malloc(PATH_MAX);
+        if (cmdline == NULL) {
+            break;
+        }
+
+        /*
+         * The path to the executable command is the link in /proc/<pid>/exe.
+         */
+        snprintf(fn, sizeof fn, "/proc/%d/exe", pid);
+        if ((cmdlen = readlink(fn, cmdline, PATH_MAX - 1)) > 0) {
+            // null terminate and create String to store for command
+            cmdline[cmdlen] = '\0';
+            cmdexe = JNU_NewStringPlatform(env, cmdline);
+            (*env)->ExceptionClear(env);        // unconditionally clear any exception
+        }
+
+        /*
+         * The buffer format is the arguments nul terminated with an extra nul.
+         */
+        cmdlen = read(fd, cmdline, PATH_MAX-1);
+        if (cmdlen < 0) {
+            break;
+        }
+
+        // Terminate the buffer and count the arguments
+        cmdline[cmdlen] = '\0';
+        cmdEnd = &cmdline[cmdlen + 1];
+        for (s = cmdline,i = 0; *s != '\0' && (s < cmdEnd); i++) {
+            s += strnlen(s, (cmdEnd - s)) + 1;
+        }
+
+        if (fillArgArray(env, jinfo, i, cmdline, cmdEnd, cmdexe) < 0) {
+            break;
+        }
+    } while (0);
+
+    if (cmdline != NULL) {
+        free(cmdline);
+    }
+    if (fd >= 0) {
+        close(fd);
+    }
+}
+
+/**
+ * Read the boottime from /proc/stat.
+ */
+static long long getBoottime(JNIEnv *env) {
+    FILE *fp;
+    char *line = NULL;
+    size_t len = 0;
+    long long bootTime = 0;
+
+    fp = fopen("/proc/stat", "r");
+    if (fp == NULL) {
+        return -1;
+    }
+
+    while (getline(&line, &len, fp) != -1) {
+        if (sscanf(line, "btime %llu", &bootTime) == 1) {
+            break;
+        }
+    }
+    free(line);
+
+    if (fp != 0) {
+        fclose(fp);
+    }
+
+    return bootTime * 1000;
+}
+
+#endif  //  defined(__linux__) || defined(__AIX__)
+
+
+/* Block until a child process exits and return its exit code.
+   Note, can only be called once for any given pid. */
+JNIEXPORT jint JNICALL
+Java_java_lang_ProcessImpl_waitForProcessExit(JNIEnv* env,
+                                              jobject junk,
+                                              jint pid)
+{
+    /* We used to use waitid() on Solaris, waitpid() on Linux, but
+     * waitpid() is more standard, so use it on all POSIX platforms. */
+    int status;
+    /* Wait for the child process to exit.  This returns immediately if
+       the child has already exited. */
+    while (waitpid(pid, &status, 0) < 0) {
+        switch (errno) {
+        case ECHILD: return 0;
+        case EINTR: break;
+        default: return -1;
+        }
+    }
+
+    if (WIFEXITED(status)) {
+        /*
+         * The child exited normally; get its exit code.
+         */
+        return WEXITSTATUS(status);
+    } else if (WIFSIGNALED(status)) {
+        /* The child exited because of a signal.
+         * The best value to return is 0x80 + signal number,
+         * because that is what all Unix shells do, and because
+         * it allows callers to distinguish between process exit and
+         * process death by signal.
+         * Unfortunately, the historical behavior on Solaris is to return
+         * the signal number, and we preserve this for compatibility. */
+#ifdef __solaris__
+        return WTERMSIG(status);
+#else
+        return 0x80 + WTERMSIG(status);
+#endif
+    } else {
+        /*
+         * Unknown exit code; pass it through.
+         */
+        return status;
+    }
+}
+
+
--- a/jdk/src/java.base/unix/native/libjava/ProcessImpl_md.c	Fri May 29 10:28:28 2015 -0700
+++ b/jdk/src/java.base/unix/native/libjava/ProcessImpl_md.c	Fri May 29 14:04:12 2015 -0400
@@ -226,52 +226,6 @@
 #define WTERMSIG(status) ((status)&0x7F)
 #endif
 
-/* Block until a child process exits and return its exit code.
-   Note, can only be called once for any given pid. */
-JNIEXPORT jint JNICALL
-Java_java_lang_ProcessImpl_waitForProcessExit(JNIEnv* env,
-                                              jobject junk,
-                                              jint pid)
-{
-    /* We used to use waitid() on Solaris, waitpid() on Linux, but
-     * waitpid() is more standard, so use it on all POSIX platforms. */
-    int status;
-    /* Wait for the child process to exit.  This returns immediately if
-       the child has already exited. */
-    while (waitpid(pid, &status, 0) < 0) {
-        switch (errno) {
-        case ECHILD: return 0;
-        case EINTR: break;
-        default: return -1;
-        }
-    }
-
-    if (WIFEXITED(status)) {
-        /*
-         * The child exited normally; get its exit code.
-         */
-        return WEXITSTATUS(status);
-    } else if (WIFSIGNALED(status)) {
-        /* The child exited because of a signal.
-         * The best value to return is 0x80 + signal number,
-         * because that is what all Unix shells do, and because
-         * it allows callers to distinguish between process exit and
-         * process death by signal.
-         * Unfortunately, the historical behavior on Solaris is to return
-         * the signal number, and we preserve this for compatibility. */
-#ifdef __solaris__
-        return WTERMSIG(status);
-#else
-        return 0x80 + WTERMSIG(status);
-#endif
-    } else {
-        /*
-         * Unknown exit code; pass it through.
-         */
-        return status;
-    }
-}
-
 static const char *
 getBytes(JNIEnv *env, jbyteArray arr)
 {
@@ -686,12 +640,3 @@
     goto Finally;
 }
 
-JNIEXPORT void JNICALL
-Java_java_lang_ProcessImpl_destroyProcess(JNIEnv *env,
-                                          jobject junk,
-                                          jint pid,
-                                          jboolean force)
-{
-    int sig = (force == JNI_TRUE) ? SIGKILL : SIGTERM;
-    kill(pid, sig);
-}
--- a/jdk/src/java.base/windows/classes/java/lang/ProcessImpl.java	Fri May 29 10:28:28 2015 -0700
+++ b/jdk/src/java.base/windows/classes/java/lang/ProcessImpl.java	Fri May 29 14:04:12 2015 -0400
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1995, 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
@@ -34,10 +34,12 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.lang.Override;
 import java.lang.ProcessBuilder.Redirect;
 import java.security.AccessController;
 import java.security.PrivilegedAction;
 import java.util.ArrayList;
+import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.TimeUnit;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -53,6 +55,9 @@
     private static final sun.misc.JavaIOFileDescriptorAccess fdAccess
         = sun.misc.SharedSecrets.getJavaIOFileDescriptorAccess();
 
+    // Windows platforms support a forcible kill signal.
+    static final boolean SUPPORTS_NORMAL_TERMINATION = false;
+
     /**
      * Open a file for writing. If {@code append} is {@code true} then the file
      * is opened for atomic append directly and a FileOutputStream constructed
@@ -306,7 +311,8 @@
     }
 
 
-    private long handle = 0;
+    private final long handle;
+    private final ProcessHandle processHandle;
     private OutputStream stdin_stream;
     private InputStream stdout_stream;
     private InputStream stderr_stream;
@@ -385,6 +391,7 @@
 
         handle = create(cmdstr, envblock, path,
                         stdHandles, redirectErrorStream);
+        processHandle = ProcessHandleImpl.getUnchecked(getProcessId0(handle));
 
         java.security.AccessController.doPrivileged(
         new java.security.PrivilegedAction<Void>() {
@@ -481,7 +488,30 @@
     private static native void waitForTimeoutInterruptibly(
         long handle, long timeout);
 
-    public void destroy() { terminateProcess(handle); }
+    @Override
+    public void destroy() {
+        terminateProcess(handle);
+    }
+
+    @Override
+    public CompletableFuture<Process> onExit() {
+        return ProcessHandleImpl.completion(getPid(), false)
+                .handleAsync((exitStatus, unusedThrowable) -> this);
+    }
+
+    @Override
+    public ProcessHandle toHandle() {
+        SecurityManager sm = System.getSecurityManager();
+        if (sm != null) {
+            sm.checkPermission(new RuntimePermission("manageProcess"));
+        }
+        return processHandle;
+    }
+
+    @Override
+    public boolean supportsNormalTermination() {
+        return ProcessImpl.SUPPORTS_NORMAL_TERMINATION;
+    }
 
     @Override
     public Process destroyForcibly() {
@@ -493,8 +523,7 @@
 
     @Override
     public long getPid() {
-        int pid = getProcessId0(handle);
-        return pid;
+        return processHandle.getPid();
     }
 
     private static native int getProcessId0(long handle);
@@ -538,7 +567,7 @@
      * Opens a file for atomic append. The file is created if it doesn't
      * already exist.
      *
-     * @param file the file to open or create
+     * @param path the file to open or create
      * @return the native HANDLE
      */
     private static native long openForAtomicAppend(String path)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/windows/native/libjava/ProcessHandleImpl_win.c	Fri May 29 14:04:12 2015 -0400
@@ -0,0 +1,426 @@
+/*
+ * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+
+#include "jni.h"
+#include "jvm.h"
+#include "jni_util.h"
+#include "java_lang_ProcessHandleImpl.h"
+#include "java_lang_ProcessHandleImpl_Info.h"
+
+#include <windows.h>
+#include <tlhelp32.h>
+
+static void getStatInfo(JNIEnv *env, HANDLE handle, jobject jinfo);
+static void getCmdlineInfo(JNIEnv *env, HANDLE handle, jobject jinfo);
+static void procToUser( JNIEnv *env, HANDLE handle, jobject jinfo);
+
+/**************************************************************
+ * Implementation of ProcessHandleImpl_Info native methods.
+ */
+
+/* Field id for jString 'command' in java.lang.ProcessHandle.Info */
+static jfieldID ProcessHandleImpl_Info_commandID;
+
+/* Field id for jString[] 'arguments' in java.lang.ProcessHandle.Info */
+static jfieldID ProcessHandleImpl_Info_argumentsID;
+
+/* Field id for jlong 'totalTime' in java.lang.ProcessHandle.Info */
+static jfieldID ProcessHandleImpl_Info_totalTimeID;
+
+/* Field id for jlong 'startTime' in java.lang.ProcessHandle.Info */
+static jfieldID ProcessHandleImpl_Info_startTimeID;
+
+/* Field id for jString 'accountName' in java.lang.ProcessHandleImpl.UserPrincipal */
+static jfieldID ProcessHandleImpl_Info_userID;
+
+/**************************************************************
+ * Static method to initialize field IDs.
+ *
+ * Class:     java_lang_ProcessHandleImpl_Info
+ * Method:    initIDs
+ * Signature: ()V
+ */
+JNIEXPORT void JNICALL Java_java_lang_ProcessHandleImpl_00024Info_initIDs
+  (JNIEnv *env, jclass clazz) {
+
+    CHECK_NULL(ProcessHandleImpl_Info_commandID = (*env)->GetFieldID(env,
+        clazz, "command", "Ljava/lang/String;"));
+    CHECK_NULL(ProcessHandleImpl_Info_argumentsID = (*env)->GetFieldID(env,
+        clazz, "arguments", "[Ljava/lang/String;"));
+    CHECK_NULL(ProcessHandleImpl_Info_totalTimeID = (*env)->GetFieldID(env,
+        clazz, "totalTime", "J"));
+    CHECK_NULL(ProcessHandleImpl_Info_startTimeID = (*env)->GetFieldID(env,
+        clazz, "startTime", "J"));
+    CHECK_NULL(ProcessHandleImpl_Info_userID = (*env)->GetFieldID(env,
+        clazz, "user", "Ljava/lang/String;"));
+}
+
+/*
+ * Block until a child process exits and return its exit code.
+ */
+JNIEXPORT jint JNICALL
+Java_java_lang_ProcessHandleImpl_waitForProcessExit0(JNIEnv* env,
+                                              jclass junk,
+                                              jlong jpid,
+                                              jboolean reapStatus) {
+    DWORD pid = (DWORD)jpid;
+    DWORD exitValue = -1;
+    HANDLE handle = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_LIMITED_INFORMATION,
+                                FALSE, pid);
+    if (handle == NULL) {
+        return exitValue;          // No process with that pid is alive
+    }
+    do {
+        if (!GetExitCodeProcess(handle, &exitValue)) {
+            JNU_ThrowByNameWithLastError(env,
+                "java/lang/Runtime", "GetExitCodeProcess");
+            break;
+        }
+        if (exitValue == STILL_ACTIVE) {
+            HANDLE events[2];
+            events[0] = handle;
+            events[1] = JVM_GetThreadInterruptEvent();
+
+            if (WaitForMultipleObjects(sizeof(events)/sizeof(events[0]), events,
+                                       FALSE,    /* Wait for ANY event */
+                                       INFINITE) /* Wait forever */
+                == WAIT_FAILED) {
+                JNU_ThrowByNameWithLastError(env,
+                    "java/lang/Runtime", "WaitForMultipleObjects");
+                break;
+            }
+        }
+    } while (exitValue == STILL_ACTIVE);
+    CloseHandle(handle);         // Ignore return code
+    return exitValue;
+}
+
+/*
+ * Returns the pid of the caller.
+ *
+ * Class:     java_lang_ProcessHandleImpl
+ * Method:    getCurrentPid0
+ * Signature: ()J
+ */
+JNIEXPORT jlong JNICALL Java_java_lang_ProcessHandleImpl_getCurrentPid0
+(JNIEnv *env, jclass clazz) {
+    DWORD  pid = GetCurrentProcessId();
+    return (jlong)pid;
+}
+
+/*
+ * Returns the parent pid of the requested pid.
+ *
+ * Class:     java_lang_ProcessHandleImpl
+ * Method:    parent0
+ * Signature: (J)J
+ */
+JNIEXPORT jlong JNICALL Java_java_lang_ProcessHandleImpl_parent0
+(JNIEnv *env, jclass clazz, jlong jpid) {
+
+    DWORD ppid = -1;
+    DWORD wpid = (DWORD)jpid;
+    PROCESSENTRY32 pe32;
+    HANDLE hProcessSnap;
+
+    // Take a snapshot of all processes in the system.
+    hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
+    if (hProcessSnap == INVALID_HANDLE_VALUE) {
+        JNU_ThrowByName(env,
+            "java/lang/RuntimeException", "snapshot not available");
+        return -1;
+    }
+
+    // Retrieve information about the first process,
+    pe32.dwSize = sizeof (PROCESSENTRY32);
+    if (Process32First(hProcessSnap, &pe32)) {
+        // Now walk the snapshot of processes, and
+        do {
+            if (wpid == pe32.th32ProcessID) {
+                ppid = pe32.th32ParentProcessID;
+                break;
+            }
+        } while (Process32Next(hProcessSnap, &pe32));
+    } else {
+        JNU_ThrowByName(env,
+            "java/lang/RuntimeException", "snapshot not available");
+        return -1;
+    }
+    CloseHandle(hProcessSnap); // Ignore return code
+    return (jlong)ppid;
+}
+
+/*
+ * Returns the children of the requested pid and optionally each parent.
+ *
+ * Class:     java_lang_ProcessHandleImpl
+ * Method:    getChildPids
+ * Signature: (J[J[J)I
+ */
+JNIEXPORT jint JNICALL Java_java_lang_ProcessHandleImpl_getProcessPids0
+(JNIEnv *env, jclass clazz, jlong jpid,
+    jlongArray jarray, jlongArray jparentArray) {
+
+    HANDLE hProcessSnap;
+    PROCESSENTRY32 pe32;
+    DWORD ppid = (DWORD)jpid;
+    size_t count = 0;
+    jlong* pids = NULL;
+    jlong* ppids = NULL;
+    size_t parentArraySize = 0;
+    size_t arraySize = 0;
+
+    arraySize = (*env)->GetArrayLength(env, jarray);
+    JNU_CHECK_EXCEPTION_RETURN(env, -1);
+    if (jparentArray != NULL) {
+        parentArraySize = (*env)->GetArrayLength(env, jparentArray);
+        JNU_CHECK_EXCEPTION_RETURN(env, -1);
+
+        if (arraySize != parentArraySize) {
+            JNU_ThrowIllegalArgumentException(env, "array sizes not equal");
+            return 0;
+        }
+    }
+
+    // Take a snapshot of all processes in the system.
+    hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
+    if (hProcessSnap == INVALID_HANDLE_VALUE) {
+        JNU_ThrowByName(env,
+            "java/lang/RuntimeException", "snapshot not available");
+        return 0;
+    }
+
+    // Retrieve information about the first process,
+    pe32.dwSize = sizeof (PROCESSENTRY32);
+    if (Process32First(hProcessSnap, &pe32)) {
+        do { // Block to break out of on Exception
+            pids = (*env)->GetLongArrayElements(env, jarray, NULL);
+            if (pids == NULL) {
+                break;
+            }
+            if (jparentArray != NULL) {
+                ppids  = (*env)->GetLongArrayElements(env, jparentArray, NULL);
+                if (ppids == NULL) {
+                    break;
+                }
+            }
+            // Now walk the snapshot of processes, and
+            // save information about each process in turn
+            do {
+                if (ppid == 0 ||
+                    (pe32.th32ParentProcessID > 0
+                    && (pe32.th32ParentProcessID == ppid))) {
+                    if (count < arraySize) {
+                        // Only store if it fits
+                        pids[count] = (jlong)pe32.th32ProcessID;
+                        if (ppids != NULL) {
+                            // Store the parentPid
+                            ppids[count] = (jlong) pe32.th32ParentProcessID;
+                        }
+                    }
+                    count++;    // Count to tabulate size needed
+                }
+            } while (Process32Next(hProcessSnap, &pe32));
+        } while (0);
+
+        if (pids != NULL) {
+            (*env)->ReleaseLongArrayElements(env, jarray, pids, 0);
+        }
+        if (ppids != NULL) {
+            (*env)->ReleaseLongArrayElements(env, jparentArray, ppids, 0);
+        }
+    } else {
+        JNU_ThrowByName(env,
+            "java/lang/RuntimeException", "snapshot not available");
+        return 0;
+    }
+    CloseHandle(hProcessSnap);
+    // If more pids than array had size for;  count will be greater than array size
+    return (jint)count;
+}
+
+/*
+ * Destroy the process.
+ *
+ * Class:     java_lang_ProcessHandleImpl
+ * Method:    destroy0
+ * Signature: (Z)V
+ */
+JNIEXPORT jboolean JNICALL Java_java_lang_ProcessHandleImpl_destroy0
+(JNIEnv *env, jclass clazz, jlong jpid, jboolean force) {
+    DWORD pid = (DWORD)jpid;
+    HANDLE handle = OpenProcess(PROCESS_TERMINATE, FALSE, pid);
+    if (handle != NULL) {
+        TerminateProcess(handle, 1);
+        CloseHandle(handle);         // Ignore return code
+        return JNI_TRUE;
+    }
+    return JNI_FALSE;
+}
+
+/*
+ * Class:     java_lang_ProcessHandleImpl
+ * Method:    isAlive0
+ * Signature: (J)Z
+ */
+JNIEXPORT jboolean JNICALL Java_java_lang_ProcessHandleImpl_isAlive0
+(JNIEnv *env, jclass clazz, jlong jpid) {
+    DWORD pid = (DWORD)jpid;
+
+    jboolean ret = JNI_FALSE;
+    HANDLE handle =
+        OpenProcess(THREAD_QUERY_INFORMATION | PROCESS_QUERY_LIMITED_INFORMATION,
+                    FALSE, pid);
+    if (handle != NULL) {
+        DWORD dwExitStatus;
+
+        GetExitCodeProcess(handle, &dwExitStatus);
+        CloseHandle(handle); // Ignore return code
+        ret = (dwExitStatus == STILL_ACTIVE);
+    }
+    return ret;
+}
+
+/**
+ * Assemble a 64 bit value from two 32 bit values.
+ */
+static jlong jlong_from(jint high, jint low) {
+    jlong result = 0;
+    result = ((jlong)high << 32) | ((0x000000000ffffffff) & (jlong)low);
+    return result;
+}
+
+/*
+ * Fill in the Info object from the OS information about the process.
+ *
+ * Class:     java_lang_ProcessHandleImpl
+ * Method:    info0
+ * Signature: (J)V
+ */
+JNIEXPORT void JNICALL Java_java_lang_ProcessHandleImpl_00024Info_info0
+  (JNIEnv *env, jobject jinfo, jlong jpid) {
+    DWORD pid = (DWORD)jpid;
+    int ret = 0;
+    HANDLE handle =
+        OpenProcess(THREAD_QUERY_INFORMATION | PROCESS_QUERY_LIMITED_INFORMATION,
+                    FALSE, pid);
+    if (handle == NULL) {
+        return;
+    }
+    getStatInfo(env, handle, jinfo);
+    getCmdlineInfo(env, handle, jinfo);
+    procToUser(env, handle, jinfo);
+
+    CloseHandle(handle);                // Ignore return code
+}
+
+/**
+ * Read /proc/<pid>/stat and fill in the fields of the Info object.
+ * The executable name, plus the user, system, and start times are gathered.
+ */
+static void getStatInfo(JNIEnv *env, HANDLE handle, jobject jinfo) {
+    FILETIME CreationTime;
+    FILETIME ExitTime;
+    FILETIME KernelTime;
+    FILETIME UserTime;
+    jlong userTime;             // nanoseconds
+    jlong totalTime;            // nanoseconds
+    jlong startTime;            // nanoseconds
+    UserTime.dwHighDateTime = 0;
+    UserTime.dwLowDateTime = 0;
+    KernelTime.dwHighDateTime = 0;
+    KernelTime.dwLowDateTime = 0;
+    CreationTime.dwHighDateTime = 0;
+    CreationTime.dwLowDateTime = 0;
+
+    if (GetProcessTimes(handle, &CreationTime, &ExitTime, &KernelTime, &UserTime)) {
+        userTime = jlong_from(UserTime.dwHighDateTime, UserTime.dwLowDateTime);
+        totalTime = jlong_from( KernelTime.dwHighDateTime, KernelTime.dwLowDateTime);
+        totalTime = (totalTime + userTime) * 100;  // convert sum to nano-seconds
+
+        startTime = jlong_from(CreationTime.dwHighDateTime,
+                               CreationTime.dwLowDateTime) / 10000;
+        startTime -= 11644473600000L; // Rebase Epoch from 1601 to 1970
+
+        (*env)->SetLongField(env, jinfo,
+                             ProcessHandleImpl_Info_totalTimeID, totalTime);
+        JNU_CHECK_EXCEPTION(env);
+        (*env)->SetLongField(env, jinfo,
+                             ProcessHandleImpl_Info_startTimeID, startTime);
+        JNU_CHECK_EXCEPTION(env);
+    }
+}
+
+static void getCmdlineInfo(JNIEnv *env, HANDLE handle, jobject jinfo) {
+    char exeName[1024];
+    int bufsize = sizeof exeName;
+    jstring commandObj;
+
+    if (QueryFullProcessImageName(handle, 0,  exeName, &bufsize)) {
+        commandObj = (*env)->NewStringUTF(env, exeName);
+        CHECK_NULL(commandObj);
+        (*env)->SetObjectField(env, jinfo,
+                               ProcessHandleImpl_Info_commandID, commandObj);
+    }
+}
+
+static void procToUser( JNIEnv *env, HANDLE handle, jobject jinfo) {
+#define TOKEN_LEN 256
+    DWORD token_len = TOKEN_LEN;
+    char token_buf[TOKEN_LEN];
+    TOKEN_USER *token_user = (TOKEN_USER*)token_buf;
+    HANDLE tokenHandle;
+    WCHAR domain[255];
+    WCHAR name[255];
+    DWORD domainLen = sizeof(domain);
+    DWORD nameLen = sizeof(name);
+    SID_NAME_USE use;
+    jstring s;
+    int ret;
+
+    if (!OpenProcessToken(handle, TOKEN_READ, &tokenHandle)) {
+        return;
+    }
+
+    ret = GetTokenInformation(tokenHandle, TokenUser, token_user,
+                              token_len, &token_len);
+    CloseHandle(tokenHandle);           // always close handle
+    if (!ret) {
+        JNU_ThrowByNameWithLastError(env,
+            "java/lang/RuntimeException", "GetTokenInformation");
+        return;
+    }
+
+    if (LookupAccountSidW(NULL, token_user->User.Sid, &name[0], &nameLen,
+                          &domain[0], &domainLen, &use) == 0) {
+        // Name not available
+        return;
+    }
+
+    s = (*env)->NewString(env, (const jchar *)name, (jsize)wcslen(name));
+    CHECK_NULL(s);
+    (*env)->SetObjectField(env, jinfo, ProcessHandleImpl_Info_userID, s);
+}
--- a/jdk/test/TEST.ROOT	Fri May 29 10:28:28 2015 -0700
+++ b/jdk/test/TEST.ROOT	Fri May 29 14:04:12 2015 -0400
@@ -12,7 +12,7 @@
 keys=2d dnd i18n intermittent randomness
 
 # Tests that must run in othervm mode
-othervm.dirs=java/awt java/beans javax/accessibility javax/imageio javax/sound javax/print javax/management com/sun/awt sun/awt sun/java2d sun/pisces javax/xml/jaxp/testng/validation
+othervm.dirs=java/awt java/beans javax/accessibility javax/imageio javax/sound javax/print javax/management com/sun/awt sun/awt sun/java2d sun/pisces javax/xml/jaxp/testng/validation java/lang/ProcessHandle
 
 # Tests that cannot run concurrently
 exclusiveAccess.dirs=java/rmi/Naming java/util/prefs sun/management/jmxremote sun/tools/jstatd sun/security/mscapi java/util/stream javax/rmi
--- a/jdk/test/java/lang/ProcessBuilder/Basic.java	Fri May 29 10:28:28 2015 -0700
+++ b/jdk/test/java/lang/ProcessBuilder/Basic.java	Fri May 29 14:04:12 2015 -0400
@@ -1175,13 +1175,13 @@
         equal(actualPid, expectedPid);
 
         // Test the default implementation of Process.getPid
-        try {
-            DelegatingProcess p = new DelegatingProcess(null);
-            p.getPid();
-            fail("non-overridden Process.getPid method should throw UOE");
-        } catch (UnsupportedOperationException uoe) {
-            // correct
-        }
+        DelegatingProcess p = new DelegatingProcess(null);
+        THROWS(UnsupportedOperationException.class,
+                () -> p.getPid(),
+                () -> p.toHandle(),
+                () -> p.supportsNormalTermination(),
+                () -> p.children(),
+                () -> p.allChildren());
 
     }
 
@@ -2604,7 +2604,7 @@
     static volatile int passed = 0, failed = 0;
     static void pass() {passed++;}
     static void fail() {failed++; Thread.dumpStack();}
-    static void fail(String msg) {System.out.println(msg); fail();}
+    static void fail(String msg) {System.err.println(msg); fail();}
     static void unexpected(Throwable t) {failed++; t.printStackTrace();}
     static void check(boolean cond) {if (cond) pass(); else fail();}
     static void check(boolean cond, String m) {if (cond) pass(); else fail(m);}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/ProcessHandle/Basic.java	Fri May 29 14:04:12 2015 -0400
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
+
+import java.io.IOException;
+import java.util.Optional;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.testng.TestNG;
+import org.testng.annotations.Test;
+
+/*
+ * @test
+ * @summary Basic tests for ProcessHandler
+ * @author Roger Riggs
+ */
+public class Basic {
+    /**
+     * Tests of ProcessHandle.current.
+     */
+    @Test
+    public static void test1() {
+        try {
+            ProcessHandle self = ProcessHandle.current();
+            ProcessHandle self1 = ProcessHandle.current();
+            assertEquals(self, self1); //, "get pid twice should be same %d: %d");
+        } finally {
+            // Cleanup any left over processes
+            ProcessHandle.current().children().forEach(ProcessHandle::destroy);
+        }
+    }
+
+    /**
+     * Tests of ProcessHandle.get.
+     */
+    @Test
+    public static void test2() {
+        try {
+            ProcessHandle self = ProcessHandle.current();
+            long pid = self.getPid();       // known native process id
+            Optional<ProcessHandle> self1 = ProcessHandle.of(pid);
+            assertEquals(self1.get(), self,
+                    "ProcessHandle.of(x.getPid()) should be equal getPid() %d: %d");
+
+            Optional<ProcessHandle> ph = ProcessHandle.of(pid);
+            assertEquals(pid, ph.get().getPid());
+        } finally {
+            // Cleanup any left over processes
+            ProcessHandle.current().children().forEach(ProcessHandle::destroy);
+        }
+    }
+
+    @Test
+    public static void test3() {
+        // Test can get parent of current
+        ProcessHandle ph = ProcessHandle.current();
+        try {
+            Optional<ProcessHandle> pph = ph.parent();
+            assertTrue(pph.isPresent(), "Current has a Parent");
+        } finally {
+            // Cleanup any left over processes
+            ProcessHandle.current().children().forEach(ProcessHandle::destroy);
+        }
+    }
+
+    @Test
+    public static void test4() {
+        try {
+            Process p = new ProcessBuilder("sleep", "0").start();
+            p.waitFor();
+
+            long deadPid = p.getPid();
+            p = null;               // Forget the process
+
+            Optional<ProcessHandle> t = ProcessHandle.of(deadPid);
+            assertFalse(t.isPresent(), "Handle created for invalid pid:" + t);
+        } catch (IOException | InterruptedException ex) {
+            fail("Unexpected exception", ex);
+        } finally {
+            // Cleanup any left over processes
+            ProcessHandle.current().children().forEach(ProcessHandle::destroy);
+        }
+    }
+
+    @Test
+    public static void test5() {
+        // Always contains itself.
+        ProcessHandle current = ProcessHandle.current();
+        List<ProcessHandle> list = ProcessHandle.allProcesses().collect(Collectors.toList());
+        if (!list.stream()
+                .anyMatch(ph -> ph.equals(ProcessHandle.current()))) {
+            System.out.printf("current: %s%n", current);
+            System.out.printf("all processes.size: %d%n", list.size());
+            list.forEach(p -> ProcessUtil.printProcess(p, "   allProcesses: "));
+            fail("current process not found in all processes");
+        }
+    }
+
+    @Test(expectedExceptions = IllegalStateException.class)
+    public static void test6() {
+        ProcessHandle.current().onExit();
+    }
+
+    @Test(expectedExceptions = IllegalStateException.class)
+    public static void test7() {
+        ProcessHandle.current().destroyForcibly();
+    }
+
+    // Main can be used to run the tests from the command line with only testng.jar.
+    @SuppressWarnings("raw_types")
+    public static void main(String[] args) {
+        Class<?>[] testclass = {TreeTest.class};
+        TestNG testng = new TestNG();
+        testng.setTestClasses(testclass);
+        testng.run();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/ProcessHandle/InfoTest.java	Fri May 29 14:04:12 2015 -0400
@@ -0,0 +1,350 @@
+/*
+ * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.File;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.lang.ProcessBuilder;
+import java.time.Duration;
+import java.time.Instant;
+import java.time.temporal.ChronoUnit;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Random;
+import java.util.Scanner;
+import java.util.StringTokenizer;
+import java.util.concurrent.TimeUnit;
+
+import jdk.testlibrary.Platform;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+import org.testng.TestNG;
+
+/*
+ * @test
+ * @library /lib/testlibrary
+ * @summary Functions of ProcessHandle.Info
+ * @author Roger Riggs
+ */
+
+public class InfoTest {
+
+    static String whoami;
+
+    static {
+        ProcessBuilder pb = new ProcessBuilder("whoami");
+        String fullName;
+        try {
+            fullName = new Scanner(pb.start().getInputStream()).nextLine();
+            StringTokenizer st = new StringTokenizer(fullName, "\\");
+            while (st.hasMoreTokens()) {
+                whoami = st.nextToken();
+            }
+            System.out.printf("whoami: %s, user.name: %s%n", whoami, System.getProperty("user.name"));
+        } catch (IOException ex) {
+            throw new RuntimeException(ex);
+        }
+    }
+
+    // Main can be used to run the tests from the command line with only testng.jar.
+    @SuppressWarnings("raw_types")
+    public static void main(String[] args) {
+        Class<?>[] testclass = {InfoTest.class};
+        TestNG testng = new TestNG();
+        testng.setTestClasses(testclass);
+        testng.run();
+    }
+
+    /**
+     * Test that cputime used shows up in ProcessHandle.info
+     */
+    @Test
+    public static void test1() {
+        System.out.println("Note: when run in samevm mode the cputime of the " +
+                "test runner is included.");
+        ProcessHandle self = ProcessHandle.current();
+
+        Duration somecpu = Duration.ofMillis(200L);
+        Instant end = Instant.now().plus(somecpu);
+        while (Instant.now().isBefore(end)) {
+            // waste the cpu
+        }
+        ProcessHandle.Info info = self.info();
+        System.out.printf(" info: %s%n", info);
+        Optional<Duration> totalCpu = info.totalCpuDuration();
+        if (totalCpu.isPresent() && (totalCpu.get().compareTo(somecpu) < 0)) {
+            Assert.fail("reported cputime less than expected: " + somecpu + ", " +
+                    "actual: " + info.totalCpuDuration());
+        }
+    }
+
+    /**
+     * Spawn a child with arguments and check they are visible via the ProcessHandle.
+     */
+    @Test
+    public static void test2() {
+        try {
+            long cpulooptime = 1 << 8;
+            String[] extraArgs = {"pid", "parent", "stdin"};
+            Instant beforeStart = Instant.now().truncatedTo(ChronoUnit.SECONDS);
+            JavaChild p1 = JavaChild.spawnJavaChild((Object[])extraArgs);
+            Instant afterStart = Instant.now();
+
+            try (BufferedReader lines = p1.outputReader()) {
+                Duration lastCpu = Duration.ofMillis(0L);
+                for (int j = 0; j < 20; j++) {
+
+                    p1.sendAction("cpuloop", cpulooptime);
+                    p1.sendAction("cputime", "");
+
+                    // Read cputime from child
+                    Duration childCpuTime = null;
+                    // Read lines from the child until the result from cputime is returned
+                    String s;
+                    while ((s = lines.readLine()) != null) {
+                        String[] split = s.trim().split(" ");
+                        if (split.length == 3 && split[1].equals("cputime")) {
+                            long nanos = Long.valueOf(split[2]);
+                            childCpuTime = Duration.ofNanos(nanos);
+                            break;      // found the result we're looking for
+                        }
+                    }
+
+
+                    ProcessHandle.Info info = p1.info();
+                    System.out.printf(" info: %s%n", info);
+
+                    if (info.user().isPresent()) {
+                        String user = info.user().get();
+                        Assert.assertNotNull(user, "User name");
+                        Assert.assertEquals(user, whoami, "User name");
+                    }
+
+                    Optional<String> command = info.command();
+                    if (command.isPresent()) {
+                        String javaExe = System.getProperty("test.jdk") +
+                                File.separator + "bin" + File.separator + "java";
+                        String expected = Platform.isWindows() ? javaExe + ".exe" : javaExe;
+                        Assert.assertEquals(command.get(), expected,
+                                "Command: expected: 'java'" + ", actual: " + command);
+                    }
+
+                    if (info.arguments().isPresent()) {
+                        String[] args = info.arguments().get();
+
+                        if (Platform.isLinux() || Platform.isOSX()) {
+                            int offset = args.length - extraArgs.length;
+                            for (int i = 0; i < extraArgs.length; i++) {
+                                Assert.assertEquals(args[offset + i], extraArgs[i],
+                                        "Actual argument mismatch, index: " + i);
+                            }
+                        } else if (Platform.isSolaris()) {
+                            Assert.assertEquals(args.length, 1,
+                                    "Expected argument list length: 1");
+                            Assert.assertNotNull(args[0],
+                                    "Expected an argument");
+                        } else {
+                            System.out.printf("No argument test for OS: %s%n", Platform.getOsName());
+                        }
+
+                        // Now check that the first argument is not the same as the executed command
+                        if (args.length > 0) {
+                            Assert.assertNotEquals(args[0], command,
+                                    "First argument should not be the executable: args[0]: "
+                                            + args[0] + ", command: " + command);
+                        }
+                    }
+
+                    if (info.totalCpuDuration().isPresent()) {
+                        Duration totalCPU = info.totalCpuDuration().get();
+                        Duration epsilon = Duration.ofMillis(200L);
+                        Assert.assertTrue(totalCPU.toNanos() > 0L,
+                                "total cpu time expected > 0ms, actual: " + totalCPU);
+                        Assert.assertTrue(totalCPU.toNanos() < lastCpu.toNanos() + 10_000_000_000L,
+                                "total cpu time expected < 10s more than previous iteration, actual: " + totalCPU);
+                        if (childCpuTime != null) {
+                            System.out.printf(" info.totalCPU: %s, childCpuTime: %s, diff: %s%n",
+                                    totalCPU.toNanos(), childCpuTime.toNanos(), childCpuTime.toNanos() - totalCPU.toNanos());
+                            Assert.assertTrue(checkEpsilon(childCpuTime, totalCPU, epsilon),
+                                    childCpuTime + " should be within " +
+                                            epsilon + " of " + totalCPU);
+                        }
+                        lastCpu = totalCPU;
+                    }
+
+                    if (info.startInstant().isPresent()) {
+                        Instant startTime = info.startInstant().get();
+                        Assert.assertTrue(startTime.isBefore(afterStart),
+                                "startTime after process spawn completed"
+                                        + startTime + " + > " + afterStart);
+                    }
+                }
+            }
+            p1.waitFor(5, TimeUnit.SECONDS);
+        } catch (IOException | InterruptedException ie) {
+            ie.printStackTrace(System.out);
+            Assert.fail("unexpected exception", ie);
+        }
+    }
+
+    /**
+     * Spawn a child with arguments and check they are visible via the ProcessHandle.
+     */
+    @Test
+    public static void test3() {
+        try {
+            for (int sleepTime : Arrays.asList(1, 2)) {
+                Process p = spawn("sleep", String.valueOf(sleepTime));
+                ProcessHandle.Info info = p.info();
+                System.out.printf(" info: %s%n", info);
+
+                if (info.user().isPresent()) {
+                    String user = info.user().get();
+                    Assert.assertNotNull(user);
+                    Assert.assertEquals(user, whoami);
+                }
+                if (info.command().isPresent()) {
+                    String command = info.command().get();
+                    String expected = Platform.isWindows() ? "sleep.exe" : "sleep";
+                    Assert.assertTrue(command.endsWith(expected), "Command: expected: \'" +
+                            expected + "\', actual: " + command);
+
+                    // Verify the command exists and is executable
+                    File exe = new File(command);
+                    Assert.assertTrue(exe.exists(), "command must exist: " + exe);
+                    Assert.assertTrue(exe.canExecute(), "command must be executable: " + exe);
+                }
+                if (info.arguments().isPresent()) {
+                    String[] args = info.arguments().get();
+                    if (args.length > 0) {
+                        Assert.assertEquals(args[0], String.valueOf(sleepTime));
+                    }
+                }
+                Assert.assertTrue(p.waitFor(15, TimeUnit.SECONDS));
+            }
+        } catch (IOException | InterruptedException ex) {
+            ex.printStackTrace(System.out);;
+        } finally {
+            // Destroy any children that still exist
+            ProcessUtil.destroyProcessTree(ProcessHandle.current());
+        }
+    }
+
+    /**
+     * Cross check the cputime reported from java.management with that for the current process.
+     */
+    @Test
+    public static void test4() {
+        Duration myCputime1 = ProcessUtil.MXBeanCpuTime();
+
+        Optional<Duration> dur1 = ProcessHandle.current().info().totalCpuDuration();
+
+        Duration myCputime2 = ProcessUtil.MXBeanCpuTime();
+
+        Optional<Duration> dur2 = ProcessHandle.current().info().totalCpuDuration();
+
+        if (dur1.isPresent() && dur2.isPresent()) {
+            Duration total1 = dur1.get();
+            Duration total2 = dur2.get();       ;
+            System.out.printf(" total1 vs. mbean: %s, getProcessCpuTime: %s, diff: %s%n",
+                    Objects.toString(total1), myCputime1, myCputime1.minus(total1));
+            System.out.printf(" total2 vs. mbean: %s, getProcessCpuTime: %s, diff: %s%n",
+                    Objects.toString(total2), myCputime2, myCputime2.minus(total2));
+
+            Duration epsilon = Duration.ofMillis(200L);      // Epsilon is 200ms.
+            Assert.assertTrue(checkEpsilon(myCputime1, myCputime2, epsilon),
+                    myCputime1.toNanos() + " should be within " + epsilon
+                            + " of " + myCputime2.toNanos());
+            Assert.assertTrue(checkEpsilon(total1, total2, epsilon),
+                    total1.toNanos() + " should be within " + epsilon
+                            + " of " + total2.toNanos());
+            Assert.assertTrue(checkEpsilon(myCputime1, total1, epsilon),
+                    myCputime1.toNanos() + " should be within " + epsilon
+                            + " of " + total1.toNanos());
+            Assert.assertTrue(checkEpsilon(total1, myCputime2, epsilon),
+                    total1.toNanos() + " should be within " + epsilon
+                            + " of " + myCputime2.toNanos());
+            Assert.assertTrue(checkEpsilon(myCputime2, total2, epsilon),
+                    myCputime2.toNanos() + " should be within " + epsilon
+                            + " of " + total2.toNanos());
+        }
+    }
+
+    @Test
+    public static void test5() {
+        ProcessHandle self = ProcessHandle.current();
+        Random r = new Random();
+        for (int i = 0; i < 30; i++) {
+            Instant end = Instant.now().plusMillis(500L);
+            while (end.isBefore(Instant.now())) {
+                // burn the cpu time checking the time
+                long x = r.nextLong();
+            }
+            if (self.info().totalCpuDuration().isPresent()) {
+                Duration totalCpu = self.info().totalCpuDuration().get();
+                long infoTotalCputime = totalCpu.toNanos();
+                long beanCputime = ProcessUtil.MXBeanCpuTime().toNanos();
+                System.out.printf(" infoTotal: %12d, beanCpu: %12d, diff: %12d%n",
+                        infoTotalCputime, beanCputime, beanCputime - infoTotalCputime);
+            } else {
+                break;  // nothing to compare; continue
+            }
+        }
+    }
+    /**
+     * Check two Durations, the second should be greater than the first or
+     * within the supplied Epsilon.
+     * @param d1 a Duration - presumed to be shorter
+     * @param d2 a 2nd Duration - presumed to be greater (or within Epsilon)
+     * @param epsilon Epsilon the amount of overlap allowed
+     * @return
+     */
+    static boolean checkEpsilon(Duration d1, Duration d2, Duration epsilon) {
+        if (d1.toNanos() <= d2.toNanos()) {
+            return true;
+        }
+        Duration diff = d1.minus(d2).abs();
+        return diff.compareTo(epsilon) <= 0;
+    }
+
+    /**
+     * Spawn a native process with the provided arguments.
+     * @param command the executable of native process
+     * @args
+     * @return the Process that was started
+     * @throws IOException thrown by ProcessBuilder.start
+     */
+    static Process spawn(String command, String... args) throws IOException {
+        ProcessBuilder pb = new ProcessBuilder();
+        pb.inheritIO();
+        List<String> list = new ArrayList<>();
+        list.add(command);
+        for (String arg : args)
+            list.add(arg);
+        pb.command(list);
+        return pb.start();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/ProcessHandle/JavaChild.java	Fri May 29 14:04:12 2015 -0400
@@ -0,0 +1,524 @@
+/*
+ * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import com.sun.management.OperatingSystemMXBean;
+import java.io.File;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.InputStreamReader;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.io.Reader;
+import java.io.PrintWriter;
+import java.lang.InterruptedException;
+import java.lang.Override;
+import java.lang.management.ManagementFactory;
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.concurrent.CompletableFuture;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Consumer;
+
+
+/**
+ * Command driven subprocess with useful child functions.
+ */
+public class JavaChild extends Process {
+
+private static volatile int commandSeq = 0;         // Command sequence number
+    private static final ProcessHandle self = ProcessHandle.current();
+    private static int finalStatus = 0;
+    private static final List<JavaChild> children = new ArrayList<>();
+    private static final Set<JavaChild> completedChildren =
+            Collections.synchronizedSet(new HashSet<>());
+
+    private final Process delegate;
+    private final PrintWriter inputWriter;
+    private final BufferedReader outputReader;
+
+
+    /**
+     * Create a JavaChild control instance that delegates to the spawned process.
+     * {@link #sendAction} is used to send commands via the processes stdin.
+     * {@link #forEachOutputLine} can be used to process output from the child
+     * @param delegate the process to delegate and send commands to and get responses from
+     */
+    private JavaChild(Process delegate) {
+        this.delegate = delegate;
+        // Initialize PrintWriter with autoflush (on println)
+        inputWriter = new PrintWriter(delegate.getOutputStream(), true);
+        outputReader = new BufferedReader(new InputStreamReader(delegate.getInputStream()));
+    }
+
+    @Override
+    public void destroy() {
+        delegate.destroy();
+    }
+
+    @Override
+    public int exitValue() {
+        return delegate.exitValue();
+    }
+
+    @Override
+    public int waitFor() throws InterruptedException {
+        return delegate.waitFor();
+    }
+
+    @Override
+    public OutputStream getOutputStream() {
+        return delegate.getOutputStream();
+    }
+
+    @Override
+    public InputStream getInputStream() {
+        return delegate.getInputStream();
+    }
+
+    @Override
+    public InputStream getErrorStream() {
+        return delegate.getErrorStream();
+    }
+
+    @Override
+    public ProcessHandle toHandle() {
+        return delegate.toHandle();
+    }
+
+    @Override
+    public CompletableFuture<Process> onExit() {
+        return delegate.onExit();
+    }
+    @Override
+    public String toString() {
+        return "delegate: " + delegate.toString();
+    }
+
+    public CompletableFuture<JavaChild> onJavaChildExit() {
+        return onExit().thenApply(ph -> this);
+    }
+
+    /**
+     * Send an action and arguments to the child via stdin.
+     * @param action the action
+     * @param args additional arguments
+     * @throws IOException if something goes wrong writing to the child
+     */
+    void sendAction(String action, Object... args) throws IOException {
+        StringBuilder sb = new StringBuilder();
+        sb.append(action);
+        for (Object arg :args) {
+            sb.append(" ");
+            sb.append(arg);
+        }
+        String cmd = sb.toString();
+        synchronized (this) {
+            inputWriter.println(cmd);
+        }
+    }
+
+    public BufferedReader outputReader() {
+        return outputReader;
+    }
+
+    /**
+     * Asynchronously evaluate each line of output received back from the child process.
+     * @param consumer a Consumer of each line read from the child
+     * @return a CompletableFuture that is completed when the child closes System.out.
+     */
+    CompletableFuture<String> forEachOutputLine(Consumer<String> consumer) {
+        final CompletableFuture<String> future = new CompletableFuture<>();
+        String name = "OutputLineReader-" + getPid();
+        Thread t = new Thread(() -> {
+            try (BufferedReader reader = outputReader()) {
+                String line;
+                while ((line = reader.readLine()) != null) {
+                    consumer.accept(line);
+                }
+            } catch (IOException | RuntimeException ex) {
+                consumer.accept("IOE (" + getPid() + "):" + ex.getMessage());
+                future.completeExceptionally(ex);
+            }
+            future.complete("success");
+        }, name);
+        t.start();
+        return future;
+    }
+
+    /**
+     * Spawn a JavaChild with the provided arguments.
+     * Commands can be send to the child with {@link #sendAction}.
+     * Output lines from the child can be processed with {@link #forEachOutputLine}.
+     * System.err is set to inherit and is the unstructured async logging
+     * output for all subprocesses.
+     * @param args the command line arguments to JavaChild
+     * @return the JavaChild that was started
+     * @throws IOException thrown by ProcessBuilder.start
+     */
+    static JavaChild spawnJavaChild(Object... args) throws IOException {
+        String[] stringArgs = new String[args.length];
+        for (int i = 0; i < args.length; i++) {
+            stringArgs[i] = args[i].toString();
+        }
+        ProcessBuilder pb = build(stringArgs);
+        pb.redirectError(ProcessBuilder.Redirect.INHERIT);
+        return new JavaChild(pb.start());
+    }
+
+    /**
+     * Spawn a JavaChild with the provided arguments.
+     * Sets the process to inherit the I/O channels.
+     * @param args the command line arguments to JavaChild
+     * @return the Process that was started
+     * @throws IOException thrown by ProcessBuilder.start
+     */
+    static Process spawn(String... args) throws IOException {
+        ProcessBuilder pb = build(args);
+        pb.inheritIO();
+        return pb.start();
+    }
+
+    /**
+     * Return a ProcessBuilder with the javaChildArgs and
+     * any additional supplied args.
+     *
+     * @param args the command line arguments to JavaChild
+     * @return the ProcessBuilder
+     */
+    static ProcessBuilder build(String ... args) {
+        ProcessBuilder pb = new ProcessBuilder();
+        List<String> list = new ArrayList<>(javaChildArgs);
+        for (String arg : args)
+            list.add(arg);
+        pb.command(list);
+        return pb;
+    }
+
+    static final String javaHome = (System.getProperty("test.jdk") != null)
+            ? System.getProperty("test.jdk")
+            : System.getProperty("java.home");
+
+    static final String javaExe =
+            javaHome + File.separator + "bin" + File.separator + "java";
+
+    static final String classpath =
+            System.getProperty("java.class.path");
+
+    static final List<String> javaChildArgs =
+            Arrays.asList(javaExe,
+                    "-XX:+DisplayVMOutputToStderr",
+                    "-Dtest.jdk=" + javaHome,
+                    "-classpath", absolutifyPath(classpath),
+                    "JavaChild");
+
+    private static String absolutifyPath(String path) {
+        StringBuilder sb = new StringBuilder();
+        for (String file : path.split(File.pathSeparator)) {
+            if (sb.length() != 0)
+                sb.append(File.pathSeparator);
+            sb.append(new File(file).getAbsolutePath());
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Main program that interprets commands from the command line args or stdin.
+     * Each command produces output to stdout confirming the command and
+     * providing results.
+     * System.err is used for unstructured information.
+     * @param args an array of strings to be interpreted as commands;
+     *             each command uses additional arguments as needed
+     */
+    public static void main(String[] args) {
+        System.out.printf("args: %s %s%n", ProcessHandle.current(), Arrays.toString(args));
+        interpretCommands(args);
+        System.exit(finalStatus);
+    }
+
+    /**
+     * Interpret an array of strings as a command line.
+     * @param args an array of strings to be interpreted as commands;
+     *             each command uses additional arguments as needed
+     */
+    private static void interpretCommands(String[] args) {
+        try {
+            int nextArg = 0;
+            while (nextArg < args.length) {
+                String action = args[nextArg++];
+                switch (action) {
+                    case "help":
+                        sendResult(action, "");
+                        help();
+                        break;
+                    case "sleep":
+                        int millis = Integer.valueOf(args[nextArg++]);
+                        Thread.sleep(millis);
+                        sendResult(action, Integer.toString(millis));
+                        break;
+                    case "cpuloop":
+                        long times = Long.valueOf(args[nextArg++]);
+                        Instant end = Instant.now().plusMillis(times);
+                        while (Instant.now().isBefore(end)) {
+                            // burn the cpu til the time is up
+                        }
+                        sendResult(action, times);
+                        break;
+                    case "cputime":
+                        sendResult(action, getCpuTime());
+                        break;
+                    case "out":
+                    case "err":
+                        String value = args[nextArg++];
+                        sendResult(action, value);
+                        if (action.equals("err")) {
+                            System.err.println(value);
+                        }
+                        break;
+                    case "stdin":
+                        // Read commands from stdin;  at eof, close stdin of
+                        // children and wait for each to exit
+                        sendResult(action, "start");
+                        try (Reader reader = new InputStreamReader(System.in);
+                             BufferedReader input = new BufferedReader(reader)) {
+                            String line;
+                            while ((line = input.readLine()) != null) {
+                                line = line.trim();
+                                if (!line.isEmpty()) {
+                                    String[] split = line.split("\\s");
+                                    interpretCommands(split);
+                                }
+                            }
+                            // EOF on stdin, close stdin on all spawned processes
+                            for (JavaChild p : children) {
+                                try {
+                                    p.getOutputStream().close();
+                                } catch (IOException ie) {
+                                    sendResult("stdin_closing", p.getPid(),
+                                            "exception", ie.getMessage());
+                                }
+                            }
+
+                            for (JavaChild p : children) {
+                                do {
+                                    try {
+                                        p.waitFor();
+                                        break;
+                                    } catch (InterruptedException e) {
+                                        // retry
+                                    }
+                                } while (true);
+                            }
+                            // Wait for all children to be gone
+                            Instant timeOut = Instant.now().plusSeconds(10L);
+                            while (!completedChildren.containsAll(children)) {
+                                if (Instant.now().isBefore(timeOut)) {
+                                    Thread.sleep(100L);
+                                } else {
+                                    System.err.printf("Timeout waiting for " +
+                                            "children to terminate%n");
+                                    children.removeAll(completedChildren);
+                                    for (JavaChild c : children) {
+                                        sendResult("stdin_noterm", c.getPid());
+                                        System.err.printf("  Process not terminated: " +
+                                                "pid: %d%n", c.getPid());
+                                    }
+                                    System.exit(2);
+                                }
+                            }
+                        }
+                        sendResult(action, "done");
+                        return;                 // normal exit from JavaChild Process
+                    case "parent":
+                        sendResult(action, self.parent().toString());
+                        break;
+                    case "pid":
+                        sendResult(action, self.toString());
+                        break;
+                    case "exit":
+                        int exitValue = (nextArg < args.length)
+                                ?  Integer.valueOf(args[nextArg]) : 0;
+                        sendResult(action, exitValue);
+                        System.exit(exitValue);
+                        break;
+                    case "spawn": {
+                        if (args.length - nextArg < 2) {
+                            throw new RuntimeException("not enough args for respawn: " +
+                                    (args.length - 2));
+                        }
+                        // Spawn as many children as requested and
+                        // pass on rest of the arguments
+                        int ncount = Integer.valueOf(args[nextArg++]);
+                        Object[] subargs = new String[args.length - nextArg];
+                        System.arraycopy(args, nextArg, subargs, 0, subargs.length);
+                        for (int i = 0; i < ncount; i++) {
+                            JavaChild p = spawnJavaChild(subargs);
+                            sendResult(action, p.getPid());
+                            p.forEachOutputLine(JavaChild::sendRaw);
+                            p.onJavaChildExit().thenAccept((p1) -> {
+                                int excode = p1.exitValue();
+                                sendResult("child_exit", p1.getPid(), excode);
+                                completedChildren.add(p1);
+                            });
+                            children.add(p);        // Add child to spawned list
+                        }
+                        nextArg = args.length;
+                        break;
+                    }
+                    case "child": {
+                        // Send the command to all the live children;
+                        // ignoring those that are not alive
+                        int sentCount = 0;
+                        Object[] result =
+                                Arrays.copyOfRange(args, nextArg - 1, args.length);
+                        Object[] subargs =
+                                Arrays.copyOfRange(args, nextArg + 1, args.length);
+                        for (JavaChild p : children) {
+                            if (p.isAlive()) {
+                                sentCount++;
+                                // overwrite with current pid
+                                result[0] = Long.toString(p.getPid());
+                                sendResult(action, result);
+                                p.sendAction(args[nextArg], subargs);
+                            }
+                        }
+                        if (sentCount == 0) {
+                            sendResult(action, "n/a");
+                        }
+                        nextArg = args.length;
+                        break;
+                    }
+                    case "child_eof" :
+                        // Close the InputStream of all the live children;
+                        // ignoring those that are not alive
+                        for (JavaChild p : children) {
+                            if (p.isAlive()) {
+                                sendResult(action, p.getPid());
+                                p.getOutputStream().close();
+                            }
+                        }
+                        break;
+                    case "property":
+                        String name = args[nextArg++];
+                        sendResult(action, name, System.getProperty(name));
+                        break;
+                    case "threaddump":
+                        Thread.dumpStack();
+                        break;
+                    default:
+                        throw new Error("JavaChild action unknown: " + action);
+                }
+            }
+        } catch (Throwable t) {
+            t.printStackTrace(System.err);
+            System.exit(1);
+        }
+    }
+
+    static synchronized void sendRaw(String s) {
+        System.out.println(s);
+        System.out.flush();
+    }
+    static void sendResult(String action, Object... results) {
+        sendRaw(new Event(action, results).toString());
+    }
+
+    static long getCpuTime() {
+        OperatingSystemMXBean osMbean =
+                (OperatingSystemMXBean)ManagementFactory.getOperatingSystemMXBean();
+        return osMbean.getProcessCpuTime();
+    }
+
+    /**
+     * Print command usage to stderr.
+     */
+    private static void help() {
+        System.err.println("Commands:");
+        System.err.println("  help");
+        System.err.println("  pid");
+        System.err.println("  parent");
+        System.err.println("  cpuloop <loopcount>");
+        System.err.println("  cputime");
+        System.err.println("  stdin - read commands from stdin");
+        System.err.println("  sleep <millis>");
+        System.err.println("  spawn <n> command... - spawn n new children and send command");
+        System.err.println("  child command... - send command to all live children");
+        System.err.println("  child_eof - send eof to all live children");
+        System.err.println("  exit <exitcode>");
+        System.err.println("  out arg...");
+        System.err.println("  err arg...");
+    }
+
+    static class Event {
+        long pid;
+        long seq;
+        String command;
+        Object[] results;
+        Event(String command, Object... results) {
+            this(self.getPid(), ++commandSeq, command, results);
+        }
+        Event(long pid, int seq, String command, Object... results) {
+            this.pid = pid;
+            this.seq = seq;
+            this.command = command;
+            this.results = results;
+        }
+
+        /**
+         * Create a String encoding the pid, seq, command, and results.
+         *
+         * @return a String formatted  to send to the stream.
+         */
+        String format() {
+            StringBuilder sb = new StringBuilder();
+            sb.append(pid);
+            sb.append(":");
+            sb.append(seq);
+            sb.append(" ");
+            sb.append(command);
+            for (int i = 0; i < results.length; i++) {
+                sb.append(" ");
+                sb.append(results[i]);
+            }
+            return sb.toString();
+        }
+
+        Event(String encoded) {
+            String[] split = encoded.split("\\s");
+            String[] pidSeq = split[0].split(":");
+            pid = Long.valueOf(pidSeq[0]);
+            seq = Integer.valueOf(pidSeq[1]);
+            command = split[1];
+            Arrays.copyOfRange(split, 1, split.length);
+        }
+
+        public String toString() {
+            return format();
+        }
+
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/ProcessHandle/OnExitTest.java	Fri May 29 14:04:12 2015 -0400
@@ -0,0 +1,193 @@
+/*
+ * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.IOException;
+import java.lang.InterruptedException;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.ArrayBlockingQueue;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutionException;
+import java.util.stream.Collectors;
+import jdk.testlibrary.Platform;
+import org.testng.annotations.Test;
+import org.testng.Assert;
+import org.testng.TestNG;
+
+/*
+ * @test
+ * @library /lib/testlibrary
+ * @summary Functions of Process.onExit and ProcessHandle.onExit
+ * @author Roger Riggs
+ */
+
+public class OnExitTest extends ProcessUtil {
+
+    @SuppressWarnings("raw_types")
+    public static void main(String[] args) {
+        Class<?>[] testclass = { OnExitTest.class};
+        TestNG testng = new TestNG();
+        testng.setTestClasses(testclass);
+        testng.run();
+    }
+
+    /**
+     * Basic test of exitValue and onExit.
+     */
+    @Test
+    public static void test1() {
+        try {
+            int[] exitValues = {0, 1, 10};
+            for (int value : exitValues) {
+                Process p = JavaChild.spawn("exit", Integer.toString(value));
+                CompletableFuture<Process> future = p.onExit();
+                future.thenAccept( (ph) -> {
+                    int actualExitValue = ph.exitValue();
+                    printf(" javaChild done: %s, exitStatus: %d%n",
+                            ph, actualExitValue);
+                    Assert.assertEquals(actualExitValue, value, "actualExitValue incorrect");
+                    Assert.assertEquals(ph, p, "Different Process passed to thenAccept");
+                });
+
+                Process h = future.get();
+                Assert.assertEquals(h, p);
+                Assert.assertEquals(p.exitValue(), value);
+                Assert.assertFalse(p.isAlive(), "Process should not be alive");
+                p.waitFor();
+            }
+        } catch (IOException | InterruptedException | ExecutionException ex) {
+            Assert.fail(ex.getMessage(), ex);
+        } finally {
+            destroyProcessTree(ProcessHandle.current());
+        }
+    }
+
+    /**
+     * Test of Completion handler when parent is killed.
+     * Spawn 1 child to spawn 3 children each with 2 children.
+     */
+    @Test
+    public static void test2() {
+        try {
+            ConcurrentHashMap<ProcessHandle, ProcessHandle> processes = new ConcurrentHashMap<>();
+            List<ProcessHandle> children = getChildren(ProcessHandle.current());
+            children.forEach(ProcessUtil::printProcess);
+            Assert.assertEquals(children.size(), 0,
+                    "Expected to start with zero children; " + children);
+
+            JavaChild proc = JavaChild.spawnJavaChild("stdin");
+            ProcessHandle procHandle = proc.toHandle();
+            printf(" spawned: %d%n", proc.getPid());
+
+            proc.forEachOutputLine((s) -> {
+                String[] split = s.trim().split(" ");
+                if (split.length == 3 && split[1].equals("spawn")) {
+                    Long child = Long.valueOf(split[2]);
+                    Long parent = Long.valueOf(split[0].split(":")[0]);
+                    processes.put(ProcessHandle.of(child).get(), ProcessHandle.of(parent).get());
+                }
+            });
+
+            proc.sendAction("spawn", "3", "stdin");
+
+            proc.sendAction("child", "spawn", "2", "stdin");
+
+            // Poll until all 9 child processes exist or the timeout is reached
+            int expected = 9;
+            Instant endTimeout = Instant.now().plusSeconds(10L);
+            do {
+                Thread.sleep(200L);
+                printf(" subprocess count: %d, waiting for %d%n", processes.size(), expected);
+            } while (processes.size() < expected &&
+                    Instant.now().isBefore(endTimeout));
+
+            children = getAllChildren(procHandle);
+
+            ArrayBlockingQueue<ProcessHandle> completions = new ArrayBlockingQueue<>(expected + 1);
+            Instant startTime = Instant.now();
+            // Create a future for each of the 9 children
+            processes.forEach( (p, parent) -> {
+                        p.onExit().whenComplete((ph, ex) -> {
+                            Duration elapsed = Duration.between(startTime, Instant.now());
+                            completions.add(ph);
+                            printf("whenComplete: pid: %s, exception: %s, thread: %s, elapsed: %s%n",
+                                    ph, ex, Thread.currentThread(), elapsed);
+                        });
+                    });
+
+            // Check that each of the spawned processes is included in the children
+            List<ProcessHandle> remaining = new ArrayList<>(children);
+            processes.forEach((p, parent) -> {
+                Assert.assertTrue(remaining.remove(p), "spawned process should have been in children");
+            });
+
+            // Remove Win32 system spawned conhost.exe processes
+            remaining.removeIf(ProcessUtil::isWindowsConsole);
+
+            remaining.forEach(p -> printProcess(p, "unexpected: "));
+            if (remaining.size() > 0) {
+                // Show full list for debugging
+                ProcessUtil.logTaskList();
+            }
+
+            proc.destroy();  // kill off the parent
+            proc.waitFor();
+
+            // Wait for all the processes to be completed
+            processes.forEach((p, parent) -> {
+                try {
+                    p.onExit().get();
+                } catch (InterruptedException | ExecutionException ex) {
+                    // ignore
+                }
+            });
+
+            // Verify that all 9 exit handlers were called
+            processes.forEach((p, parent) ->
+                Assert.assertTrue(completions.contains(p), "Child onExit not called: " + p
+                        + ", parent: " + parent
+                        + ": " + p.info()));
+
+            // Show the status of the original children
+            children.forEach(p -> printProcess(p, "after onExit:"));
+
+            Assert.assertEquals(proc.isAlive(), false, "destroyed process is alive:: %s%n" + proc);
+
+            List<ProcessHandle> children2 = getAllChildren(procHandle);
+            printf(" children2: %s%n", children2.toString());
+            Assert.assertEquals(children2.size(), 0, "After onExit, expected no children");
+
+            Assert.assertEquals(remaining.size(), 0, "Unaccounted for children");
+
+        } catch (IOException | InterruptedException ex) {
+            Assert.fail(ex.getMessage());
+        } finally {
+            destroyProcessTree(ProcessHandle.current());
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/ProcessHandle/PermissionTest.java	Fri May 29 14:04:12 2015 -0400
@@ -0,0 +1,222 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.FilePermission;
+import java.io.IOException;
+import java.security.CodeSource;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.Permissions;
+import java.security.Policy;
+import java.security.ProtectionDomain;
+import java.security.SecurityPermission;
+import java.util.Arrays;
+import java.util.Optional;
+import java.util.PropertyPermission;
+
+import org.testng.Assert;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeGroups;
+import org.testng.annotations.Test;
+
+public class PermissionTest {
+    /**
+     * Backing up policy.
+     */
+    protected static Policy policy;
+
+    /**
+     * Backing up security manager.
+     */
+    private static SecurityManager sm;
+
+    /**
+     * Current process handle.
+     */
+    private final ProcessHandle currentHndl;
+
+    PermissionTest() {
+        policy = Policy.getPolicy();
+        sm = System.getSecurityManager();
+        currentHndl = ProcessHandle.current();
+    }
+
+    @Test
+    public void allChildrenWithPermission() {
+        Policy.setPolicy(new TestPolicy(new RuntimePermission("manageProcess")));
+        currentHndl.allChildren();
+    }
+
+    @Test
+    public void allProcessesWithPermission() {
+        Policy.setPolicy(new TestPolicy(new RuntimePermission("manageProcess")));
+        ProcessHandle.allProcesses();
+    }
+
+    @Test
+    public void childrenWithPermission() {
+        Policy.setPolicy(new TestPolicy(new RuntimePermission("manageProcess")));
+        currentHndl.children();
+    }
+
+    @Test
+    public void currentWithPermission() {
+        Policy.setPolicy(new TestPolicy(new RuntimePermission("manageProcess")));
+        ProcessHandle.current();
+    }
+
+    @Test
+    public void ofWithPermission() {
+        Policy.setPolicy(new TestPolicy(new RuntimePermission("manageProcess")));
+        ProcessHandle.of(0);
+    }
+
+    @Test
+    public void parentWithPermission() {
+        Policy.setPolicy(new TestPolicy(new RuntimePermission("manageProcess")));
+        currentHndl.parent();
+    }
+
+    @Test
+    public void processToHandleWithPermission() throws IOException {
+        Policy.setPolicy(new TestPolicy(new RuntimePermission("manageProcess")));
+        Process p = null;
+        try {
+            ProcessBuilder pb = new ProcessBuilder("sleep", "30");
+            p = pb.start();
+            ProcessHandle ph = p.toHandle();
+            Assert.assertNotNull(ph, "ProcessHandle expected from Process");
+        } finally {
+            if (p != null) {
+                p.destroy();
+            }
+        }
+    }
+
+    @BeforeGroups (groups = {"NoManageProcessPermission"})
+    public void noPermissionsSetup(){
+        Policy.setPolicy(new TestPolicy());
+        SecurityManager sm = new SecurityManager();
+        System.setSecurityManager(sm);
+    }
+
+    @Test(groups = { "NoManageProcessPermission" }, expectedExceptions = SecurityException.class)
+    public void noPermissionAllChildren() {
+        currentHndl.allChildren();
+    }
+
+    @Test(groups = { "NoManageProcessPermission" }, expectedExceptions = SecurityException.class)
+    public void noPermissionAllProcesses() {
+        ProcessHandle.allProcesses();
+    }
+
+    @Test(groups = { "NoManageProcessPermission" }, expectedExceptions = SecurityException.class)
+    public void noPermissionChildren() {
+        currentHndl.children();
+    }
+
+    @Test(groups = { "NoManageProcessPermission" }, expectedExceptions = SecurityException.class)
+    public void noPermissionCurrent() {
+        ProcessHandle.current();
+    }
+
+    @Test(groups = { "NoManageProcessPermission" }, expectedExceptions = SecurityException.class)
+    public void noPermissionOf() {
+        ProcessHandle.of(0);
+    }
+
+    @Test(groups = { "NoManageProcessPermission" }, expectedExceptions = SecurityException.class)
+    public void noPermissionParent() {
+        currentHndl.parent();
+    }
+
+    @Test(groups = { "NoManageProcessPermission" }, expectedExceptions = SecurityException.class)
+    public void noPermissionProcessToHandle() throws IOException {
+        Process p = null;
+        try {
+            ProcessBuilder pb = new ProcessBuilder("sleep", "30");
+            p = pb.start();
+            ProcessHandle ph = p.toHandle();
+            Assert.assertNotNull(ph, "ProcessHandle expected from Process");
+        } finally {
+            if (p != null) {
+                p.destroy();
+            }
+        }
+    }
+
+    @AfterClass
+    public void tearDownClass() throws Exception {
+        System.setSecurityManager(sm);
+        Policy.setPolicy(policy);
+    }
+}
+
+class TestPolicy extends Policy {
+    private final PermissionCollection permissions = new Permissions();
+
+    public TestPolicy() {
+        setBasicPermissions();
+    }
+
+    /*
+     * Defines the minimal permissions required by testNG and set security
+     * manager permission when running these tests.
+     */
+    public void setBasicPermissions() {
+        permissions.add(new SecurityPermission("getPolicy"));
+        permissions.add(new SecurityPermission("setPolicy"));
+        permissions.add(new RuntimePermission("getClassLoader"));
+        permissions.add(new RuntimePermission("setSecurityManager"));
+        permissions.add(new RuntimePermission("createSecurityManager"));
+        permissions.add(new PropertyPermission("testng.show.stack.frames",
+                "read"));
+        permissions.add(new PropertyPermission("user.dir", "read"));
+        permissions.add(new PropertyPermission("test.src", "read"));
+        permissions.add(new PropertyPermission("file.separator", "read"));
+        permissions.add(new PropertyPermission("line.separator", "read"));
+        permissions.add(new PropertyPermission("fileStringBuffer", "read"));
+        permissions.add(new PropertyPermission("dataproviderthreadcount", "read"));
+        permissions.add(new FilePermission("<<ALL FILES>>", "execute"));
+    }
+
+    public TestPolicy(Permission... ps) {
+        setBasicPermissions();
+        Arrays.stream(ps).forEach(p -> permissions.add(p));
+    }
+
+    @Override
+    public PermissionCollection getPermissions(ProtectionDomain domain) {
+        return permissions;
+    }
+
+    @Override
+    public PermissionCollection getPermissions(CodeSource codesource) {
+        return permissions;
+    }
+
+    @Override
+    public boolean implies(ProtectionDomain domain, Permission perm) {
+        return permissions.implies(perm);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/ProcessHandle/ProcessUtil.java	Fri May 29 14:04:12 2015 -0400
@@ -0,0 +1,246 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.IOException;
+import java.lang.management.ManagementFactory;
+import java.lang.ProcessBuilder;
+import java.time.Duration;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import com.sun.management.OperatingSystemMXBean;
+
+import jdk.testlibrary.Platform;
+
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/**
+ * Useful utilities for testing Process and ProcessHandle.
+ */
+public abstract class ProcessUtil {
+    /**
+     * Constructor
+     */
+    public ProcessUtil() {}
+
+    /**
+     * Returns the list of direct children.
+     * WIndows conhost.exe children are filtered out.
+     * @param ph the Process to get children of
+     * @return a list of child ProcessHandles
+     */
+    public static List<ProcessHandle> getChildren(ProcessHandle ph) {
+        return ph.children()
+                .filter(ProcessUtil::isNotWindowsConsole)
+                .collect(Collectors.toList());
+    }
+
+    /**
+     * Returns the list of all direct and indirect children.
+     * WIndows conhost.exe children are filtered out.
+     * @param ph the Process to get children of
+     * @return a list of child ProcessHandles
+     */
+    public static List<ProcessHandle> getAllChildren(ProcessHandle ph) {
+        return ph.allChildren()
+                .filter(ProcessUtil::isNotWindowsConsole)
+                .collect(Collectors.toList());
+    }
+
+    /**
+     * Waits for and returns the direct expected Children of a ProcessHandle.
+     * For Windows, the conhost.exe children are filtered out.
+     *
+     * @param ph the process to get the children of
+     * @param nchildren the minimum number of children to expect
+     * @return a list of ProcessHandles of the children.
+     */
+    public static List<ProcessHandle> waitForChildren(ProcessHandle ph, long nchildren) {
+        List<ProcessHandle> subprocesses = null;
+        long count = 0;
+        do {
+            if (subprocesses != null) {
+                // Only wait if this is not the first time looking
+                try {
+                    Thread.sleep(500L);     // It will happen but don't burn the cpu
+                } catch (InterruptedException ie) {
+                    // ignore
+                }
+            }
+            subprocesses = getChildren(ph);
+            count = subprocesses.size();
+            System.out.printf(" waiting for subprocesses of %s to start," +
+                    " expected: %d, current: %d%n", ph, nchildren, count);
+        } while (count < nchildren);
+        return subprocesses;
+    }
+
+    /**
+     * Waits for and returns all expected Children of a ProcessHandle.
+     * For Windows, the conhost.exe children are filtered out.
+     *
+     * @param ph the process to get the children of
+     * @param nchildren the minimum number of children to expect
+     * @return a list of ProcessHandles of the children.
+     */
+    public static List<ProcessHandle> waitForAllChildren(ProcessHandle ph, long nchildren) {
+        List<ProcessHandle> subprocesses = null;
+        long count = 0;
+        do {
+            if (subprocesses != null) {
+                // Only wait if this is not the first time looking
+                try {
+                    Thread.sleep(500L);     // It will happen but don't burn the cpu
+                } catch (InterruptedException ie) {
+                    // ignore
+                }
+            }
+            subprocesses = getAllChildren(ph);
+            count = subprocesses.size();
+            System.out.printf(" waiting for subprocesses of %s to start," +
+                    " expected: %d, current: %d%n", ph, nchildren, count);
+        } while (count < nchildren);
+        return subprocesses;
+    }
+
+    /**
+     * Destroy all children of the ProcessHandle.
+     * (Except the conhost.exe on Windows)
+     *
+     * @param p a ProcessHandle
+     * @return the ProcessHandle
+     */
+    public static ProcessHandle destroyProcessTree(ProcessHandle p) {
+        Stream<ProcessHandle> children = p.allChildren().filter(ProcessUtil::isNotWindowsConsole);
+        children.forEach(ph -> {
+            System.out.printf("destroyProcessTree destroyForcibly%n");
+            printProcess(ph);
+            ph.destroyForcibly();
+        });
+        return p;
+    }
+
+    /**
+     * The OSMXBean for this process.
+     */
+    public static final OperatingSystemMXBean osMbean =
+            (OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean();
+
+    /**
+     * Return the CPU time of the current process according to the OperatingSystemMXBean.
+     *
+     * @return the CPU time of the current process
+     */
+    public static Duration MXBeanCpuTime() {
+        return Duration.ofNanos(osMbean.getProcessCpuTime());
+    }
+
+    /**
+     * Return true if the ProcessHandle is a Windows i586 conhost.exe process.
+     *
+     * @param p the processHandle of the Process
+     * @return Return true if the ProcessHandle is for a Windows i586 conhost.exe process
+     */
+    static boolean isWindowsConsole(ProcessHandle p) {
+        return Platform.isWindows() && p.info().command().orElse("").endsWith("C:\\Windows\\System32\\conhost.exe");
+    }
+
+    /**
+     * Return true if the ProcessHandle is NOT  a Windows i586 conhost.exe process.
+     *
+     * @param p the processHandle of the Process
+     * @return Return true if the ProcessHandle is NOT for a Windows i586 conhost.exe process
+     */
+    static boolean isNotWindowsConsole(ProcessHandle p) {
+        return !isWindowsConsole(p);
+    }
+
+    /**
+     * Print a formatted string to System.out.
+     * @param format the format
+     * @param args the argument array
+     */
+    static void printf(String format, Object... args) {
+        String s = String.format(format, args);
+        System.out.print(s);
+    }
+
+    /**
+     * Print information about a process.
+     * Prints the pid, if it is alive, and information about the process.
+     * @param ph the processHandle at the top
+     */
+    static void printProcess(ProcessHandle ph) {
+        printProcess(ph, "");
+    }
+
+    /**
+     * Print information about a process.
+     * Prints the pid, if it is alive, and information about the process.
+     * @param ph the processHandle at the top
+     * @param prefix the String to prefix the output with
+     */
+    static void printProcess(ProcessHandle ph, String prefix) {
+        printf("%spid %s, alive: %s; parent: %s, %s%n", prefix,
+                ph.getPid(), ph.isAlive(), ph.parent(), ph.info());
+    }
+
+    /**
+     * Print the process hierarchy as visible via ProcessHandle.
+     * Prints the pid, if it is alive, and information about the process.
+     * @param ph the processHandle at the top
+     * @param prefix the String to prefix the output with
+     */
+    static void printDeep(ProcessHandle ph, String prefix) {
+        printProcess(ph, prefix);
+        ph.children().forEach(p -> printDeep(p, prefix + "   "));
+    }
+
+    /**
+     * Use the native command to list the active processes.
+     */
+    static void logTaskList() {
+        String[] windowsArglist = {"tasklist.exe", "/v"};
+        String[] unixArglist = {"ps", "-ef"};
+
+        String[] argList = null;
+        if (Platform.isWindows()) {
+            argList = windowsArglist;
+        } else if (Platform.isLinux() || Platform.isOSX()) {
+            argList = unixArglist;
+        } else {
+            return;
+        }
+
+        ProcessBuilder pb = new ProcessBuilder(argList);
+        pb.inheritIO();
+        try {
+            Process proc = pb.start();
+            proc.waitFor();
+        } catch (IOException | InterruptedException ex) {
+            ex.printStackTrace();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/ProcessHandle/ScaleTest.java	Fri May 29 14:04:12 2015 -0400
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+import java.util.stream.Collectors;
+import static org.testng.Assert.assertEquals;
+import org.testng.TestNG;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+/*
+ * @test
+ * @summary Scalibities test for checking scability of ProcessHandle
+ * @run testng/othervm ScaleTest
+ */
+public class ScaleTest {
+    /**
+     * Scale max processes number to 5000.
+     */
+    private final static int MAX_PROCESSES_LIMIT = 1000;
+
+    /**
+     * Create test process number as 1, 2, 5, 10, 20, 50, 100, 200... until it reach
+     * setting process limit.
+     * @return iterator on how many processes can be created.
+     * @throws IOException if can't create processes.
+     */
+    @DataProvider
+    public Iterator<Object[]> processesNumbers() {
+        // Limit spawn processes number less than total limitation.
+
+        List<Object[]> testProcesses = new ArrayList<>();
+        int i = 1, j = 0;
+        while (i <= MAX_PROCESSES_LIMIT) {
+            testProcesses.add(new Object[]{i});
+            if ((j % 3) != 1)
+                i *= 2;
+            else
+                i = i * 25 / 10;
+            j++;
+        }
+        return testProcesses.iterator();
+    }
+
+    /**
+     * Start process by given number, compare created processes with
+     * ProcessHandle.children by order.
+     * @param processNum processes number that will be created
+     */
+    @Test(dataProvider = "processesNumbers")
+    public void scaleProcesses(int processNum) {
+        try {
+            ProcessBuilder pb = new ProcessBuilder();
+            pb.command("sleep", "600");
+            List<ProcessHandle> children = new ArrayList<>();
+
+            int createdProcessNum = 0;
+            for (int i = 0; i < processNum; i++) {
+                try {
+                    children.add(pb.start().toHandle());
+                    createdProcessNum++;
+                } catch (Throwable ignore) {
+                    // Hard to control how many processes we can generate.
+                    // Ignore every error when create new process
+                }
+            }
+            List<ProcessHandle> phs = ProcessHandle.current().allChildren()
+                    .filter(ph -> ph.info().command().orElse("").contains("sleep"))
+                    .collect(Collectors.toList());
+            assertEquals(phs.size(), createdProcessNum, "spawned processes vs allChildren");
+            assertEqualsWithoutOrder(phs, children, ProcessHandle::compareTo, processNum);
+        } finally {
+            ProcessHandle.current().children().forEach(ProcessHandle::destroyForcibly);
+        }
+    }
+
+    /**
+     * Sort two list by given comparator and compare two list without order
+     * @param actual Process handle list1
+     * @param expected Process handle list1
+     * @param comp ProcessHandle comparator for sorting
+     * @param pn number of processes
+     */
+    private void assertEqualsWithoutOrder(List<ProcessHandle> actual,
+            List<ProcessHandle> expected, Comparator<ProcessHandle> comp, int pn) {
+        Collections.sort(actual, comp);
+        Collections.sort(expected, comp);
+
+        assertEquals(actual, expected);
+    }
+
+    // Main can be used to run the tests from the command line with only testng.jar.
+    @SuppressWarnings("raw_types")
+    public static void main(String[] args) {
+        Class<?>[] testclass = {ScaleTest.class};
+        TestNG testng = new TestNG();
+        testng.setTestClasses(testclass);
+        testng.run();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/ProcessHandle/TEST.properties	Fri May 29 14:04:12 2015 -0400
@@ -0,0 +1,4 @@
+# ProcessHandle tests use TestNG
+TestNG.dirs = .
+lib.dirs = /lib/testlibrary
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/lang/ProcessHandle/TreeTest.java	Fri May 29 14:04:12 2015 -0400
@@ -0,0 +1,358 @@
+/*
+ * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import java.util.concurrent.ExecutionException;
+import org.testng.Assert;
+import org.testng.TestNG;
+import org.testng.annotations.Test;
+
+/*
+ * @test
+ * @library /lib/testlibrary
+ * Test counting and JavaChild.spawning and counting of Processes.
+ * @run testng/othervm InfoTest
+ * @author Roger Riggs
+ */
+public class TreeTest extends ProcessUtil {
+    // Main can be used to run the tests from the command line with only testng.jar.
+    @SuppressWarnings("raw_types")
+    public static void main(String[] args) {
+        Class<?>[] testclass = {TreeTest.class};
+        TestNG testng = new TestNG();
+        testng.setTestClasses(testclass);
+        testng.run();
+    }
+
+    /**
+     * Test counting and spawning and counting of Processes.
+     */
+    @Test
+    public static void test1() {
+        final int MAXCHILDREN = 2;
+        List<JavaChild> spawned = new ArrayList<>();
+
+        try {
+            ProcessHandle self = ProcessHandle.current();
+
+            printf("self pid: %d%n", self.getPid());
+            printDeep(self, "");
+            long count = getChildren(self).size();
+            Assert.assertEquals(count, 0, "Start with zero children");
+
+            for (int i = 0; i < MAXCHILDREN; i++) {
+                // spawn and wait for instructions
+                spawned.add(JavaChild.spawnJavaChild("pid", "stdin"));
+            }
+
+            List<ProcessHandle> subprocesses = getChildren(self);
+            subprocesses.forEach(ProcessUtil::printProcess);
+            count = subprocesses.size();
+            Assert.assertEquals(count, MAXCHILDREN, "Wrong number of spawned children");
+
+            // Send exit command to each spawned Process
+            spawned.forEach(p -> {
+                    try {
+                        p.sendAction("exit", "");
+                    } catch (IOException ex) {
+                        Assert.fail("IOException in sendAction", ex);
+                    }
+                });
+
+            // Wait for each Process to exit
+            spawned.forEach(p -> {
+                    do {
+                        try {
+                            Assert.assertEquals(p.waitFor(), 0, "exit status incorrect");
+                            break;
+                        } catch (InterruptedException  ex) {
+                            continue; // Retry
+                        }
+                    } while (true);
+                });
+
+            // Verify that ProcessHandle.isAlive sees each of them as not alive
+            for (ProcessHandle ph : subprocesses) {
+                Assert.assertFalse(ph.isAlive(),
+                        "ProcessHandle.isAlive for exited process: " + ph);
+            }
+
+            // Verify no current children are visible
+            count = getChildren(self).size();
+            Assert.assertEquals(count, 0, "Children destroyed, should be zero");
+
+        } catch (IOException ioe) {
+            Assert.fail("unable to spawn process", ioe);
+        } finally {
+            // Cleanup any left over processes
+            spawned.stream().map(Process::toHandle)
+                    .filter(ProcessHandle::isAlive)
+                    .forEach(ph -> printDeep(ph, "test1 cleanup: "));
+            destroyProcessTree(ProcessHandle.current());
+        }
+    }
+
+    /**
+     * Test counting and spawning and counting of Processes.
+     */
+    @Test
+    public static void test2() {
+        ProcessHandle p1Handle = null;
+        try {
+            ProcessHandle self = ProcessHandle.current();
+            List<ProcessHandle> initialChildren = getChildren(self);
+            long count = initialChildren.size();
+            if (count > 0) {
+                initialChildren.forEach(p -> printDeep(p, "test2 initial unexpected: "));
+                Assert.assertEquals(count, 0, "Start with zero children (except Windows conhost.exe)");
+            }
+
+            JavaChild p1 = JavaChild.spawnJavaChild("stdin");
+            p1Handle = p1.toHandle();
+            printf("  p1 pid: %d%n", p1.getPid());
+
+            int spawnNew = 3;
+            p1.sendAction("spawn", spawnNew, "stdin");
+
+            // Wait for direct children to be created and save the list
+            List<ProcessHandle> subprocesses = waitForAllChildren(p1Handle, spawnNew);
+            for (ProcessHandle ph : subprocesses) {
+                Assert.assertTrue(ph.isAlive(), "Child should be alive: " + ph);
+            }
+
+            // Each child spawns two processes and waits for commands
+            int spawnNewSub = 2;
+            p1.sendAction("child", "spawn", spawnNewSub, "stdin");
+
+            // For each spawned child, wait for its children
+            for (ProcessHandle p : subprocesses) {
+                List<ProcessHandle> grandChildren = waitForChildren(p, spawnNewSub);
+            }
+
+            List<ProcessHandle> allChildren = getAllChildren(p1Handle);
+            printf(" allChildren:  %s%n",
+                    allChildren.stream().map(p -> p.getPid())
+                            .collect(Collectors.toList()));
+            for (ProcessHandle ph : allChildren) {
+                Assert.assertEquals(ph.isAlive(), true, "Child should be alive: " + ph);
+            }
+
+            // Closing JavaChild's InputStream will cause all children to exit
+            p1.getOutputStream().close();
+
+            for (ProcessHandle p : allChildren) {
+                try {
+                    p.onExit().get();       // wait for the child to exit
+                } catch (ExecutionException e) {
+                    Assert.fail("waiting for process to exit", e);
+                }
+            }
+            p1.waitFor();           // wait for spawned process to exit
+
+            List<ProcessHandle> remaining = getChildren(self);
+            remaining.forEach(ph -> Assert.assertFalse(ph.isAlive(),
+                            "process should not be alive: " + ph));
+        } catch (IOException | InterruptedException t) {
+            t.printStackTrace();
+            throw new RuntimeException(t);
+        } finally {
+            // Cleanup any left over processes
+            if (p1Handle.isAlive()) {
+                printDeep(p1Handle, "test2 cleanup: ");
+            }
+            destroyProcessTree(ProcessHandle.current());
+        }
+    }
+
+    /**
+     * Test destroy of processes.
+     */
+    @Test
+    public static void test3() {
+        try {
+            ProcessHandle self = ProcessHandle.current();
+
+            JavaChild p1 = JavaChild.spawnJavaChild("stdin");
+            ProcessHandle p1Handle = p1.toHandle();
+            printf(" p1: %s%n", p1.getPid());
+            long count = getChildren(self).size();
+            Assert.assertEquals(count, 1, "Wrong number of spawned children");
+
+            int newChildren = 3;
+            // Spawn children and have them wait
+            p1.sendAction("spawn", newChildren, "stdin");
+
+            // Wait for the new processes and save the list
+            List<ProcessHandle> subprocesses = waitForAllChildren(p1Handle, newChildren);
+            printDeep(p1Handle, "allChildren");
+
+            Assert.assertEquals(subprocesses.size(), newChildren, "Wrong number of children");
+
+            p1.children().filter(TreeTest::isNotWindowsConsole)
+                    .forEach(ProcessHandle::destroyForcibly);
+
+            self.children().filter(TreeTest::isNotWindowsConsole)
+                    .forEach(ProcessHandle::destroyForcibly);
+
+            do {
+                Thread.sleep(500L);      // It will happen but don't burn the cpu
+                Object[] children = self.allChildren()
+                        .filter(TreeTest::isNotWindowsConsole)
+                        .toArray();
+                count = children.length;
+                printf(" waiting for subprocesses of %s to terminate," +
+                                " expected: 0, current: %d, children: %s%n", self, count,
+                        Arrays.toString(children));
+                printDeep(self, "");
+            } while (count > 0);
+
+            boolean ex1 = p1.waitFor(5, TimeUnit.SECONDS);
+            Assert.assertTrue(ex1, "Subprocess should have exited: " + p1);
+
+            for (ProcessHandle p : subprocesses) {
+                Assert.assertFalse(p.isAlive(), "Destroyed process.isAlive: " + p +
+                        ", parent: " + p.parent() +
+                        ", info: " + p.info().toString());
+            }
+
+        } catch (IOException ioe) {
+            Assert.fail("Spawn of subprocess failed", ioe);
+        } catch (InterruptedException inte) {
+            Assert.fail("InterruptedException", inte);
+        }
+    }
+
+    /**
+     * Test (Not really a test) that dumps the list of all Processes.
+     */
+    @Test
+    public static void test4() {
+        printf("    Parent     Child  Info%n");
+        Stream<ProcessHandle> s = ProcessHandle.allProcesses();
+        ProcessHandle[] processes = s.toArray(ProcessHandle[]::new);
+        int len = processes.length;
+        ProcessHandle[] parent = new ProcessHandle[len];
+        Set<ProcessHandle> processesSet =
+                Arrays.stream(processes).collect(Collectors.toSet());
+        Integer[] sortindex = new Integer[len];
+        for (int i = 0; i < len; i++) {
+            sortindex[i] = i;
+         }
+        for (int i = 0; i < len; i++) {
+            parent[sortindex[i]] = processes[sortindex[i]].parent().orElse(null);
+        }
+        Arrays.sort(sortindex, (i1, i2) -> {
+            int cmp = Long.compare((parent[i1] == null ? 0L : parent[i1].getPid()),
+                    (parent[i2] == null ? 0L : parent[i2].getPid()));
+            if (cmp == 0) {
+                cmp = Long.compare((processes[i1] == null ? 0L : processes[i1].getPid()),
+                        (processes[i2] == null ? 0L : processes[i2].getPid()));
+            }
+            return cmp;
+        });
+        boolean fail = false;
+        for (int i = 0; i < len; i++) {
+            ProcessHandle p = processes[sortindex[i]];
+            ProcessHandle p_parent = parent[sortindex[i]];
+            ProcessHandle.Info info = p.info();
+            String indent = "    ";
+            if (p_parent != null) {
+                if (!processesSet.contains(p_parent)) {
+                    fail = true;
+                    indent = "*** ";
+                }
+            }
+            printf("%s %7s, %7s, %s%n", indent, p_parent, p, info);
+        }
+        Assert.assertFalse(fail, "Parents missing from all Processes");
+
+    }
+
+    /**
+     * A test for scale; launch a large number (39) of subprocesses.
+     */
+    @Test
+    public static void test5() {
+        int factor = 2;
+        ProcessHandle p1Handle = null;
+        Instant start = Instant.now();
+        try {
+            JavaChild p1 = JavaChild.spawnJavaChild("stdin");
+            p1Handle = p1.toHandle();
+
+            printf("Spawning %d x %d x %d processes, pid: %d%n",
+                    factor, factor, factor, p1.getPid());
+
+            // Start the first tier of subprocesses
+            p1.sendAction("spawn", factor, "stdin");
+
+            // Start the second tier of subprocesses
+            p1.sendAction("child", "spawn", factor, "stdin");
+
+            // Start the third tier of subprocesses
+            p1.sendAction("child", "child", "spawn", factor, "stdin");
+
+            int newChildren = factor * (1 + factor * (1 + factor));
+            List<ProcessHandle> children = ProcessUtil.waitForAllChildren(p1Handle, newChildren);
+
+            Assert.assertEquals(p1.children()
+                    .filter(ProcessUtil::isNotWindowsConsole)
+                    .count(), factor, "expected direct children");
+            Assert.assertEquals(p1.allChildren()
+                    .filter(ProcessUtil::isNotWindowsConsole)
+                    .count(),
+                    factor * factor * factor + factor * factor + factor,
+                    "expected all children");
+
+            List<ProcessHandle> subprocesses = p1.allChildren()
+                    .filter(ProcessUtil::isNotWindowsConsole)
+                    .collect(Collectors.toList());
+            printf(" allChildren:  %s%n",
+                    subprocesses.stream().map(p -> p.getPid())
+                    .collect(Collectors.toList()));
+
+            p1.getOutputStream().close();  // Close stdin for the controlling p1
+            p1.waitFor();
+        } catch (InterruptedException | IOException ex) {
+            Assert.fail("Unexpected Exception", ex);
+        } finally {
+            printf("Duration: %s%n", Duration.between(start, Instant.now()));
+            // Cleanup any left over processes
+            if (p1Handle.isAlive()) {
+                printDeep(p1Handle, "test5 cleanup: ");
+            }
+            destroyProcessTree(ProcessHandle.current());
+        }
+    }
+
+}