Mercurial > hg > icedtea9-forest > jdk
changeset 5768:ff0da4ea08a2
4244896: (process) Provide System.getPid(), System.killProcess(String pid)
Reviewed-by: alanb
author | robm |
---|---|
date | Tue, 26 Jun 2012 13:27:26 +0100 |
parents | 4a4a04bfeece |
children | 94b525ce3653 7e9a7400329b |
files | src/share/classes/java/lang/Process.java src/solaris/classes/java/lang/UNIXProcess.java.bsd src/solaris/classes/java/lang/UNIXProcess.java.linux src/solaris/classes/java/lang/UNIXProcess.java.solaris src/solaris/native/java/lang/UNIXProcess_md.c src/windows/classes/java/lang/ProcessImpl.java src/windows/native/java/lang/ProcessImpl_md.c test/java/lang/ProcessBuilder/Basic.java test/java/lang/ProcessBuilder/DestroyTest.java |
diffstat | 9 files changed, 598 insertions(+), 26 deletions(-) [+] |
line wrap: on
line diff
--- a/src/share/classes/java/lang/Process.java Mon Jun 25 14:19:38 2012 +0100 +++ b/src/share/classes/java/lang/Process.java Tue Jun 26 13:27:26 2012 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995, 2008, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1995, 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,6 +26,7 @@ package java.lang; import java.io.*; +import java.util.concurrent.TimeUnit; /** * The {@link ProcessBuilder#start()} and @@ -91,7 +92,7 @@ * @return the output stream connected to the normal input of the * subprocess */ - abstract public OutputStream getOutputStream(); + public abstract OutputStream getOutputStream(); /** * Returns the input stream connected to the normal output of the @@ -117,7 +118,7 @@ * @return the input stream connected to the normal output of the * subprocess */ - abstract public InputStream getInputStream(); + public abstract InputStream getInputStream(); /** * Returns the input stream connected to the error output of the @@ -138,7 +139,7 @@ * @return the input stream connected to the error output of * the subprocess */ - abstract public InputStream getErrorStream(); + public abstract InputStream getErrorStream(); /** * Causes the current thread to wait, if necessary, until the @@ -156,7 +157,51 @@ * thread while it is waiting, then the wait is ended and * an {@link InterruptedException} is thrown. */ - abstract public int waitFor() throws InterruptedException; + public abstract int waitFor() throws InterruptedException; + + /** + * Causes the current thread to wait, if necessary, until the + * subprocess 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 + * 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}. + * + * <p>The default implementation of this methods polls the {@code exitValue} + * to check if the process has terminated. Concrete implementations of this + * class are strongly encouraged to override this method with a more + * efficient implementation. + * + * @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. + * @throws InterruptedException if the current thread is interrupted + * while waiting. + * @throws NullPointerException if unit is null + * @since 1.8 + */ + public boolean waitFor(long timeout, TimeUnit unit) + throws InterruptedException + { + long startTime = System.nanoTime(); + long rem = unit.toNanos(timeout); + + do { + try { + exitValue(); + return true; + } catch(IllegalThreadStateException ex) { + if (rem > 0) + Thread.sleep( + Math.min(TimeUnit.NANOSECONDS.toMillis(rem) + 1, 100)); + } + rem = unit.toNanos(timeout) - (System.nanoTime() - startTime); + } while (rem > 0); + return false; + } /** * Returns the exit value for the subprocess. @@ -167,11 +212,54 @@ * @throws IllegalThreadStateException if the subprocess represented * by this {@code Process} object has not yet terminated */ - abstract public int exitValue(); + public abstract int exitValue(); + + /** + * Kills the subprocess. Whether the subprocess represented by this + * {@code Process} object is forcibly terminated or not is + * implementation dependent. + */ + public abstract void destroy(); /** * Kills the subprocess. The subprocess represented by this * {@code Process} object is forcibly terminated. + * + * <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. + * 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. + * @since 1.8 */ - abstract public void destroy(); + public Process destroyForcibly() { + destroy(); + return this; + } + + /** + * Tests whether the subprocess represented by this {@code Process} is + * alive. + * + * @return {@code true} if the subprocess represented by this + * {@code Process} object has not yet terminated. + * @since 1.8 + */ + public boolean isAlive() { + try { + exitValue(); + return false; + } catch(IllegalThreadStateException e) { + return true; + } + } }
--- a/src/solaris/classes/java/lang/UNIXProcess.java.bsd Mon Jun 25 14:19:38 2012 +0100 +++ b/src/solaris/classes/java/lang/UNIXProcess.java.bsd Tue Jun 26 13:27:26 2012 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1995, 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -38,6 +38,7 @@ import java.util.concurrent.Executors; import java.util.concurrent.Executor; import java.util.concurrent.ThreadFactory; +import java.util.concurrent.TimeUnit; import java.security.AccessController; import static java.security.AccessController.doPrivileged; import java.security.PrivilegedAction; @@ -211,6 +212,24 @@ } return exitcode; } + + @Override + public synchronized boolean waitFor(long timeout, TimeUnit unit) + throws InterruptedException + { + if (hasExited) return true; + if (timeout <= 0) return false; + + long timeoutAsNanos = unit.toNanos(timeout); + long startTime = System.nanoTime(); + long rem = timeoutAsNanos; + + while (!hasExited && (rem > 0)) { + wait(Math.max(TimeUnit.NANOSECONDS.toMillis(rem), 1)); + rem = timeoutAsNanos - (System.nanoTime() - startTime); + } + return hasExited; + } public synchronized int exitValue() { if (!hasExited) { @@ -219,8 +238,8 @@ return exitcode; } - private static native void destroyProcess(int pid); - public void destroy() { + private static native void destroyProcess(int pid, boolean force); + private void destroy(boolean force) { // There is a risk that pid will be recycled, causing us to // kill the wrong process! So we only terminate processes // that appear to still be running. Even with this check, @@ -229,13 +248,28 @@ // soon, so this is quite safe. synchronized (this) { if (!hasExited) - destroyProcess(pid); + destroyProcess(pid, force); } try { stdin.close(); } catch (IOException ignored) {} try { stdout.close(); } catch (IOException ignored) {} try { stderr.close(); } catch (IOException ignored) {} } + public void destroy() { + destroy(false); + } + + @Override + public Process destroyForcibly() { + destroy(true); + return this; + } + + @Override + public synchronized boolean isAlive() { + return !hasExited; + } + /* This routine initializes JNI field offsets for the class */ private static native void initIDs();
--- a/src/solaris/classes/java/lang/UNIXProcess.java.linux Mon Jun 25 14:19:38 2012 +0100 +++ b/src/solaris/classes/java/lang/UNIXProcess.java.linux Tue Jun 26 13:27:26 2012 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1995, 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -38,6 +38,7 @@ import java.util.concurrent.Executors; import java.util.concurrent.Executor; import java.util.concurrent.ThreadFactory; +import java.util.concurrent.TimeUnit; import java.security.AccessController; import static java.security.AccessController.doPrivileged; import java.security.PrivilegedAction; @@ -212,6 +213,24 @@ return exitcode; } + @Override + public synchronized boolean waitFor(long timeout, TimeUnit unit) + throws InterruptedException + { + if (hasExited) return true; + if (timeout <= 0) return false; + + long timeoutAsNanos = unit.toNanos(timeout); + long startTime = System.nanoTime(); + long rem = timeoutAsNanos; + + while (!hasExited && (rem > 0)) { + wait(Math.max(TimeUnit.NANOSECONDS.toMillis(rem), 1)); + rem = timeoutAsNanos - (System.nanoTime() - startTime); + } + return hasExited; + } + public synchronized int exitValue() { if (!hasExited) { throw new IllegalThreadStateException("process hasn't exited"); @@ -219,8 +238,8 @@ return exitcode; } - private static native void destroyProcess(int pid); - public void destroy() { + private static native void destroyProcess(int pid, boolean force); + private void destroy(boolean force) { // There is a risk that pid will be recycled, causing us to // kill the wrong process! So we only terminate processes // that appear to still be running. Even with this check, @@ -229,13 +248,28 @@ // soon, so this is quite safe. synchronized (this) { if (!hasExited) - destroyProcess(pid); + destroyProcess(pid, force); } try { stdin.close(); } catch (IOException ignored) {} try { stdout.close(); } catch (IOException ignored) {} try { stderr.close(); } catch (IOException ignored) {} } + public void destroy() { + destroy(false); + } + + @Override + public Process destroyForcibly() { + destroy(true); + return this; + } + + @Override + public synchronized boolean isAlive() { + return !hasExited; + } + /* This routine initializes JNI field offsets for the class */ private static native void initIDs();
--- a/src/solaris/classes/java/lang/UNIXProcess.java.solaris Mon Jun 25 14:19:38 2012 +0100 +++ b/src/solaris/classes/java/lang/UNIXProcess.java.solaris Tue Jun 26 13:27:26 2012 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1995, 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,6 +26,7 @@ package java.lang; import java.io.*; +import java.util.concurrent.TimeUnit; /* java.lang.Process subclass in the UNIX environment. * @@ -158,6 +159,24 @@ return exitcode; } + @Override + public synchronized boolean waitFor(long timeout, TimeUnit unit) + throws InterruptedException + { + if (hasExited) return true; + if (timeout <= 0) return false; + + long timeoutAsNanos = unit.toNanos(timeout); + long startTime = System.nanoTime(); + long rem = timeoutAsNanos; + + while (!hasExited && (rem > 0)) { + wait(Math.max(TimeUnit.NANOSECONDS.toMillis(rem), 1)); + rem = timeoutAsNanos - (System.nanoTime() - startTime); + } + return hasExited; + } + public synchronized int exitValue() { if (!hasExited) { throw new IllegalThreadStateException("process hasn't exited"); @@ -165,8 +184,8 @@ return exitcode; } - private static native void destroyProcess(int pid); - public synchronized void destroy() { + private static native void destroyProcess(int pid, boolean force); + private synchronized void destroy(boolean force) { // There is a risk that pid will be recycled, causing us to // kill the wrong process! So we only terminate processes // that appear to still be running. Even with this check, @@ -174,7 +193,7 @@ // is very small, and OSes try hard to not recycle pids too // soon, so this is quite safe. if (!hasExited) - destroyProcess(pid); + destroyProcess(pid, force); try { stdin_stream.close(); if (stdout_inner_stream != null) @@ -187,6 +206,21 @@ } } + public void destroy() { + destroy(false); + } + + @Override + public Process destroyForcibly() { + destroy(true); + return this; + } + + @Override + public synchronized boolean isAlive() { + return !hasExited; + } + // A FileInputStream that supports the deferment of the actual close // operation until the last pending I/O operation on the stream has // finished. This is required on Solaris because we must close the stdin
--- a/src/solaris/native/java/lang/UNIXProcess_md.c Mon Jun 25 14:19:38 2012 +0100 +++ b/src/solaris/native/java/lang/UNIXProcess_md.c Tue Jun 26 13:27:26 2012 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995, 2008, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1995, 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -955,7 +955,11 @@ } JNIEXPORT void JNICALL -Java_java_lang_UNIXProcess_destroyProcess(JNIEnv *env, jobject junk, jint pid) +Java_java_lang_UNIXProcess_destroyProcess(JNIEnv *env, + jobject junk, + jint pid, + jboolean force) { - kill(pid, SIGTERM); + int sig = (force == JNI_TRUE) ? SIGKILL : SIGTERM; + kill(pid, sig); }
--- a/src/windows/classes/java/lang/ProcessImpl.java Mon Jun 25 14:19:38 2012 +0100 +++ b/src/windows/classes/java/lang/ProcessImpl.java Tue Jun 26 13:27:26 2012 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1995, 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -37,6 +37,7 @@ import java.lang.ProcessBuilder.Redirect; import java.security.AccessController; import java.security.PrivilegedAction; +import java.util.concurrent.TimeUnit; /* This class is for the exclusive use of ProcessBuilder.start() to * create new processes. @@ -254,11 +255,44 @@ throw new InterruptedException(); return exitValue(); } + private static native void waitForInterruptibly(long handle); + @Override + public boolean waitFor(long timeout, TimeUnit unit) + throws InterruptedException + { + if (getExitCodeProcess(handle) != STILL_ACTIVE) return true; + if (timeout <= 0) return false; + + long msTimeout = unit.toMillis(timeout); + + waitForTimeoutInterruptibly(handle, msTimeout); + if (Thread.interrupted()) + throw new InterruptedException(); + return (getExitCodeProcess(handle) != STILL_ACTIVE); + } + + private static native void waitForTimeoutInterruptibly( + long handle, long timeout); + public void destroy() { terminateProcess(handle); } + + @Override + public Process destroyForcibly() { + destroy(); + return this; + } + private static native void terminateProcess(long handle); + @Override + public boolean isAlive() { + return isProcessAlive(handle); + } + + private static native boolean isProcessAlive(long handle); + /** * Create a process using the win32 function CreateProcess. *
--- a/src/windows/native/java/lang/ProcessImpl_md.c Mon Jun 25 14:19:38 2012 +0100 +++ b/src/windows/native/java/lang/ProcessImpl_md.c Tue Jun 26 13:27:26 2012 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -299,18 +299,45 @@ if (WaitForMultipleObjects(sizeof(events)/sizeof(events[0]), events, FALSE, /* Wait for ANY event */ - INFINITE) /* Wait forever */ + INFINITE) /* Wait forever */ == WAIT_FAILED) win32Error(env, "WaitForMultipleObjects"); } JNIEXPORT void JNICALL +Java_java_lang_ProcessImpl_waitForTimeoutInterruptibly(JNIEnv *env, + jclass ignored, + jlong handle, + jlong timeout) +{ + HANDLE events[2]; + DWORD dwTimeout = (DWORD)timeout; + DWORD result; + events[0] = (HANDLE) handle; + events[1] = JVM_GetThreadInterruptEvent(); + result = WaitForMultipleObjects(sizeof(events)/sizeof(events[0]), events, + FALSE, /* Wait for ANY event */ + dwTimeout); /* Wait for dwTimeout */ + + if (result == WAIT_FAILED) + win32Error(env, "WaitForMultipleObjects"); +} + +JNIEXPORT void JNICALL Java_java_lang_ProcessImpl_terminateProcess(JNIEnv *env, jclass ignored, jlong handle) { TerminateProcess((HANDLE) handle, 1); } JNIEXPORT jboolean JNICALL +Java_java_lang_ProcessImpl_isProcessAlive(JNIEnv *env, jclass ignored, jlong handle) +{ + DWORD dwExitStatus; + GetExitCodeProcess(handle, &dwExitStatus); + return dwExitStatus == STILL_ACTIVE; +} + +JNIEXPORT jboolean JNICALL Java_java_lang_ProcessImpl_closeHandle(JNIEnv *env, jclass ignored, jlong handle) { return CloseHandle((HANDLE) handle);
--- a/test/java/lang/ProcessBuilder/Basic.java Mon Jun 25 14:19:38 2012 +0100 +++ b/test/java/lang/ProcessBuilder/Basic.java Tue Jun 26 13:27:26 2012 +0100 @@ -26,7 +26,7 @@ * @bug 4199068 4738465 4937983 4930681 4926230 4931433 4932663 4986689 * 5026830 5023243 5070673 4052517 4811767 6192449 6397034 6413313 * 6464154 6523983 6206031 4960438 6631352 6631966 6850957 6850958 - * 4947220 7018606 7034570 + * 4947220 7018606 7034570 4244896 * @summary Basic tests for Process and Environment Variable code * @run main/othervm/timeout=300 Basic * @author Martin Buchholz @@ -38,6 +38,7 @@ import java.io.*; import java.util.*; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; import java.security.*; import java.util.regex.Pattern; import java.util.regex.Matcher; @@ -636,6 +637,44 @@ static boolean is() { return is; } } + static class DelegatingProcess extends Process { + final Process p; + + DelegatingProcess(Process p) { + this.p = p; + } + + @Override + public void destroy() { + p.destroy(); + } + + @Override + public int exitValue() { + return p.exitValue(); + } + + @Override + public int waitFor() throws InterruptedException { + return p.waitFor(); + } + + @Override + public OutputStream getOutputStream() { + return p.getOutputStream(); + } + + @Override + public InputStream getInputStream() { + return p.getInputStream(); + } + + @Override + public InputStream getErrorStream() { + return p.getErrorStream(); + } + } + private static boolean matches(String str, String regex) { return Pattern.compile(regex).matcher(str).find(); } @@ -2090,6 +2129,112 @@ policy.setPermissions(new RuntimePermission("setSecurityManager")); System.setSecurityManager(null); + //---------------------------------------------------------------- + // Check that Process.isAlive() & + // Process.waitFor(0, TimeUnit.MILLISECONDS) work as expected. + //---------------------------------------------------------------- + try { + List<String> childArgs = new ArrayList<String>(javaChildArgs); + childArgs.add("sleep"); + final Process p = new ProcessBuilder(childArgs).start(); + long start = System.nanoTime(); + if (!p.isAlive() || p.waitFor(0, TimeUnit.MILLISECONDS)) { + fail("Test failed: Process exited prematurely"); + } + long end = System.nanoTime(); + // give waitFor(timeout) a wide berth (100ms) + if ((end - start) > 100000000) + fail("Test failed: waitFor took too long"); + + p.destroy(); + p.waitFor(); + + if (p.isAlive() || + !p.waitFor(0, TimeUnit.MILLISECONDS)) + { + fail("Test failed: Process still alive - please terminate " + + p.toString() + " manually"); + } + } catch (Throwable t) { unexpected(t); } + + //---------------------------------------------------------------- + // Check that Process.waitFor(timeout, TimeUnit.MILLISECONDS) + // works as expected. + //---------------------------------------------------------------- + try { + List<String> childArgs = new ArrayList<String>(javaChildArgs); + childArgs.add("sleep"); + final Process p = new ProcessBuilder(childArgs).start(); + long start = System.nanoTime(); + + p.waitFor(1000, TimeUnit.MILLISECONDS); + + long end = System.nanoTime(); + if ((end - start) < 500000000) + fail("Test failed: waitFor didn't take long enough"); + + p.destroy(); + + start = System.nanoTime(); + p.waitFor(1000, TimeUnit.MILLISECONDS); + end = System.nanoTime(); + if ((end - start) > 100000000) + fail("Test failed: waitFor took too long on a dead process."); + } catch (Throwable t) { unexpected(t); } + + //---------------------------------------------------------------- + // Check that Process.waitFor(timeout, TimeUnit.MILLISECONDS) + // interrupt works as expected. + //---------------------------------------------------------------- + try { + List<String> childArgs = new ArrayList<String>(javaChildArgs); + childArgs.add("sleep"); + final Process p = new ProcessBuilder(childArgs).start(); + final long start = System.nanoTime(); + + final Thread thread = new Thread() { + public void run() { + try { + try { + p.waitFor(10000, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + return; + } + fail("waitFor() wasn't interrupted"); + } catch (Throwable t) { unexpected(t); }}}; + + thread.start(); + Thread.sleep(1000); + thread.interrupt(); + p.destroy(); + } catch (Throwable t) { unexpected(t); } + + //---------------------------------------------------------------- + // Check the default implementation for + // Process.waitFor(long, TimeUnit) + //---------------------------------------------------------------- + try { + List<String> childArgs = new ArrayList<String>(javaChildArgs); + childArgs.add("sleep"); + final Process proc = new ProcessBuilder(childArgs).start(); + DelegatingProcess p = new DelegatingProcess(proc); + long start = System.nanoTime(); + + p.waitFor(1000, TimeUnit.MILLISECONDS); + + long end = System.nanoTime(); + if ((end - start) < 500000000) + fail("Test failed: waitFor didn't take long enough"); + + p.destroy(); + + start = System.nanoTime(); + p.waitFor(1000, TimeUnit.MILLISECONDS); + end = System.nanoTime(); + // allow for the less accurate default implementation + if ((end - start) > 200000000) + fail("Test failed: waitFor took too long on a dead process."); + } catch (Throwable t) { unexpected(t); } } static void closeStreams(Process p) {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/java/lang/ProcessBuilder/DestroyTest.java Tue Jun 26 13:27:26 2012 +0100 @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @bug 4244896 + * @summary Test for the various platform specific implementations of + * destroyForcibly. + */ + +import java.io.*; +import java.util.ArrayList; +import java.util.concurrent.TimeUnit; + +abstract class ProcessTest implements Runnable { + ProcessBuilder bldr; + Process p; + + public Process killProc(boolean force) throws Exception { + if (force) { + p.destroyForcibly(); + } else { + p.destroy(); + } + return p; + } + + public boolean isAlive() { + return p.isAlive(); + } + + public void run() { + try { + String line; + BufferedReader is = + new BufferedReader(new InputStreamReader(p.getInputStream())); + while ((line = is.readLine()) != null) + System.err.println("ProcessTrap: " + line); + } catch(IOException e) { + if (!e.getMessage().matches("[Ss]tream [Cc]losed")) { + throw new RuntimeException(e); + } + } + } + + public abstract void runTest() throws Exception; +} + +class UnixTest extends ProcessTest { + public UnixTest(File script) throws IOException { + script.deleteOnExit(); + createScript(script); + bldr = new ProcessBuilder(script.getCanonicalPath()); + bldr.redirectErrorStream(true); + bldr.directory(new File(".")); + p = bldr.start(); + } + + void createScript(File processTrapScript) throws IOException { + processTrapScript.deleteOnExit(); + FileWriter fstream = new FileWriter(processTrapScript); + try (BufferedWriter out = new BufferedWriter(fstream)) { + out.write("#!/bin/bash\n" + + "echo \\\"ProcessTrap.sh started: trapping SIGTERM/SIGINT\\\"\n" + + "trap bashtrap SIGTERM SIGINT\n" + + "bashtrap()\n" + + "{\n" + + " echo \\\"SIGTERM/SIGINT detected!\\\"\n" + + "}\n" + + "\n" + + "while :\n" + + "do\n" + + " sleep 1;\n" + + "done\n"); + } + processTrapScript.setExecutable(true, true); + } + + @Override + public void runTest() throws Exception { + killProc(false); + Thread.sleep(1000); + if (!p.isAlive()) + throw new RuntimeException("Process terminated prematurely."); + killProc(true).waitFor(); + if (p.isAlive()) + throw new RuntimeException("Problem terminating the process."); + } +} + +class MacTest extends UnixTest { + public MacTest(File script) throws IOException { + super(script); + } + + @Override + public void runTest() throws Exception { + // On Mac, it appears that when we close the processes streams + // after a destroy() call, the process terminates with a + // SIGPIPE even if it was trapping the SIGTERM, so as with + // windows, we skip the trap test and go straight to destroyForcibly(). + killProc(true).waitFor(); + if (p.isAlive()) + throw new RuntimeException("Problem terminating the process."); + } +} + +class WindowsTest extends ProcessTest { + public WindowsTest() throws IOException { + bldr = new ProcessBuilder("ftp"); + bldr.redirectErrorStream(true); + bldr.directory(new File(".")); + p = bldr.start(); + } + + @Override + public void runTest() throws Exception { + killProc(true).waitFor(); + } +} + +public class DestroyTest { + + public static ProcessTest getTest() throws Exception { + String osName = System.getProperty("os.name"); + if (osName.startsWith("Windows")) { + return new WindowsTest(); + } else if (osName.startsWith("Linux") == true) { + return new UnixTest( + File.createTempFile("ProcessTrap-", ".sh",null)); + } else if (osName.startsWith("Mac OS")) { + return new MacTest( + File.createTempFile("ProcessTrap-", ".sh",null)); + } else if (osName.equals("SunOS")) { + return new UnixTest( + File.createTempFile("ProcessTrap-", ".sh",null)); + } + return null; + } + + public static void main(String args[]) throws Exception { + ProcessTest test = getTest(); + if (test == null) { + throw new RuntimeException("Unrecognised OS"); + } else { + new Thread(test).start(); + Thread.sleep(1000); + test.runTest(); + } + } +} +