4244896: (process) Provide System.getPid(), System.killProcess(String pid)

Reviewed-by: alanb
This commit is contained in:
Rob McKenna 2012-06-26 13:27:26 +01:00
parent d558c37a5b
commit 54ddaf5ab7
9 changed files with 598 additions and 26 deletions

View file

@ -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 @@ public abstract class Process {
* @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 @@ public abstract class Process {
* @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 @@ public abstract class Process {
* @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 @@ public abstract class Process {
* 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 @@ public abstract class Process {
* @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;
}
}
}

View file

@ -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.Arrays;
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 @@ final class UNIXProcess extends Process {
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 @@ final class UNIXProcess extends Process {
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 @@ final class UNIXProcess extends Process {
// 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();

View file

@ -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.Arrays;
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 @@ final class UNIXProcess extends Process {
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 @@ final class UNIXProcess extends Process {
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 @@ final class UNIXProcess extends Process {
// 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();

View file

@ -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 @@ final class UNIXProcess extends Process {
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 @@ final class UNIXProcess extends Process {
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 @@ final class UNIXProcess extends Process {
// 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 @@ final class UNIXProcess extends Process {
}
}
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

View file

@ -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 @@ Java_java_lang_UNIXProcess_forkAndExec(JNIEnv *env,
}
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);
}

View file

@ -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.io.BufferedOutputStream;
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 @@ final class ProcessImpl extends Process {
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.
*

View file

@ -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
@ -304,12 +304,39 @@ Java_java_lang_ProcessImpl_waitForInterruptibly(JNIEnv *env, jclass ignored, jlo
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)
{

View file

@ -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 static java.lang.ProcessBuilder.Redirect.*;
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 @@ public class Basic {
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 @@ public class Basic {
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) {

View file

@ -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();
}
}
}