Merge
This commit is contained in:
commit
cc358443d5
@ -85,7 +85,6 @@ SUNWprivate_1.1 {
|
|||||||
Java_java_io_FileOutputStream_close0;
|
Java_java_io_FileOutputStream_close0;
|
||||||
Java_java_io_FileOutputStream_initIDs;
|
Java_java_io_FileOutputStream_initIDs;
|
||||||
Java_java_io_FileOutputStream_open;
|
Java_java_io_FileOutputStream_open;
|
||||||
Java_java_io_FileOutputStream_openAppend;
|
|
||||||
Java_java_io_FileOutputStream_write;
|
Java_java_io_FileOutputStream_write;
|
||||||
Java_java_io_FileOutputStream_writeBytes;
|
Java_java_io_FileOutputStream_writeBytes;
|
||||||
Java_java_io_FileSystem_getFileSystem;
|
Java_java_io_FileSystem_getFileSystem;
|
||||||
|
@ -48,14 +48,14 @@ public
|
|||||||
class FileInputStream extends InputStream
|
class FileInputStream extends InputStream
|
||||||
{
|
{
|
||||||
/* File Descriptor - handle to the open file */
|
/* File Descriptor - handle to the open file */
|
||||||
private FileDescriptor fd;
|
private final FileDescriptor fd;
|
||||||
|
|
||||||
private FileChannel channel = null;
|
private FileChannel channel = null;
|
||||||
|
|
||||||
private Object closeLock = new Object();
|
private final Object closeLock = new Object();
|
||||||
private volatile boolean closed = false;
|
private volatile boolean closed = false;
|
||||||
|
|
||||||
private static ThreadLocal<Boolean> runningFinalize =
|
private static final ThreadLocal<Boolean> runningFinalize =
|
||||||
new ThreadLocal<Boolean>();
|
new ThreadLocal<Boolean>();
|
||||||
|
|
||||||
private static boolean isRunningFinalize() {
|
private static boolean isRunningFinalize() {
|
||||||
@ -151,7 +151,7 @@ class FileInputStream extends InputStream
|
|||||||
* is thrown.
|
* is thrown.
|
||||||
* <p>
|
* <p>
|
||||||
* This constructor does not throw an exception if <code>fdObj</code>
|
* This constructor does not throw an exception if <code>fdObj</code>
|
||||||
* is {link java.io.FileDescriptor#valid() invalid}.
|
* is {@link java.io.FileDescriptor#valid() invalid}.
|
||||||
* However, if the methods are invoked on the resulting stream to attempt
|
* However, if the methods are invoked on the resulting stream to attempt
|
||||||
* I/O on the stream, an <code>IOException</code> is thrown.
|
* I/O on the stream, an <code>IOException</code> is thrown.
|
||||||
*
|
*
|
||||||
@ -389,7 +389,7 @@ class FileInputStream extends InputStream
|
|||||||
* @see java.io.FileInputStream#close()
|
* @see java.io.FileInputStream#close()
|
||||||
*/
|
*/
|
||||||
protected void finalize() throws IOException {
|
protected void finalize() throws IOException {
|
||||||
if ((fd != null) && (fd != fd.in)) {
|
if ((fd != null) && (fd != FileDescriptor.in)) {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Finalizer should not release the FileDescriptor if another
|
* Finalizer should not release the FileDescriptor if another
|
||||||
|
@ -52,19 +52,15 @@ public
|
|||||||
class FileOutputStream extends OutputStream
|
class FileOutputStream extends OutputStream
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* The system dependent file descriptor. The value is
|
* The system dependent file descriptor.
|
||||||
* 1 more than actual file descriptor. This means that
|
|
||||||
* the default value 0 indicates that the file is not open.
|
|
||||||
*/
|
*/
|
||||||
private FileDescriptor fd;
|
private final FileDescriptor fd;
|
||||||
|
|
||||||
private FileChannel channel= null;
|
private FileChannel channel= null;
|
||||||
|
|
||||||
private boolean append = false;
|
private final Object closeLock = new Object();
|
||||||
|
|
||||||
private Object closeLock = new Object();
|
|
||||||
private volatile boolean closed = false;
|
private volatile boolean closed = false;
|
||||||
private static ThreadLocal<Boolean> runningFinalize =
|
private static final ThreadLocal<Boolean> runningFinalize =
|
||||||
new ThreadLocal<Boolean>();
|
new ThreadLocal<Boolean>();
|
||||||
|
|
||||||
private static boolean isRunningFinalize() {
|
private static boolean isRunningFinalize() {
|
||||||
@ -75,7 +71,7 @@ class FileOutputStream extends OutputStream
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an output file stream to write to the file with the
|
* Creates a file output stream to write to the file with the
|
||||||
* specified name. A new <code>FileDescriptor</code> object is
|
* specified name. A new <code>FileDescriptor</code> object is
|
||||||
* created to represent this file connection.
|
* created to represent this file connection.
|
||||||
* <p>
|
* <p>
|
||||||
@ -100,8 +96,8 @@ class FileOutputStream extends OutputStream
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an output file stream to write to the file with the specified
|
* Creates a file output stream to write to the file with the specified
|
||||||
* <code>name</code>. If the second argument is <code>true</code>, then
|
* name. If the second argument is <code>true</code>, then
|
||||||
* bytes will be written to the end of the file rather than the beginning.
|
* bytes will be written to the end of the file rather than the beginning.
|
||||||
* A new <code>FileDescriptor</code> object is created to represent this
|
* A new <code>FileDescriptor</code> object is created to represent this
|
||||||
* file connection.
|
* file connection.
|
||||||
@ -202,16 +198,11 @@ class FileOutputStream extends OutputStream
|
|||||||
}
|
}
|
||||||
fd = new FileDescriptor();
|
fd = new FileDescriptor();
|
||||||
fd.incrementAndGetUseCount();
|
fd.incrementAndGetUseCount();
|
||||||
this.append = append;
|
open(name, append);
|
||||||
if (append) {
|
|
||||||
openAppend(name);
|
|
||||||
} else {
|
|
||||||
open(name);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an output file stream to write to the specified file
|
* Creates a file output stream to write to the specified file
|
||||||
* descriptor, which represents an existing connection to an actual
|
* descriptor, which represents an existing connection to an actual
|
||||||
* file in the file system.
|
* file in the file system.
|
||||||
* <p>
|
* <p>
|
||||||
@ -223,7 +214,7 @@ class FileOutputStream extends OutputStream
|
|||||||
* is thrown.
|
* is thrown.
|
||||||
* <p>
|
* <p>
|
||||||
* This constructor does not throw an exception if <code>fdObj</code>
|
* This constructor does not throw an exception if <code>fdObj</code>
|
||||||
* is {link java.io.FileDescriptor#valid() invalid}.
|
* is {@link java.io.FileDescriptor#valid() invalid}.
|
||||||
* However, if the methods are invoked on the resulting stream to attempt
|
* However, if the methods are invoked on the resulting stream to attempt
|
||||||
* I/O on the stream, an <code>IOException</code> is thrown.
|
* I/O on the stream, an <code>IOException</code> is thrown.
|
||||||
*
|
*
|
||||||
@ -252,16 +243,12 @@ class FileOutputStream extends OutputStream
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Opens a file, with the specified name, for writing.
|
* Opens a file, with the specified name, for overwriting or appending.
|
||||||
* @param name name of file to be opened
|
* @param name name of file to be opened
|
||||||
|
* @param append whether the file is to be opened in append mode
|
||||||
*/
|
*/
|
||||||
private native void open(String name) throws FileNotFoundException;
|
private native void open(String name, boolean append)
|
||||||
|
throws FileNotFoundException;
|
||||||
/**
|
|
||||||
* Opens a file, with the specified name, for appending.
|
|
||||||
* @param name name of file to be opened
|
|
||||||
*/
|
|
||||||
private native void openAppend(String name) throws FileNotFoundException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes the specified byte to this file output stream. Implements
|
* Writes the specified byte to this file output stream. Implements
|
||||||
@ -385,7 +372,7 @@ class FileOutputStream extends OutputStream
|
|||||||
public FileChannel getChannel() {
|
public FileChannel getChannel() {
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
if (channel == null) {
|
if (channel == null) {
|
||||||
channel = FileChannelImpl.open(fd, false, true, this, append);
|
channel = FileChannelImpl.open(fd, false, true, this);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Increment fd's use count. Invoking the channel's close()
|
* Increment fd's use count. Invoking the channel's close()
|
||||||
@ -408,7 +395,7 @@ class FileOutputStream extends OutputStream
|
|||||||
*/
|
*/
|
||||||
protected void finalize() throws IOException {
|
protected void finalize() throws IOException {
|
||||||
if (fd != null) {
|
if (fd != null) {
|
||||||
if (fd == fd.out || fd == fd.err) {
|
if (fd == FileDescriptor.out || fd == FileDescriptor.err) {
|
||||||
flush();
|
flush();
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
|
@ -41,18 +41,24 @@ import java.io.*;
|
|||||||
* <p>The methods that create processes may not work well for special
|
* <p>The methods that create processes may not work well for special
|
||||||
* processes on certain native platforms, such as native windowing
|
* processes on certain native platforms, such as native windowing
|
||||||
* processes, daemon processes, Win16/DOS processes on Microsoft
|
* processes, daemon processes, Win16/DOS processes on Microsoft
|
||||||
* Windows, or shell scripts. The created subprocess does not have
|
* Windows, or shell scripts.
|
||||||
* its own terminal or console. All its standard I/O (i.e. stdin,
|
*
|
||||||
* stdout, stderr) operations will be redirected to the parent process
|
* <p>By default, the created subprocess does not have its own terminal
|
||||||
* through three streams
|
* or console. All its standard I/O (i.e. stdin, stdout, stderr)
|
||||||
* ({@link #getOutputStream()},
|
* operations will be redirected to the parent process, where they can
|
||||||
* {@link #getInputStream()},
|
* be accessed via the streams obtained using the methods
|
||||||
* {@link #getErrorStream()}).
|
* {@link #getOutputStream()},
|
||||||
|
* {@link #getInputStream()}, and
|
||||||
|
* {@link #getErrorStream()}.
|
||||||
* The parent process uses these streams to feed input to and get output
|
* The parent process uses these streams to feed input to and get output
|
||||||
* from the subprocess. Because some native platforms only provide
|
* from the subprocess. Because some native platforms only provide
|
||||||
* limited buffer size for standard input and output streams, failure
|
* limited buffer size for standard input and output streams, failure
|
||||||
* to promptly write the input stream or read the output stream of
|
* to promptly write the input stream or read the output stream of
|
||||||
* the subprocess may cause the subprocess to block, and even deadlock.
|
* the subprocess may cause the subprocess to block, or even deadlock.
|
||||||
|
*
|
||||||
|
* <p>Where desired, <a href="ProcessBuilder.html#redirect-input">
|
||||||
|
* subprocess 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
|
* <p>The subprocess is not killed when there are no more references to
|
||||||
* the {@code Process} object, but rather the subprocess
|
* the {@code Process} object, but rather the subprocess
|
||||||
@ -62,16 +68,22 @@ import java.io.*;
|
|||||||
* Process} object execute asynchronously or concurrently with respect
|
* Process} object execute asynchronously or concurrently with respect
|
||||||
* to the Java process that owns the {@code Process} object.
|
* to the Java process that owns the {@code Process} object.
|
||||||
*
|
*
|
||||||
* @author unascribed
|
* <p>As of 1.5, {@link ProcessBuilder#start()} is the preferred way
|
||||||
* @see ProcessBuilder
|
* to create a {@code Process}.
|
||||||
|
*
|
||||||
* @since JDK1.0
|
* @since JDK1.0
|
||||||
*/
|
*/
|
||||||
public abstract class Process {
|
public abstract class Process {
|
||||||
/**
|
/**
|
||||||
* Returns the output stream connected to the normal input of the
|
* Returns the output stream connected to the normal input of the
|
||||||
* subprocess. Output to the stream is piped into the standard
|
* subprocess. Output to the stream is piped into the standard
|
||||||
* input stream of the process represented by this {@code Process}
|
* input of the process represented by this {@code Process} object.
|
||||||
* object.
|
*
|
||||||
|
* <p>If the standard input of the subprocess has been redirected using
|
||||||
|
* {@link ProcessBuilder#redirectInput(Redirect)
|
||||||
|
* ProcessBuilder.redirectInput}
|
||||||
|
* then this method will return a
|
||||||
|
* <a href="ProcessBuilder.html#redirect-input">null output stream</a>.
|
||||||
*
|
*
|
||||||
* <p>Implementation note: It is a good idea for the returned
|
* <p>Implementation note: It is a good idea for the returned
|
||||||
* output stream to be buffered.
|
* output stream to be buffered.
|
||||||
@ -84,30 +96,47 @@ public abstract class Process {
|
|||||||
/**
|
/**
|
||||||
* Returns the input stream connected to the normal output of the
|
* Returns the input stream connected to the normal output of the
|
||||||
* subprocess. The stream obtains data piped from the standard
|
* subprocess. The stream obtains data piped from the standard
|
||||||
* output stream of the process represented by this {@code
|
* output of the process represented by this {@code Process} object.
|
||||||
* Process} object.
|
*
|
||||||
|
* <p>If the standard output of the subprocess 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
|
||||||
|
* 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.
|
||||||
*
|
*
|
||||||
* <p>Implementation note: It is a good idea for the returned
|
* <p>Implementation note: It is a good idea for the returned
|
||||||
* input stream to be buffered.
|
* input stream to be buffered.
|
||||||
*
|
*
|
||||||
* @return the input stream connected to the normal output of the
|
* @return the input stream connected to the normal output of the
|
||||||
* subprocess
|
* subprocess
|
||||||
* @see ProcessBuilder#redirectErrorStream()
|
|
||||||
*/
|
*/
|
||||||
abstract public InputStream getInputStream();
|
abstract public InputStream getInputStream();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the input stream connected to the error output stream of
|
* Returns the input stream connected to the error output of the
|
||||||
* the subprocess. The stream obtains data piped from the error
|
* subprocess. The stream obtains data piped from the error output
|
||||||
* output stream of the process represented by this {@code Process}
|
* of the process represented by this {@code Process} object.
|
||||||
* object.
|
*
|
||||||
|
* <p>If the standard error of the subprocess has been redirected using
|
||||||
|
* {@link ProcessBuilder#redirectError(Redirect)
|
||||||
|
* ProcessBuilder.redirectError} or
|
||||||
|
* {@link ProcessBuilder#redirectErrorStream(boolean)
|
||||||
|
* ProcessBuilder.redirectErrorStream}
|
||||||
|
* then this method will return a
|
||||||
|
* <a href="ProcessBuilder.html#redirect-output">null input stream</a>.
|
||||||
*
|
*
|
||||||
* <p>Implementation note: It is a good idea for the returned
|
* <p>Implementation note: It is a good idea for the returned
|
||||||
* input stream to be buffered.
|
* input stream to be buffered.
|
||||||
*
|
*
|
||||||
* @return the input stream connected to the error output stream of
|
* @return the input stream connected to the error output of
|
||||||
* the subprocess
|
* the subprocess
|
||||||
* @see ProcessBuilder#redirectErrorStream()
|
|
||||||
*/
|
*/
|
||||||
abstract public InputStream getErrorStream();
|
abstract public InputStream getErrorStream();
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2003-2006 Sun Microsystems, Inc. All Rights Reserved.
|
* Copyright 2003-2008 Sun Microsystems, Inc. All Rights Reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -27,6 +27,10 @@ package java.lang;
|
|||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -34,7 +38,7 @@ import java.util.Map;
|
|||||||
/**
|
/**
|
||||||
* This class is used to create operating system processes.
|
* This class is used to create operating system processes.
|
||||||
*
|
*
|
||||||
* <p>Each <code>ProcessBuilder</code> instance manages a collection
|
* <p>Each {@code ProcessBuilder} instance manages a collection
|
||||||
* of process attributes. The {@link #start()} method creates a new
|
* of process attributes. The {@link #start()} method creates a new
|
||||||
* {@link Process} instance with those attributes. The {@link
|
* {@link Process} instance with those attributes. The {@link
|
||||||
* #start()} method can be invoked repeatedly from the same instance
|
* #start()} method can be invoked repeatedly from the same instance
|
||||||
@ -59,19 +63,64 @@ import java.util.Map;
|
|||||||
*
|
*
|
||||||
* <li>a <i>working directory</i>. The default value is the current
|
* <li>a <i>working directory</i>. The default value is the current
|
||||||
* working directory of the current process, usually the directory
|
* working directory of the current process, usually the directory
|
||||||
* named by the system property <code>user.dir</code>.
|
* named by the system property {@code user.dir}.
|
||||||
|
*
|
||||||
|
* <li><a name="redirect-input">a source of <i>standard input</i>.
|
||||||
|
* By default, the subprocess reads input from a pipe. Java code
|
||||||
|
* can access this pipe via the output stream returned by
|
||||||
|
* {@link Process#getOutputStream()}. However, standard input may
|
||||||
|
* be redirected to another source using
|
||||||
|
* {@link #redirectInput(Redirect) redirectInput}.
|
||||||
|
* In this case, {@link Process#getOutputStream()} will return a
|
||||||
|
* <i>null output stream</i>, for which:
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>the {@link OutputStream#write(int) write} methods always
|
||||||
|
* throw {@code IOException}
|
||||||
|
* <li>the {@link OutputStream#close() close} method does nothing
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <li><a name="redirect-output">a destination for <i>standard output</i>
|
||||||
|
* and <i>standard error</i>. By default, the subprocess writes standard
|
||||||
|
* output and standard error to pipes. Java code can access these pipes
|
||||||
|
* via the input streams returned by {@link Process#getInputStream()} and
|
||||||
|
* {@link Process#getErrorStream()}. However, standard output and
|
||||||
|
* standard error may be redirected to other destinations using
|
||||||
|
* {@link #redirectOutput(Redirect) redirectOutput} and
|
||||||
|
* {@link #redirectError(Redirect) redirectError}.
|
||||||
|
* In this case, {@link Process#getInputStream()} and/or
|
||||||
|
* {@link Process#getErrorStream()} will return a <i>null input
|
||||||
|
* stream</i>, for which:
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>the {@link InputStream#read() read} methods always return
|
||||||
|
* {@code -1}
|
||||||
|
* <li>the {@link InputStream#available() available} method always returns
|
||||||
|
* {@code 0}
|
||||||
|
* <li>the {@link InputStream#close() close} method does nothing
|
||||||
|
* </ul>
|
||||||
*
|
*
|
||||||
* <li>a <i>redirectErrorStream</i> property. Initially, this property
|
* <li>a <i>redirectErrorStream</i> property. Initially, this property
|
||||||
* is <code>false</code>, meaning that the standard output and error
|
* is {@code false}, meaning that the standard output and error
|
||||||
* output of a subprocess are sent to two separate streams, which can
|
* output of a subprocess are sent to two separate streams, which can
|
||||||
* be accessed using the {@link Process#getInputStream()} and {@link
|
* be accessed using the {@link Process#getInputStream()} and {@link
|
||||||
* Process#getErrorStream()} methods. If the value is set to
|
* Process#getErrorStream()} methods.
|
||||||
* <code>true</code>, the standard error is merged with the standard
|
*
|
||||||
* output. This makes it easier to correlate error messages with the
|
* <p>If the value is set to {@code true}, then:
|
||||||
* corresponding output. In this case, the merged data can be read
|
*
|
||||||
* from the stream returned by {@link Process#getInputStream()}, while
|
* <ul>
|
||||||
* reading from the stream returned by {@link
|
* <li>standard error is merged with the standard output and always sent
|
||||||
* Process#getErrorStream()} will get an immediate end of file.
|
* to the same destination (this makes it easier to correlate error
|
||||||
|
* messages with the corresponding output)
|
||||||
|
* <li>the common destination of standard error and standard output can be
|
||||||
|
* redirected using
|
||||||
|
* {@link #redirectOutput(Redirect) redirectOutput}
|
||||||
|
* <li>any redirection set by the
|
||||||
|
* {@link #redirectError(Redirect) redirectError}
|
||||||
|
* method is ignored when creating a subprocess
|
||||||
|
* <li>the stream returned from {@link Process#getErrorStream()} will
|
||||||
|
* always be a <a href="#redirect-output">null input stream</a>
|
||||||
|
* </ul>
|
||||||
*
|
*
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
@ -87,34 +136,43 @@ import java.util.Map;
|
|||||||
* is invoked.
|
* is invoked.
|
||||||
*
|
*
|
||||||
* <p><strong>Note that this class is not synchronized.</strong>
|
* <p><strong>Note that this class is not synchronized.</strong>
|
||||||
* If multiple threads access a <code>ProcessBuilder</code> instance
|
* If multiple threads access a {@code ProcessBuilder} instance
|
||||||
* concurrently, and at least one of the threads modifies one of the
|
* concurrently, and at least one of the threads modifies one of the
|
||||||
* attributes structurally, it <i>must</i> be synchronized externally.
|
* attributes structurally, it <i>must</i> be synchronized externally.
|
||||||
*
|
*
|
||||||
* <p>Starting a new process which uses the default working directory
|
* <p>Starting a new process which uses the default working directory
|
||||||
* and environment is easy:
|
* and environment is easy:
|
||||||
*
|
*
|
||||||
* <blockquote><pre>
|
* <pre> {@code
|
||||||
* Process p = new ProcessBuilder("myCommand", "myArg").start();
|
* Process p = new ProcessBuilder("myCommand", "myArg").start();
|
||||||
* </pre></blockquote>
|
* }</pre>
|
||||||
*
|
*
|
||||||
* <p>Here is an example that starts a process with a modified working
|
* <p>Here is an example that starts a process with a modified working
|
||||||
* directory and environment:
|
* directory and environment, and redirects standard output and error
|
||||||
|
* to be appended to a log file:
|
||||||
*
|
*
|
||||||
* <blockquote><pre>
|
* <pre> {@code
|
||||||
* ProcessBuilder pb = new ProcessBuilder("myCommand", "myArg1", "myArg2");
|
* ProcessBuilder pb =
|
||||||
* Map<String, String> env = pb.environment();
|
* new ProcessBuilder("myCommand", "myArg1", "myArg2");
|
||||||
|
* Map<String, String> env = pb.environment();
|
||||||
* env.put("VAR1", "myValue");
|
* env.put("VAR1", "myValue");
|
||||||
* env.remove("OTHERVAR");
|
* env.remove("OTHERVAR");
|
||||||
* env.put("VAR2", env.get("VAR1") + "suffix");
|
* env.put("VAR2", env.get("VAR1") + "suffix");
|
||||||
* pb.directory(new File("myDir"));
|
* pb.directory(new File("myDir"));
|
||||||
|
* File log = new File("log");
|
||||||
|
* pb.redirectErrorStream(true);
|
||||||
|
* pb.redirectOutput(Redirect.appendTo(log));
|
||||||
* Process p = pb.start();
|
* Process p = pb.start();
|
||||||
* </pre></blockquote>
|
* assert pb.redirectInput() == Redirect.PIPE;
|
||||||
|
* assert pb.redirectOutput().file() == log;
|
||||||
|
* assert p.getInputStream().read() == -1;
|
||||||
|
* }</pre>
|
||||||
*
|
*
|
||||||
* <p>To start a process with an explicit set of environment
|
* <p>To start a process with an explicit set of environment
|
||||||
* variables, first call {@link java.util.Map#clear() Map.clear()}
|
* variables, first call {@link java.util.Map#clear() Map.clear()}
|
||||||
* before adding environment variables.
|
* before adding environment variables.
|
||||||
*
|
*
|
||||||
|
* @author Martin Buchholz
|
||||||
* @since 1.5
|
* @since 1.5
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -124,20 +182,19 @@ public final class ProcessBuilder
|
|||||||
private File directory;
|
private File directory;
|
||||||
private Map<String,String> environment;
|
private Map<String,String> environment;
|
||||||
private boolean redirectErrorStream;
|
private boolean redirectErrorStream;
|
||||||
|
private Redirect[] redirects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a process builder with the specified operating
|
* Constructs a process builder with the specified operating
|
||||||
* system program and arguments. This constructor does <i>not</i>
|
* system program and arguments. This constructor does <i>not</i>
|
||||||
* make a copy of the <code>command</code> list. Subsequent
|
* make a copy of the {@code command} list. Subsequent
|
||||||
* updates to the list will be reflected in the state of the
|
* updates to the list will be reflected in the state of the
|
||||||
* process builder. It is not checked whether
|
* process builder. It is not checked whether
|
||||||
* <code>command</code> corresponds to a valid operating system
|
* {@code command} corresponds to a valid operating system
|
||||||
* command.</p>
|
* command.
|
||||||
*
|
*
|
||||||
* @param command The list containing the program and its arguments
|
* @param command the list containing the program and its arguments
|
||||||
*
|
* @throws NullPointerException if the argument is null
|
||||||
* @throws NullPointerException
|
|
||||||
* If the argument is <code>null</code>
|
|
||||||
*/
|
*/
|
||||||
public ProcessBuilder(List<String> command) {
|
public ProcessBuilder(List<String> command) {
|
||||||
if (command == null)
|
if (command == null)
|
||||||
@ -149,12 +206,12 @@ public final class ProcessBuilder
|
|||||||
* Constructs a process builder with the specified operating
|
* Constructs a process builder with the specified operating
|
||||||
* system program and arguments. This is a convenience
|
* system program and arguments. This is a convenience
|
||||||
* constructor that sets the process builder's command to a string
|
* constructor that sets the process builder's command to a string
|
||||||
* list containing the same strings as the <code>command</code>
|
* list containing the same strings as the {@code command}
|
||||||
* array, in the same order. It is not checked whether
|
* array, in the same order. It is not checked whether
|
||||||
* <code>command</code> corresponds to a valid operating system
|
* {@code command} corresponds to a valid operating system
|
||||||
* command.</p>
|
* command.
|
||||||
*
|
*
|
||||||
* @param command A string array containing the program and its arguments
|
* @param command a string array containing the program and its arguments
|
||||||
*/
|
*/
|
||||||
public ProcessBuilder(String... command) {
|
public ProcessBuilder(String... command) {
|
||||||
this.command = new ArrayList<String>(command.length);
|
this.command = new ArrayList<String>(command.length);
|
||||||
@ -165,16 +222,15 @@ public final class ProcessBuilder
|
|||||||
/**
|
/**
|
||||||
* Sets this process builder's operating system program and
|
* Sets this process builder's operating system program and
|
||||||
* arguments. This method does <i>not</i> make a copy of the
|
* arguments. This method does <i>not</i> make a copy of the
|
||||||
* <code>command</code> list. Subsequent updates to the list will
|
* {@code command} list. Subsequent updates to the list will
|
||||||
* be reflected in the state of the process builder. It is not
|
* be reflected in the state of the process builder. It is not
|
||||||
* checked whether <code>command</code> corresponds to a valid
|
* checked whether {@code command} corresponds to a valid
|
||||||
* operating system command.</p>
|
* operating system command.
|
||||||
*
|
*
|
||||||
* @param command The list containing the program and its arguments
|
* @param command the list containing the program and its arguments
|
||||||
* @return This process builder
|
* @return this process builder
|
||||||
*
|
*
|
||||||
* @throws NullPointerException
|
* @throws NullPointerException if the argument is null
|
||||||
* If the argument is <code>null</code>
|
|
||||||
*/
|
*/
|
||||||
public ProcessBuilder command(List<String> command) {
|
public ProcessBuilder command(List<String> command) {
|
||||||
if (command == null)
|
if (command == null)
|
||||||
@ -187,12 +243,12 @@ public final class ProcessBuilder
|
|||||||
* Sets this process builder's operating system program and
|
* Sets this process builder's operating system program and
|
||||||
* arguments. This is a convenience method that sets the command
|
* arguments. This is a convenience method that sets the command
|
||||||
* to a string list containing the same strings as the
|
* to a string list containing the same strings as the
|
||||||
* <code>command</code> array, in the same order. It is not
|
* {@code command} array, in the same order. It is not
|
||||||
* checked whether <code>command</code> corresponds to a valid
|
* checked whether {@code command} corresponds to a valid
|
||||||
* operating system command.</p>
|
* operating system command.
|
||||||
*
|
*
|
||||||
* @param command A string array containing the program and its arguments
|
* @param command a string array containing the program and its arguments
|
||||||
* @return This process builder
|
* @return this process builder
|
||||||
*/
|
*/
|
||||||
public ProcessBuilder command(String... command) {
|
public ProcessBuilder command(String... command) {
|
||||||
this.command = new ArrayList<String>(command.length);
|
this.command = new ArrayList<String>(command.length);
|
||||||
@ -205,9 +261,9 @@ public final class ProcessBuilder
|
|||||||
* Returns this process builder's operating system program and
|
* Returns this process builder's operating system program and
|
||||||
* arguments. The returned list is <i>not</i> a copy. Subsequent
|
* arguments. The returned list is <i>not</i> a copy. Subsequent
|
||||||
* updates to the list will be reflected in the state of this
|
* updates to the list will be reflected in the state of this
|
||||||
* process builder.</p>
|
* process builder.
|
||||||
*
|
*
|
||||||
* @return This process builder's program and its arguments
|
* @return this process builder's program and its arguments
|
||||||
*/
|
*/
|
||||||
public List<String> command() {
|
public List<String> command() {
|
||||||
return command;
|
return command;
|
||||||
@ -225,10 +281,10 @@ public final class ProcessBuilder
|
|||||||
* <p>The returned object may be modified using ordinary {@link
|
* <p>The returned object may be modified using ordinary {@link
|
||||||
* java.util.Map Map} operations. These modifications will be
|
* java.util.Map Map} operations. These modifications will be
|
||||||
* visible to subprocesses started via the {@link #start()}
|
* visible to subprocesses started via the {@link #start()}
|
||||||
* method. Two <code>ProcessBuilder</code> instances always
|
* method. Two {@code ProcessBuilder} instances always
|
||||||
* contain independent process environments, so changes to the
|
* contain independent process environments, so changes to the
|
||||||
* returned map will never be reflected in any other
|
* returned map will never be reflected in any other
|
||||||
* <code>ProcessBuilder</code> instance or the values returned by
|
* {@code ProcessBuilder} instance or the values returned by
|
||||||
* {@link System#getenv System.getenv}.
|
* {@link System#getenv System.getenv}.
|
||||||
*
|
*
|
||||||
* <p>If the system does not support environment variables, an
|
* <p>If the system does not support environment variables, an
|
||||||
@ -262,20 +318,19 @@ public final class ProcessBuilder
|
|||||||
* <p>The returned map is typically case-sensitive on all platforms.
|
* <p>The returned map is typically case-sensitive on all platforms.
|
||||||
*
|
*
|
||||||
* <p>If a security manager exists, its
|
* <p>If a security manager exists, its
|
||||||
* {@link SecurityManager#checkPermission checkPermission}
|
* {@link SecurityManager#checkPermission checkPermission} method
|
||||||
* method is called with a
|
* is called with a
|
||||||
* <code>{@link RuntimePermission}("getenv.*")</code>
|
* {@link RuntimePermission}{@code ("getenv.*")} permission.
|
||||||
* permission. This may result in a {@link SecurityException} being
|
* This may result in a {@link SecurityException} being thrown.
|
||||||
* thrown.
|
|
||||||
*
|
*
|
||||||
* <p>When passing information to a Java subprocess,
|
* <p>When passing information to a Java subprocess,
|
||||||
* <a href=System.html#EnvironmentVSSystemProperties>system properties</a>
|
* <a href=System.html#EnvironmentVSSystemProperties>system properties</a>
|
||||||
* are generally preferred over environment variables.</p>
|
* are generally preferred over environment variables.
|
||||||
*
|
*
|
||||||
* @return This process builder's environment
|
* @return this process builder's environment
|
||||||
*
|
*
|
||||||
* @throws SecurityException
|
* @throws SecurityException
|
||||||
* If a security manager exists and its
|
* if a security manager exists and its
|
||||||
* {@link SecurityManager#checkPermission checkPermission}
|
* {@link SecurityManager#checkPermission checkPermission}
|
||||||
* method doesn't allow access to the process environment
|
* method doesn't allow access to the process environment
|
||||||
*
|
*
|
||||||
@ -328,12 +383,12 @@ public final class ProcessBuilder
|
|||||||
*
|
*
|
||||||
* Subprocesses subsequently started by this object's {@link
|
* Subprocesses subsequently started by this object's {@link
|
||||||
* #start()} method will use this as their working directory.
|
* #start()} method will use this as their working directory.
|
||||||
* The returned value may be <code>null</code> -- this means to use
|
* The returned value may be {@code null} -- this means to use
|
||||||
* the working directory of the current Java process, usually the
|
* the working directory of the current Java process, usually the
|
||||||
* directory named by the system property <code>user.dir</code>,
|
* directory named by the system property {@code user.dir},
|
||||||
* as the working directory of the child process.</p>
|
* as the working directory of the child process.
|
||||||
*
|
*
|
||||||
* @return This process builder's working directory
|
* @return this process builder's working directory
|
||||||
*/
|
*/
|
||||||
public File directory() {
|
public File directory() {
|
||||||
return directory;
|
return directory;
|
||||||
@ -344,50 +399,522 @@ public final class ProcessBuilder
|
|||||||
*
|
*
|
||||||
* Subprocesses subsequently started by this object's {@link
|
* Subprocesses subsequently started by this object's {@link
|
||||||
* #start()} method will use this as their working directory.
|
* #start()} method will use this as their working directory.
|
||||||
* The argument may be <code>null</code> -- this means to use the
|
* The argument may be {@code null} -- this means to use the
|
||||||
* working directory of the current Java process, usually the
|
* working directory of the current Java process, usually the
|
||||||
* directory named by the system property <code>user.dir</code>,
|
* directory named by the system property {@code user.dir},
|
||||||
* as the working directory of the child process.</p>
|
* as the working directory of the child process.
|
||||||
*
|
*
|
||||||
* @param directory The new working directory
|
* @param directory the new working directory
|
||||||
* @return This process builder
|
* @return this process builder
|
||||||
*/
|
*/
|
||||||
public ProcessBuilder directory(File directory) {
|
public ProcessBuilder directory(File directory) {
|
||||||
this.directory = directory;
|
this.directory = directory;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------- I/O Redirection ----------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements a <a href="#redirect-output">null input stream</a>.
|
||||||
|
*/
|
||||||
|
static class NullInputStream extends InputStream {
|
||||||
|
public int read() { return -1; }
|
||||||
|
public int available() { return 0; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements a <a href="#redirect-input">null output stream</a>.
|
||||||
|
*/
|
||||||
|
static class NullOutputStream extends OutputStream {
|
||||||
|
public void write(int b) throws IOException {
|
||||||
|
throw new IOException("Stream closed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a source of subprocess input or a destination of
|
||||||
|
* subprocess output.
|
||||||
|
*
|
||||||
|
* Each {@code Redirect} instance is one of the following:
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>the special value {@link #PIPE Redirect.PIPE}
|
||||||
|
* <li>the special value {@link #INHERIT Redirect.INHERIT}
|
||||||
|
* <li>a redirection to read from a file, created by an invocation of
|
||||||
|
* {@link Redirect#from Redirect.from(File)}
|
||||||
|
* <li>a redirection to write to a file, created by an invocation of
|
||||||
|
* {@link Redirect#to Redirect.to(File)}
|
||||||
|
* <li>a redirection to append to a file, created by an invocation of
|
||||||
|
* {@link Redirect#appendTo Redirect.appendTo(File)}
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <p>Each of the above categories has an associated unique
|
||||||
|
* {@link Type Type}.
|
||||||
|
*
|
||||||
|
* @since 1.7
|
||||||
|
*/
|
||||||
|
public static abstract class Redirect {
|
||||||
|
/**
|
||||||
|
* The type of a {@link Redirect}.
|
||||||
|
*/
|
||||||
|
public enum Type {
|
||||||
|
/**
|
||||||
|
* The type of {@link Redirect#PIPE Redirect.PIPE}.
|
||||||
|
*/
|
||||||
|
PIPE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type of {@link Redirect#INHERIT Redirect.INHERIT}.
|
||||||
|
*/
|
||||||
|
INHERIT,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type of redirects returned from
|
||||||
|
* {@link Redirect#from Redirect.from(File)}.
|
||||||
|
*/
|
||||||
|
READ,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type of redirects returned from
|
||||||
|
* {@link Redirect#to Redirect.to(File)}.
|
||||||
|
*/
|
||||||
|
WRITE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The type of redirects returned from
|
||||||
|
* {@link Redirect#appendTo Redirect.appendTo(File)}.
|
||||||
|
*/
|
||||||
|
APPEND
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the type of this {@code Redirect}.
|
||||||
|
* @return the type of this {@code Redirect}
|
||||||
|
*/
|
||||||
|
public abstract Type type();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates that subprocess I/O will be connected to the
|
||||||
|
* current Java process over a pipe.
|
||||||
|
*
|
||||||
|
* This is the default handling of subprocess standard I/O.
|
||||||
|
*
|
||||||
|
* <p>It will always be true that
|
||||||
|
* <pre> {@code
|
||||||
|
* Redirect.PIPE.file() == null &&
|
||||||
|
* Redirect.PIPE.type() == Redirect.Type.PIPE
|
||||||
|
* }</pre>
|
||||||
|
*/
|
||||||
|
public static final Redirect PIPE = new Redirect() {
|
||||||
|
public Type type() { return Type.PIPE; }
|
||||||
|
public String toString() { return type().toString(); }};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates that subprocess I/O source or destination will be the
|
||||||
|
* same as those of the current process. This is the normal
|
||||||
|
* behavior of most operating system command interpreters (shells).
|
||||||
|
*
|
||||||
|
* <p>It will always be true that
|
||||||
|
* <pre> {@code
|
||||||
|
* Redirect.INHERIT.file() == null &&
|
||||||
|
* Redirect.INHERIT.type() == Redirect.Type.INHERIT
|
||||||
|
* }</pre>
|
||||||
|
*/
|
||||||
|
public static final Redirect INHERIT = new Redirect() {
|
||||||
|
public Type type() { return Type.INHERIT; }
|
||||||
|
public String toString() { return type().toString(); }};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the {@link File} source or destination associated
|
||||||
|
* with this redirect, or {@code null} if there is no such file.
|
||||||
|
*
|
||||||
|
* @return the file associated with this redirect,
|
||||||
|
* or {@code null} if there is no such file
|
||||||
|
*/
|
||||||
|
public File file() { return null; }
|
||||||
|
|
||||||
|
FileOutputStream toFileOutputStream() throws IOException {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a redirect to read from the specified file.
|
||||||
|
*
|
||||||
|
* <p>It will always be true that
|
||||||
|
* <pre> {@code
|
||||||
|
* Redirect.from(file).file() == file &&
|
||||||
|
* Redirect.from(file).type() == Redirect.Type.READ
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* @throws NullPointerException if the specified file is null
|
||||||
|
* @return a redirect to read from the specified file
|
||||||
|
*/
|
||||||
|
public static Redirect from(final File file) {
|
||||||
|
if (file == null)
|
||||||
|
throw new NullPointerException();
|
||||||
|
return new Redirect() {
|
||||||
|
public Type type() { return Type.READ; }
|
||||||
|
public File file() { return file; }
|
||||||
|
public String toString() {
|
||||||
|
return "redirect to read from file \"" + file + "\"";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a redirect to write to the specified file.
|
||||||
|
* If the specified file exists when the subprocess is started,
|
||||||
|
* its previous contents will be discarded.
|
||||||
|
*
|
||||||
|
* <p>It will always be true that
|
||||||
|
* <pre> {@code
|
||||||
|
* Redirect.to(file).file() == file &&
|
||||||
|
* Redirect.to(file).type() == Redirect.Type.WRITE
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* @throws NullPointerException if the specified file is null
|
||||||
|
* @return a redirect to write to the specified file
|
||||||
|
*/
|
||||||
|
public static Redirect to(final File file) {
|
||||||
|
if (file == null)
|
||||||
|
throw new NullPointerException();
|
||||||
|
return new Redirect() {
|
||||||
|
public Type type() { return Type.WRITE; }
|
||||||
|
public File file() { return file; }
|
||||||
|
public String toString() {
|
||||||
|
return "redirect to write to file \"" + file + "\"";
|
||||||
|
}
|
||||||
|
FileOutputStream toFileOutputStream() throws IOException {
|
||||||
|
return new FileOutputStream(file, false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a redirect to append to the specified file.
|
||||||
|
* Each write operation first advances the position to the
|
||||||
|
* end of the file and then writes the requested data.
|
||||||
|
* Whether the advancement of the position and the writing
|
||||||
|
* of the data are done in a single atomic operation is
|
||||||
|
* system-dependent and therefore unspecified.
|
||||||
|
*
|
||||||
|
* <p>It will always be true that
|
||||||
|
* <pre> {@code
|
||||||
|
* Redirect.appendTo(file).file() == file &&
|
||||||
|
* Redirect.appendTo(file).type() == Redirect.Type.APPEND
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* @throws NullPointerException if the specified file is null
|
||||||
|
* @return a redirect to append to the specified file
|
||||||
|
*/
|
||||||
|
public static Redirect appendTo(final File file) {
|
||||||
|
if (file == null)
|
||||||
|
throw new NullPointerException();
|
||||||
|
return new Redirect() {
|
||||||
|
public Type type() { return Type.APPEND; }
|
||||||
|
public File file() { return file; }
|
||||||
|
public String toString() {
|
||||||
|
return "redirect to append to file \"" + file + "\"";
|
||||||
|
}
|
||||||
|
FileOutputStream toFileOutputStream() throws IOException {
|
||||||
|
return new FileOutputStream(file, true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compares the specified object with this {@code Redirect} for
|
||||||
|
* equality. Returns {@code true} if and only if the two
|
||||||
|
* objects are identical or both objects are {@code Redirect}
|
||||||
|
* instances of the same type associated with non-null equal
|
||||||
|
* {@code File} instances.
|
||||||
|
*/
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (obj == this)
|
||||||
|
return true;
|
||||||
|
if (! (obj instanceof Redirect))
|
||||||
|
return false;
|
||||||
|
Redirect r = (Redirect) obj;
|
||||||
|
if (r.type() != this.type())
|
||||||
|
return false;
|
||||||
|
assert this.file() != null;
|
||||||
|
return this.file().equals(r.file());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a hash code value for this {@code Redirect}.
|
||||||
|
* @return a hash code value for this {@code Redirect}
|
||||||
|
*/
|
||||||
|
public int hashCode() {
|
||||||
|
File file = file();
|
||||||
|
if (file == null)
|
||||||
|
return super.hashCode();
|
||||||
|
else
|
||||||
|
return file.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* No public constructors. Clients must use predefined
|
||||||
|
* static {@code Redirect} instances or factory methods.
|
||||||
|
*/
|
||||||
|
private Redirect() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Redirect[] redirects() {
|
||||||
|
if (redirects == null)
|
||||||
|
redirects = new Redirect[] {
|
||||||
|
Redirect.PIPE, Redirect.PIPE, Redirect.PIPE
|
||||||
|
};
|
||||||
|
return redirects;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets this process builder's standard input source.
|
||||||
|
*
|
||||||
|
* Subprocesses subsequently started by this object's {@link #start()}
|
||||||
|
* method obtain their standard input from this source.
|
||||||
|
*
|
||||||
|
* <p>If the source is {@link Redirect#PIPE Redirect.PIPE}
|
||||||
|
* (the initial value), then the standard input of a
|
||||||
|
* subprocess can be written to using the output stream
|
||||||
|
* returned by {@link Process#getOutputStream()}.
|
||||||
|
* If the source is set to any other value, then
|
||||||
|
* {@link Process#getOutputStream()} will return a
|
||||||
|
* <a href="#redirect-input">null output stream</a>.
|
||||||
|
*
|
||||||
|
* @param source the new standard input source
|
||||||
|
* @return this process builder
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if the redirect does not correspond to a valid source
|
||||||
|
* of data, that is, has type
|
||||||
|
* {@link Redirect.Type#WRITE WRITE} or
|
||||||
|
* {@link Redirect.Type#APPEND APPEND}
|
||||||
|
* @since 1.7
|
||||||
|
*/
|
||||||
|
public ProcessBuilder redirectInput(Redirect source) {
|
||||||
|
if (source.type() == Redirect.Type.WRITE ||
|
||||||
|
source.type() == Redirect.Type.APPEND)
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Redirect invalid for reading: " + source);
|
||||||
|
redirects()[0] = source;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets this process builder's standard output destination.
|
||||||
|
*
|
||||||
|
* Subprocesses subsequently started by this object's {@link #start()}
|
||||||
|
* method send their standard output to this destination.
|
||||||
|
*
|
||||||
|
* <p>If the destination is {@link Redirect#PIPE Redirect.PIPE}
|
||||||
|
* (the initial value), then the standard output of a subprocess
|
||||||
|
* can be read using the input stream returned by {@link
|
||||||
|
* Process#getInputStream()}.
|
||||||
|
* If the destination is set to any other value, then
|
||||||
|
* {@link Process#getInputStream()} will return a
|
||||||
|
* <a href="#redirect-output">null input stream</a>.
|
||||||
|
*
|
||||||
|
* @param destination the new standard output destination
|
||||||
|
* @return this process builder
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if the redirect does not correspond to a valid
|
||||||
|
* destination of data, that is, has type
|
||||||
|
* {@link Redirect.Type#READ READ}
|
||||||
|
* @since 1.7
|
||||||
|
*/
|
||||||
|
public ProcessBuilder redirectOutput(Redirect destination) {
|
||||||
|
if (destination.type() == Redirect.Type.READ)
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Redirect invalid for writing: " + destination);
|
||||||
|
redirects()[1] = destination;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets this process builder's standard error destination.
|
||||||
|
*
|
||||||
|
* Subprocesses subsequently started by this object's {@link #start()}
|
||||||
|
* method send their standard error to this destination.
|
||||||
|
*
|
||||||
|
* <p>If the destination is {@link Redirect#PIPE Redirect.PIPE}
|
||||||
|
* (the initial value), then the error output of a subprocess
|
||||||
|
* can be read using the input stream returned by {@link
|
||||||
|
* Process#getErrorStream()}.
|
||||||
|
* If the destination is set to any other value, then
|
||||||
|
* {@link Process#getErrorStream()} will return a
|
||||||
|
* <a href="#redirect-output">null input stream</a>.
|
||||||
|
*
|
||||||
|
* <p>If the {@link #redirectErrorStream redirectErrorStream}
|
||||||
|
* attribute has been set {@code true}, then the redirection set
|
||||||
|
* by this method has no effect.
|
||||||
|
*
|
||||||
|
* @param destination the new standard error destination
|
||||||
|
* @return this process builder
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* if the redirect does not correspond to a valid
|
||||||
|
* destination of data, that is, has type
|
||||||
|
* {@link Redirect.Type#READ READ}
|
||||||
|
* @since 1.7
|
||||||
|
*/
|
||||||
|
public ProcessBuilder redirectError(Redirect destination) {
|
||||||
|
if (destination.type() == Redirect.Type.READ)
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Redirect invalid for writing: " + destination);
|
||||||
|
redirects()[2] = destination;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets this process builder's standard input source to a file.
|
||||||
|
*
|
||||||
|
* <p>This is a convenience method. An invocation of the form
|
||||||
|
* {@code redirectInput(file)}
|
||||||
|
* behaves in exactly the same way as the invocation
|
||||||
|
* {@link #redirectInput(Redirect) redirectInput}
|
||||||
|
* {@code (Redirect.from(file))}.
|
||||||
|
*
|
||||||
|
* @param file the new standard input source
|
||||||
|
* @return this process builder
|
||||||
|
* @since 1.7
|
||||||
|
*/
|
||||||
|
public ProcessBuilder redirectInput(File file) {
|
||||||
|
return redirectInput(Redirect.from(file));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets this process builder's standard output destination to a file.
|
||||||
|
*
|
||||||
|
* <p>This is a convenience method. An invocation of the form
|
||||||
|
* {@code redirectOutput(file)}
|
||||||
|
* behaves in exactly the same way as the invocation
|
||||||
|
* {@link #redirectOutput(Redirect) redirectOutput}
|
||||||
|
* {@code (Redirect.to(file))}.
|
||||||
|
*
|
||||||
|
* @param file the new standard output destination
|
||||||
|
* @return this process builder
|
||||||
|
* @since 1.7
|
||||||
|
*/
|
||||||
|
public ProcessBuilder redirectOutput(File file) {
|
||||||
|
return redirectOutput(Redirect.to(file));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets this process builder's standard error destination to a file.
|
||||||
|
*
|
||||||
|
* <p>This is a convenience method. An invocation of the form
|
||||||
|
* {@code redirectError(file)}
|
||||||
|
* behaves in exactly the same way as the invocation
|
||||||
|
* {@link #redirectError(Redirect) redirectError}
|
||||||
|
* {@code (Redirect.to(file))}.
|
||||||
|
*
|
||||||
|
* @param file the new standard error destination
|
||||||
|
* @return this process builder
|
||||||
|
* @since 1.7
|
||||||
|
*/
|
||||||
|
public ProcessBuilder redirectError(File file) {
|
||||||
|
return redirectError(Redirect.to(file));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns this process builder's standard input source.
|
||||||
|
*
|
||||||
|
* Subprocesses subsequently started by this object's {@link #start()}
|
||||||
|
* method obtain their standard input from this source.
|
||||||
|
* The initial value is {@link Redirect#PIPE Redirect.PIPE}.
|
||||||
|
*
|
||||||
|
* @return this process builder's standard input source
|
||||||
|
* @since 1.7
|
||||||
|
*/
|
||||||
|
public Redirect redirectInput() {
|
||||||
|
return (redirects == null) ? Redirect.PIPE : redirects[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns this process builder's standard output destination.
|
||||||
|
*
|
||||||
|
* Subprocesses subsequently started by this object's {@link #start()}
|
||||||
|
* method redirect their standard output to this destination.
|
||||||
|
* The initial value is {@link Redirect#PIPE Redirect.PIPE}.
|
||||||
|
*
|
||||||
|
* @return this process builder's standard output destination
|
||||||
|
* @since 1.7
|
||||||
|
*/
|
||||||
|
public Redirect redirectOutput() {
|
||||||
|
return (redirects == null) ? Redirect.PIPE : redirects[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns this process builder's standard error destination.
|
||||||
|
*
|
||||||
|
* Subprocesses subsequently started by this object's {@link #start()}
|
||||||
|
* method redirect their standard error to this destination.
|
||||||
|
* The initial value is {@link Redirect#PIPE Redirect.PIPE}.
|
||||||
|
*
|
||||||
|
* @return this process builder's standard error destination
|
||||||
|
* @since 1.7
|
||||||
|
*/
|
||||||
|
public Redirect redirectError() {
|
||||||
|
return (redirects == null) ? Redirect.PIPE : redirects[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the source and destination for subprocess standard I/O
|
||||||
|
* to be the same as those of the current Java process.
|
||||||
|
*
|
||||||
|
* <p>This is a convenience method. An invocation of the form
|
||||||
|
* <pre> {@code
|
||||||
|
* pb.inheritIO()
|
||||||
|
* }</pre>
|
||||||
|
* behaves in exactly the same way as the invocation
|
||||||
|
* <pre> {@code
|
||||||
|
* pb.redirectInput(Redirect.INHERIT)
|
||||||
|
* .redirectOutput(Redirect.INHERIT)
|
||||||
|
* .redirectError(Redirect.INHERIT)
|
||||||
|
* }</pre>
|
||||||
|
*
|
||||||
|
* This gives behavior equivalent to most operating system
|
||||||
|
* command interpreters, or the standard C library function
|
||||||
|
* {@code system()}.
|
||||||
|
*
|
||||||
|
* @return this process builder
|
||||||
|
* @since 1.7
|
||||||
|
*/
|
||||||
|
public ProcessBuilder inheritIO() {
|
||||||
|
Arrays.fill(redirects(), Redirect.INHERIT);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tells whether this process builder merges standard error and
|
* Tells whether this process builder merges standard error and
|
||||||
* standard output.
|
* standard output.
|
||||||
*
|
*
|
||||||
* <p>If this property is <code>true</code>, then any error output
|
* <p>If this property is {@code true}, then any error output
|
||||||
* generated by subprocesses subsequently started by this object's
|
* generated by subprocesses subsequently started by this object's
|
||||||
* {@link #start()} method will be merged with the standard
|
* {@link #start()} method will be merged with the standard
|
||||||
* output, so that both can be read using the
|
* output, so that both can be read using the
|
||||||
* {@link Process#getInputStream()} method. This makes it easier
|
* {@link Process#getInputStream()} method. This makes it easier
|
||||||
* to correlate error messages with the corresponding output.
|
* to correlate error messages with the corresponding output.
|
||||||
* The initial value is <code>false</code>.</p>
|
* The initial value is {@code false}.
|
||||||
*
|
*
|
||||||
* @return This process builder's <code>redirectErrorStream</code> property
|
* @return this process builder's {@code redirectErrorStream} property
|
||||||
*/
|
*/
|
||||||
public boolean redirectErrorStream() {
|
public boolean redirectErrorStream() {
|
||||||
return redirectErrorStream;
|
return redirectErrorStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets this process builder's <code>redirectErrorStream</code> property.
|
* Sets this process builder's {@code redirectErrorStream} property.
|
||||||
*
|
*
|
||||||
* <p>If this property is <code>true</code>, then any error output
|
* <p>If this property is {@code true}, then any error output
|
||||||
* generated by subprocesses subsequently started by this object's
|
* generated by subprocesses subsequently started by this object's
|
||||||
* {@link #start()} method will be merged with the standard
|
* {@link #start()} method will be merged with the standard
|
||||||
* output, so that both can be read using the
|
* output, so that both can be read using the
|
||||||
* {@link Process#getInputStream()} method. This makes it easier
|
* {@link Process#getInputStream()} method. This makes it easier
|
||||||
* to correlate error messages with the corresponding output.
|
* to correlate error messages with the corresponding output.
|
||||||
* The initial value is <code>false</code>.</p>
|
* The initial value is {@code false}.
|
||||||
*
|
*
|
||||||
* @param redirectErrorStream The new property value
|
* @param redirectErrorStream the new property value
|
||||||
* @return This process builder
|
* @return this process builder
|
||||||
*/
|
*/
|
||||||
public ProcessBuilder redirectErrorStream(boolean redirectErrorStream) {
|
public ProcessBuilder redirectErrorStream(boolean redirectErrorStream) {
|
||||||
this.redirectErrorStream = redirectErrorStream;
|
this.redirectErrorStream = redirectErrorStream;
|
||||||
@ -410,7 +937,7 @@ public final class ProcessBuilder
|
|||||||
* <p>If there is a security manager, its
|
* <p>If there is a security manager, its
|
||||||
* {@link SecurityManager#checkExec checkExec}
|
* {@link SecurityManager#checkExec checkExec}
|
||||||
* method is called with the first component of this object's
|
* method is called with the first component of this object's
|
||||||
* <code>command</code> array as its argument. This may result in
|
* {@code command} array as its argument. This may result in
|
||||||
* a {@link SecurityException} being thrown.
|
* a {@link SecurityException} being thrown.
|
||||||
*
|
*
|
||||||
* <p>Starting an operating system process is highly system-dependent.
|
* <p>Starting an operating system process is highly system-dependent.
|
||||||
@ -426,26 +953,42 @@ public final class ProcessBuilder
|
|||||||
* subclass of {@link IOException}.
|
* subclass of {@link IOException}.
|
||||||
*
|
*
|
||||||
* <p>Subsequent modifications to this process builder will not
|
* <p>Subsequent modifications to this process builder will not
|
||||||
* affect the returned {@link Process}.</p>
|
* affect the returned {@link Process}.
|
||||||
*
|
*
|
||||||
* @return A new {@link Process} object for managing the subprocess
|
* @return a new {@link Process} object for managing the subprocess
|
||||||
*
|
*
|
||||||
* @throws NullPointerException
|
* @throws NullPointerException
|
||||||
* If an element of the command list is null
|
* if an element of the command list is null
|
||||||
*
|
*
|
||||||
* @throws IndexOutOfBoundsException
|
* @throws IndexOutOfBoundsException
|
||||||
* If the command is an empty list (has size <code>0</code>)
|
* if the command is an empty list (has size {@code 0})
|
||||||
*
|
*
|
||||||
* @throws SecurityException
|
* @throws SecurityException
|
||||||
* If a security manager exists and its
|
* if a security manager exists and
|
||||||
* {@link SecurityManager#checkExec checkExec}
|
* <ul>
|
||||||
* method doesn't allow creation of the subprocess
|
|
||||||
*
|
*
|
||||||
* @throws IOException
|
* <li>its
|
||||||
* If an I/O error occurs
|
* {@link SecurityManager#checkExec checkExec}
|
||||||
|
* method doesn't allow creation of the subprocess, or
|
||||||
|
*
|
||||||
|
* <li>the standard input to the subprocess was
|
||||||
|
* {@linkplain #redirectInput redirected from a file}
|
||||||
|
* and the security manager's
|
||||||
|
* {@link SecurityManager#checkRead checkRead} method
|
||||||
|
* denies read access to the file, or
|
||||||
|
*
|
||||||
|
* <li>the standard output or standard error of the
|
||||||
|
* subprocess was
|
||||||
|
* {@linkplain #redirectOutput redirected to a file}
|
||||||
|
* and the security manager's
|
||||||
|
* {@link SecurityManager#checkWrite checkWrite} method
|
||||||
|
* denies write access to the file
|
||||||
|
*
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @throws IOException if an I/O error occurs
|
||||||
*
|
*
|
||||||
* @see Runtime#exec(String[], String[], java.io.File)
|
* @see Runtime#exec(String[], String[], java.io.File)
|
||||||
* @see SecurityManager#checkExec(String)
|
|
||||||
*/
|
*/
|
||||||
public Process start() throws IOException {
|
public Process start() throws IOException {
|
||||||
// Must convert to array first -- a malicious user-supplied
|
// Must convert to array first -- a malicious user-supplied
|
||||||
@ -467,6 +1010,7 @@ public final class ProcessBuilder
|
|||||||
return ProcessImpl.start(cmdarray,
|
return ProcessImpl.start(cmdarray,
|
||||||
environment,
|
environment,
|
||||||
dir,
|
dir,
|
||||||
|
redirects,
|
||||||
redirectErrorStream);
|
redirectErrorStream);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
// It's much easier for us to create a high-quality error
|
// It's much easier for us to create a high-quality error
|
||||||
|
@ -33,4 +33,8 @@ import java.io.FileDescriptor;
|
|||||||
public interface JavaIOFileDescriptorAccess {
|
public interface JavaIOFileDescriptorAccess {
|
||||||
public void set(FileDescriptor obj, int fd);
|
public void set(FileDescriptor obj, int fd);
|
||||||
public int get(FileDescriptor fd);
|
public int get(FileDescriptor fd);
|
||||||
|
|
||||||
|
// Only valid on Windows
|
||||||
|
public void setHandle(FileDescriptor obj, long handle);
|
||||||
|
public long getHandle(FileDescriptor obj);
|
||||||
}
|
}
|
||||||
|
@ -52,39 +52,37 @@ public class FileChannelImpl
|
|||||||
{
|
{
|
||||||
|
|
||||||
// Used to make native read and write calls
|
// Used to make native read and write calls
|
||||||
private static NativeDispatcher nd;
|
private static final NativeDispatcher nd;
|
||||||
|
|
||||||
// Memory allocation size for mapping buffers
|
// Memory allocation size for mapping buffers
|
||||||
private static long allocationGranularity;
|
private static final long allocationGranularity;
|
||||||
|
|
||||||
// Cached field for MappedByteBuffer.isAMappedBuffer
|
// Cached field for MappedByteBuffer.isAMappedBuffer
|
||||||
private static Field isAMappedBufferField;
|
private static final Field isAMappedBufferField;
|
||||||
|
|
||||||
// File descriptor
|
// File descriptor
|
||||||
private FileDescriptor fd;
|
private final FileDescriptor fd;
|
||||||
|
|
||||||
// File access mode (immutable)
|
// File access mode (immutable)
|
||||||
private boolean writable;
|
private final boolean writable;
|
||||||
private boolean readable;
|
private final boolean readable;
|
||||||
private boolean appending;
|
|
||||||
|
|
||||||
// Required to prevent finalization of creating stream (immutable)
|
// Required to prevent finalization of creating stream (immutable)
|
||||||
private Object parent;
|
private final Object parent;
|
||||||
|
|
||||||
// Thread-safe set of IDs of native threads, for signalling
|
// Thread-safe set of IDs of native threads, for signalling
|
||||||
private NativeThreadSet threads = new NativeThreadSet(2);
|
private final NativeThreadSet threads = new NativeThreadSet(2);
|
||||||
|
|
||||||
// Lock for operations involving position and size
|
// Lock for operations involving position and size
|
||||||
private Object positionLock = new Object();
|
private final Object positionLock = new Object();
|
||||||
|
|
||||||
private FileChannelImpl(FileDescriptor fd, boolean readable,
|
private FileChannelImpl(FileDescriptor fd, boolean readable,
|
||||||
boolean writable, Object parent, boolean append)
|
boolean writable, Object parent)
|
||||||
{
|
{
|
||||||
this.fd = fd;
|
this.fd = fd;
|
||||||
this.readable = readable;
|
this.readable = readable;
|
||||||
this.writable = writable;
|
this.writable = writable;
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
this.appending = append;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Invoked by getChannel() methods
|
// Invoked by getChannel() methods
|
||||||
@ -94,14 +92,7 @@ public class FileChannelImpl
|
|||||||
boolean readable, boolean writable,
|
boolean readable, boolean writable,
|
||||||
Object parent)
|
Object parent)
|
||||||
{
|
{
|
||||||
return new FileChannelImpl(fd, readable, writable, parent, false);
|
return new FileChannelImpl(fd, readable, writable, parent);
|
||||||
}
|
|
||||||
|
|
||||||
public static FileChannel open(FileDescriptor fd,
|
|
||||||
boolean readable, boolean writable,
|
|
||||||
Object parent, boolean append)
|
|
||||||
{
|
|
||||||
return new FileChannelImpl(fd, readable, writable, parent, append);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ensureOpen() throws IOException {
|
private void ensureOpen() throws IOException {
|
||||||
@ -134,15 +125,7 @@ public class FileChannelImpl
|
|||||||
// superclass AbstractInterruptibleChannel, but the isOpen logic in
|
// superclass AbstractInterruptibleChannel, but the isOpen logic in
|
||||||
// that method will prevent this method from being reinvoked.
|
// that method will prevent this method from being reinvoked.
|
||||||
//
|
//
|
||||||
if (parent instanceof FileInputStream)
|
((java.io.Closeable)parent).close();
|
||||||
((FileInputStream)parent).close();
|
|
||||||
else if (parent instanceof FileOutputStream)
|
|
||||||
((FileOutputStream)parent).close();
|
|
||||||
else if (parent instanceof RandomAccessFile)
|
|
||||||
((RandomAccessFile)parent).close();
|
|
||||||
else
|
|
||||||
assert false;
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
nd.close(fd);
|
nd.close(fd);
|
||||||
}
|
}
|
||||||
@ -218,8 +201,6 @@ public class FileChannelImpl
|
|||||||
if (!isOpen())
|
if (!isOpen())
|
||||||
return 0;
|
return 0;
|
||||||
ti = threads.add();
|
ti = threads.add();
|
||||||
if (appending)
|
|
||||||
position(size());
|
|
||||||
do {
|
do {
|
||||||
n = IOUtil.write(fd, src, -1, nd, positionLock);
|
n = IOUtil.write(fd, src, -1, nd, positionLock);
|
||||||
} while ((n == IOStatus.INTERRUPTED) && isOpen());
|
} while ((n == IOStatus.INTERRUPTED) && isOpen());
|
||||||
@ -244,8 +225,6 @@ public class FileChannelImpl
|
|||||||
if (!isOpen())
|
if (!isOpen())
|
||||||
return 0;
|
return 0;
|
||||||
ti = threads.add();
|
ti = threads.add();
|
||||||
if (appending)
|
|
||||||
position(size());
|
|
||||||
do {
|
do {
|
||||||
n = IOUtil.write(fd, srcs, nd);
|
n = IOUtil.write(fd, srcs, nd);
|
||||||
} while ((n == IOStatus.INTERRUPTED) && isOpen());
|
} while ((n == IOStatus.INTERRUPTED) && isOpen());
|
||||||
@ -1051,7 +1030,7 @@ public class FileChannelImpl
|
|||||||
private FileKey fileKey;
|
private FileKey fileKey;
|
||||||
|
|
||||||
FileLockReference(FileLock referent,
|
FileLockReference(FileLock referent,
|
||||||
ReferenceQueue queue,
|
ReferenceQueue<FileLock> queue,
|
||||||
FileKey key) {
|
FileKey key) {
|
||||||
super(referent, queue);
|
super(referent, queue);
|
||||||
this.fileKey = key;
|
this.fileKey = key;
|
||||||
@ -1073,7 +1052,7 @@ public class FileChannelImpl
|
|||||||
new ConcurrentHashMap<FileKey, ArrayList<FileLockReference>>();
|
new ConcurrentHashMap<FileKey, ArrayList<FileLockReference>>();
|
||||||
|
|
||||||
// reference queue for cleared refs
|
// reference queue for cleared refs
|
||||||
private static ReferenceQueue queue = new ReferenceQueue();
|
private static ReferenceQueue<FileLock> queue = new ReferenceQueue<FileLock>();
|
||||||
|
|
||||||
// the enclosing file channel
|
// the enclosing file channel
|
||||||
private FileChannelImpl fci;
|
private FileChannelImpl fci;
|
||||||
|
@ -173,7 +173,7 @@ writeBytes(JNIEnv *env, jobject this, jbyteArray bytes,
|
|||||||
fd = GET_FD(this, fid);
|
fd = GET_FD(this, fid);
|
||||||
if (fd == -1) {
|
if (fd == -1) {
|
||||||
JNU_ThrowIOException(env, "Stream Closed");
|
JNU_ThrowIOException(env, "Stream Closed");
|
||||||
return;
|
break;
|
||||||
}
|
}
|
||||||
n = IO_Write(fd, buf+off, len);
|
n = IO_Write(fd, buf+off, len);
|
||||||
if (n == JVM_IO_ERR) {
|
if (n == JVM_IO_ERR) {
|
||||||
|
@ -152,11 +152,19 @@ public final class FileDescriptor {
|
|||||||
public int get(FileDescriptor obj) {
|
public int get(FileDescriptor obj) {
|
||||||
return obj.fd;
|
return obj.fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setHandle(FileDescriptor obj, long handle) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getHandle(FileDescriptor obj) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// pacakge private methods used by FIS,FOS and RAF
|
// package private methods used by FIS, FOS and RAF
|
||||||
|
|
||||||
int incrementAndGetUseCount() {
|
int incrementAndGetUseCount() {
|
||||||
return useCount.incrementAndGet();
|
return useCount.incrementAndGet();
|
||||||
|
@ -26,7 +26,10 @@
|
|||||||
package java.lang;
|
package java.lang;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.Process;
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.lang.ProcessBuilder.Redirect;
|
||||||
|
import java.lang.ProcessBuilder.Redirect;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class is for the exclusive use of ProcessBuilder.start() to
|
* This class is for the exclusive use of ProcessBuilder.start() to
|
||||||
@ -36,6 +39,9 @@ import java.lang.Process;
|
|||||||
* @since 1.5
|
* @since 1.5
|
||||||
*/
|
*/
|
||||||
final class ProcessImpl {
|
final class ProcessImpl {
|
||||||
|
private static final sun.misc.JavaIOFileDescriptorAccess fdAccess
|
||||||
|
= sun.misc.SharedSecrets.getJavaIOFileDescriptorAccess();
|
||||||
|
|
||||||
private ProcessImpl() {} // Not instantiable
|
private ProcessImpl() {} // Not instantiable
|
||||||
|
|
||||||
private static byte[] toCString(String s) {
|
private static byte[] toCString(String s) {
|
||||||
@ -54,6 +60,7 @@ final class ProcessImpl {
|
|||||||
static Process start(String[] cmdarray,
|
static Process start(String[] cmdarray,
|
||||||
java.util.Map<String,String> environment,
|
java.util.Map<String,String> environment,
|
||||||
String dir,
|
String dir,
|
||||||
|
ProcessBuilder.Redirect[] redirects,
|
||||||
boolean redirectErrorStream)
|
boolean redirectErrorStream)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
@ -78,11 +85,61 @@ final class ProcessImpl {
|
|||||||
int[] envc = new int[1];
|
int[] envc = new int[1];
|
||||||
byte[] envBlock = ProcessEnvironment.toEnvironmentBlock(environment, envc);
|
byte[] envBlock = ProcessEnvironment.toEnvironmentBlock(environment, envc);
|
||||||
|
|
||||||
|
int[] std_fds;
|
||||||
|
|
||||||
|
FileInputStream f0 = null;
|
||||||
|
FileOutputStream f1 = null;
|
||||||
|
FileOutputStream f2 = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (redirects == null) {
|
||||||
|
std_fds = new int[] { -1, -1, -1 };
|
||||||
|
} else {
|
||||||
|
std_fds = new int[3];
|
||||||
|
|
||||||
|
if (redirects[0] == Redirect.PIPE)
|
||||||
|
std_fds[0] = -1;
|
||||||
|
else if (redirects[0] == Redirect.INHERIT)
|
||||||
|
std_fds[0] = 0;
|
||||||
|
else {
|
||||||
|
f0 = new FileInputStream(redirects[0].file());
|
||||||
|
std_fds[0] = fdAccess.get(f0.getFD());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (redirects[1] == Redirect.PIPE)
|
||||||
|
std_fds[1] = -1;
|
||||||
|
else if (redirects[1] == Redirect.INHERIT)
|
||||||
|
std_fds[1] = 1;
|
||||||
|
else {
|
||||||
|
f1 = redirects[1].toFileOutputStream();
|
||||||
|
std_fds[1] = fdAccess.get(f1.getFD());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (redirects[2] == Redirect.PIPE)
|
||||||
|
std_fds[2] = -1;
|
||||||
|
else if (redirects[2] == Redirect.INHERIT)
|
||||||
|
std_fds[2] = 2;
|
||||||
|
else {
|
||||||
|
f2 = redirects[2].toFileOutputStream();
|
||||||
|
std_fds[2] = fdAccess.get(f2.getFD());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return new UNIXProcess
|
return new UNIXProcess
|
||||||
(toCString(cmdarray[0]),
|
(toCString(cmdarray[0]),
|
||||||
argBlock, args.length,
|
argBlock, args.length,
|
||||||
envBlock, envc[0],
|
envBlock, envc[0],
|
||||||
toCString(dir),
|
toCString(dir),
|
||||||
|
std_fds,
|
||||||
redirectErrorStream);
|
redirectErrorStream);
|
||||||
|
} finally {
|
||||||
|
// In theory, close() can throw IOException
|
||||||
|
// (although it is rather unlikely to happen here)
|
||||||
|
try { if (f0 != null) f0.close(); }
|
||||||
|
finally {
|
||||||
|
try { if (f1 != null) f1.close(); }
|
||||||
|
finally { if (f2 != null) f2.close(); }
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 1995-2006 Sun Microsystems, Inc. All Rights Reserved.
|
* Copyright 1995-2008 Sun Microsystems, Inc. All Rights Reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -34,9 +34,9 @@ import java.io.*;
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
final class UNIXProcess extends Process {
|
final class UNIXProcess extends Process {
|
||||||
private FileDescriptor stdin_fd;
|
private static final sun.misc.JavaIOFileDescriptorAccess fdAccess
|
||||||
private FileDescriptor stdout_fd;
|
= sun.misc.SharedSecrets.getJavaIOFileDescriptorAccess();
|
||||||
private FileDescriptor stderr_fd;
|
|
||||||
private int pid;
|
private int pid;
|
||||||
private int exitcode;
|
private int exitcode;
|
||||||
private boolean hasExited;
|
private boolean hasExited;
|
||||||
@ -48,14 +48,25 @@ final class UNIXProcess extends Process {
|
|||||||
/* this is for the reaping thread */
|
/* this is for the reaping thread */
|
||||||
private native int waitForProcessExit(int pid);
|
private native int waitForProcessExit(int pid);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a process using fork(2) and exec(2).
|
||||||
|
*
|
||||||
|
* @param std_fds array of file descriptors. Indexes 0, 1, and
|
||||||
|
* 2 correspond to standard input, standard output and
|
||||||
|
* standard error, respectively. On input, a value of -1
|
||||||
|
* means to create a pipe to connect child and parent
|
||||||
|
* processes. On output, a value which is not -1 is the
|
||||||
|
* parent pipe fd corresponding to the pipe which has
|
||||||
|
* been created. An element of this array is -1 on input
|
||||||
|
* if and only if it is <em>not</em> -1 on output.
|
||||||
|
* @return the pid of the subprocess
|
||||||
|
*/
|
||||||
private native int forkAndExec(byte[] prog,
|
private native int forkAndExec(byte[] prog,
|
||||||
byte[] argBlock, int argc,
|
byte[] argBlock, int argc,
|
||||||
byte[] envBlock, int envc,
|
byte[] envBlock, int envc,
|
||||||
byte[] dir,
|
byte[] dir,
|
||||||
boolean redirectErrorStream,
|
int[] std_fds,
|
||||||
FileDescriptor stdin_fd,
|
boolean redirectErrorStream)
|
||||||
FileDescriptor stdout_fd,
|
|
||||||
FileDescriptor stderr_fd)
|
|
||||||
throws IOException;
|
throws IOException;
|
||||||
|
|
||||||
/* In the process constructor we wait on this gate until the process */
|
/* In the process constructor we wait on this gate until the process */
|
||||||
@ -100,11 +111,9 @@ final class UNIXProcess extends Process {
|
|||||||
final byte[] argBlock, final int argc,
|
final byte[] argBlock, final int argc,
|
||||||
final byte[] envBlock, final int envc,
|
final byte[] envBlock, final int envc,
|
||||||
final byte[] dir,
|
final byte[] dir,
|
||||||
|
final int[] std_fds,
|
||||||
final boolean redirectErrorStream)
|
final boolean redirectErrorStream)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
stdin_fd = new FileDescriptor();
|
|
||||||
stdout_fd = new FileDescriptor();
|
|
||||||
stderr_fd = new FileDescriptor();
|
|
||||||
|
|
||||||
final Gate gate = new Gate();
|
final Gate gate = new Gate();
|
||||||
/*
|
/*
|
||||||
@ -117,8 +126,8 @@ final class UNIXProcess extends Process {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
java.security.AccessController.doPrivileged(
|
java.security.AccessController.doPrivileged(
|
||||||
new java.security.PrivilegedAction() {
|
new java.security.PrivilegedAction<Void>() {
|
||||||
public Object run() {
|
public Void run() {
|
||||||
Thread t = new Thread("process reaper") {
|
Thread t = new Thread("process reaper") {
|
||||||
public void run() {
|
public void run() {
|
||||||
try {
|
try {
|
||||||
@ -126,24 +135,43 @@ final class UNIXProcess extends Process {
|
|||||||
argBlock, argc,
|
argBlock, argc,
|
||||||
envBlock, envc,
|
envBlock, envc,
|
||||||
dir,
|
dir,
|
||||||
redirectErrorStream,
|
std_fds,
|
||||||
stdin_fd, stdout_fd, stderr_fd);
|
redirectErrorStream);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
gate.setException(e); /*remember to rethrow later*/
|
gate.setException(e); /*remember to rethrow later*/
|
||||||
gate.exit();
|
gate.exit();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
java.security.AccessController.doPrivileged(
|
java.security.AccessController.doPrivileged(
|
||||||
new java.security.PrivilegedAction() {
|
new java.security.PrivilegedAction<Void>() {
|
||||||
public Object run() {
|
public Void run() {
|
||||||
stdin_stream = new BufferedOutputStream(new
|
if (std_fds[0] == -1)
|
||||||
FileOutputStream(stdin_fd));
|
stdin_stream = new ProcessBuilder.NullOutputStream();
|
||||||
stdout_stream = new BufferedInputStream(new
|
else {
|
||||||
FileInputStream(stdout_fd));
|
FileDescriptor stdin_fd = new FileDescriptor();
|
||||||
stderr_stream = new FileInputStream(stderr_fd);
|
fdAccess.set(stdin_fd, std_fds[0]);
|
||||||
return null;
|
stdin_stream = new BufferedOutputStream(
|
||||||
|
new FileOutputStream(stdin_fd));
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
if (std_fds[1] == -1)
|
||||||
|
stdout_stream = new ProcessBuilder.NullInputStream();
|
||||||
|
else {
|
||||||
|
FileDescriptor stdout_fd = new FileDescriptor();
|
||||||
|
fdAccess.set(stdout_fd, std_fds[1]);
|
||||||
|
stdout_stream = new BufferedInputStream(
|
||||||
|
new FileInputStream(stdout_fd));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (std_fds[2] == -1)
|
||||||
|
stderr_stream = new ProcessBuilder.NullInputStream();
|
||||||
|
else {
|
||||||
|
FileDescriptor stderr_fd = new FileDescriptor();
|
||||||
|
fdAccess.set(stderr_fd, std_fds[2]);
|
||||||
|
stderr_stream = new FileInputStream(stderr_fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null; }});
|
||||||
gate.exit(); /* exit from constructor */
|
gate.exit(); /* exit from constructor */
|
||||||
int res = waitForProcessExit(pid);
|
int res = waitForProcessExit(pid);
|
||||||
synchronized (UNIXProcess.this) {
|
synchronized (UNIXProcess.this) {
|
||||||
@ -155,9 +183,7 @@ final class UNIXProcess extends Process {
|
|||||||
};
|
};
|
||||||
t.setDaemon(true);
|
t.setDaemon(true);
|
||||||
t.start();
|
t.start();
|
||||||
return null;
|
return null; }});
|
||||||
}
|
|
||||||
});
|
|
||||||
gate.waitForExit();
|
gate.waitForExit();
|
||||||
IOException e = gate.getException();
|
IOException e = gate.getException();
|
||||||
if (e != null)
|
if (e != null)
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 1995-2006 Sun Microsystems, Inc. All Rights Reserved.
|
* Copyright 1995-2008 Sun Microsystems, Inc. All Rights Reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -33,59 +33,85 @@ import java.io.*;
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
final class UNIXProcess extends Process {
|
final class UNIXProcess extends Process {
|
||||||
private FileDescriptor stdin_fd;
|
private static final sun.misc.JavaIOFileDescriptorAccess fdAccess
|
||||||
private FileDescriptor stdout_fd;
|
= sun.misc.SharedSecrets.getJavaIOFileDescriptorAccess();
|
||||||
private FileDescriptor stderr_fd;
|
|
||||||
private int pid;
|
private final int pid;
|
||||||
private int exitcode;
|
private int exitcode;
|
||||||
private boolean hasExited;
|
private boolean hasExited;
|
||||||
|
|
||||||
private OutputStream stdin_stream;
|
private OutputStream stdin_stream;
|
||||||
private BufferedInputStream stdout_stream;
|
private InputStream stdout_stream;
|
||||||
private DeferredCloseInputStream stdout_inner_stream;
|
private DeferredCloseInputStream stdout_inner_stream;
|
||||||
private DeferredCloseInputStream stderr_stream;
|
private InputStream stderr_stream;
|
||||||
|
|
||||||
/* this is for the reaping thread */
|
/* this is for the reaping thread */
|
||||||
private native int waitForProcessExit(int pid);
|
private native int waitForProcessExit(int pid);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a process using fork(2) and exec(2).
|
||||||
|
*
|
||||||
|
* @param std_fds array of file descriptors. Indexes 0, 1, and
|
||||||
|
* 2 correspond to standard input, standard output and
|
||||||
|
* standard error, respectively. On input, a value of -1
|
||||||
|
* means to create a pipe to connect child and parent
|
||||||
|
* processes. On output, a value which is not -1 is the
|
||||||
|
* parent pipe fd corresponding to the pipe which has
|
||||||
|
* been created. An element of this array is -1 on input
|
||||||
|
* if and only if it is <em>not</em> -1 on output.
|
||||||
|
* @return the pid of the subprocess
|
||||||
|
*/
|
||||||
private native int forkAndExec(byte[] prog,
|
private native int forkAndExec(byte[] prog,
|
||||||
byte[] argBlock, int argc,
|
byte[] argBlock, int argc,
|
||||||
byte[] envBlock, int envc,
|
byte[] envBlock, int envc,
|
||||||
byte[] dir,
|
byte[] dir,
|
||||||
boolean redirectErrorStream,
|
int[] std_fds,
|
||||||
FileDescriptor stdin_fd,
|
boolean redirectErrorStream)
|
||||||
FileDescriptor stdout_fd,
|
|
||||||
FileDescriptor stderr_fd)
|
|
||||||
throws IOException;
|
throws IOException;
|
||||||
|
|
||||||
UNIXProcess(final byte[] prog,
|
UNIXProcess(final byte[] prog,
|
||||||
final byte[] argBlock, int argc,
|
final byte[] argBlock, int argc,
|
||||||
final byte[] envBlock, int envc,
|
final byte[] envBlock, int envc,
|
||||||
final byte[] dir,
|
final byte[] dir,
|
||||||
|
final int[] std_fds,
|
||||||
final boolean redirectErrorStream)
|
final boolean redirectErrorStream)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
stdin_fd = new FileDescriptor();
|
|
||||||
stdout_fd = new FileDescriptor();
|
|
||||||
stderr_fd = new FileDescriptor();
|
|
||||||
|
|
||||||
pid = forkAndExec(prog,
|
pid = forkAndExec(prog,
|
||||||
argBlock, argc,
|
argBlock, argc,
|
||||||
envBlock, envc,
|
envBlock, envc,
|
||||||
dir,
|
dir,
|
||||||
redirectErrorStream,
|
std_fds,
|
||||||
stdin_fd, stdout_fd, stderr_fd);
|
redirectErrorStream);
|
||||||
|
|
||||||
java.security.AccessController.doPrivileged(
|
java.security.AccessController.doPrivileged(
|
||||||
new java.security.PrivilegedAction() {
|
new java.security.PrivilegedAction<Void>() { public Void run() {
|
||||||
public Object run() {
|
if (std_fds[0] == -1)
|
||||||
stdin_stream
|
stdin_stream = new ProcessBuilder.NullOutputStream();
|
||||||
= new BufferedOutputStream(new FileOutputStream(stdin_fd));
|
else {
|
||||||
|
FileDescriptor stdin_fd = new FileDescriptor();
|
||||||
|
fdAccess.set(stdin_fd, std_fds[0]);
|
||||||
|
stdin_stream = new BufferedOutputStream(
|
||||||
|
new FileOutputStream(stdin_fd));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (std_fds[1] == -1)
|
||||||
|
stdout_stream = new ProcessBuilder.NullInputStream();
|
||||||
|
else {
|
||||||
|
FileDescriptor stdout_fd = new FileDescriptor();
|
||||||
|
fdAccess.set(stdout_fd, std_fds[1]);
|
||||||
stdout_inner_stream = new DeferredCloseInputStream(stdout_fd);
|
stdout_inner_stream = new DeferredCloseInputStream(stdout_fd);
|
||||||
stdout_stream = new BufferedInputStream(stdout_inner_stream);
|
stdout_stream = new BufferedInputStream(stdout_inner_stream);
|
||||||
stderr_stream = new DeferredCloseInputStream(stderr_fd);
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
if (std_fds[2] == -1)
|
||||||
|
stderr_stream = new ProcessBuilder.NullInputStream();
|
||||||
|
else {
|
||||||
|
FileDescriptor stderr_fd = new FileDescriptor();
|
||||||
|
fdAccess.set(stderr_fd, std_fds[2]);
|
||||||
|
stderr_stream = new DeferredCloseInputStream(stderr_fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null; }});
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* For each subprocess forked a corresponding reaper thread
|
* For each subprocess forked a corresponding reaper thread
|
||||||
@ -97,8 +123,7 @@ final class UNIXProcess extends Process {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
java.security.AccessController.doPrivileged(
|
java.security.AccessController.doPrivileged(
|
||||||
new java.security.PrivilegedAction() {
|
new java.security.PrivilegedAction<Void>() { public Void run() {
|
||||||
public Object run() {
|
|
||||||
Thread t = new Thread("process reaper") {
|
Thread t = new Thread("process reaper") {
|
||||||
public void run() {
|
public void run() {
|
||||||
int res = waitForProcessExit(pid);
|
int res = waitForProcessExit(pid);
|
||||||
@ -111,9 +136,7 @@ final class UNIXProcess extends Process {
|
|||||||
};
|
};
|
||||||
t.setDaemon(true);
|
t.setDaemon(true);
|
||||||
t.start();
|
t.start();
|
||||||
return null;
|
return null; }});
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public OutputStream getOutputStream() {
|
public OutputStream getOutputStream() {
|
||||||
@ -154,8 +177,11 @@ final class UNIXProcess extends Process {
|
|||||||
destroyProcess(pid);
|
destroyProcess(pid);
|
||||||
try {
|
try {
|
||||||
stdin_stream.close();
|
stdin_stream.close();
|
||||||
|
if (stdout_inner_stream != null)
|
||||||
stdout_inner_stream.closeDeferred(stdout_stream);
|
stdout_inner_stream.closeDeferred(stdout_stream);
|
||||||
stderr_stream.closeDeferred(stderr_stream);
|
if (stderr_stream instanceof DeferredCloseInputStream)
|
||||||
|
((DeferredCloseInputStream) stderr_stream)
|
||||||
|
.closeDeferred(stderr_stream);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
|
@ -53,13 +53,10 @@ Java_java_io_FileOutputStream_initIDs(JNIEnv *env, jclass fdClass) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
JNIEXPORT void JNICALL
|
JNIEXPORT void JNICALL
|
||||||
Java_java_io_FileOutputStream_open(JNIEnv *env, jobject this, jstring path) {
|
Java_java_io_FileOutputStream_open(JNIEnv *env, jobject this,
|
||||||
fileOpen(env, this, path, fos_fd, O_WRONLY | O_CREAT | O_TRUNC);
|
jstring path, jboolean append) {
|
||||||
}
|
fileOpen(env, this, path, fos_fd,
|
||||||
|
O_WRONLY | O_CREAT | (append ? O_APPEND : O_TRUNC));
|
||||||
JNIEXPORT void JNICALL
|
|
||||||
Java_java_io_FileOutputStream_openAppend(JNIEnv *env, jobject this, jstring path) {
|
|
||||||
fileOpen(env, this, path, fos_fd, O_WRONLY | O_CREAT | O_APPEND);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
JNIEXPORT void JNICALL
|
JNIEXPORT void JNICALL
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 1995-2006 Sun Microsystems, Inc. All Rights Reserved.
|
* Copyright 1995-2008 Sun Microsystems, Inc. All Rights Reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -479,6 +479,37 @@ closeSafely(int fd)
|
|||||||
close(fd);
|
close(fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reads nbyte bytes from file descriptor fd into buf,
|
||||||
|
* The read operation is retried in case of EINTR or partial reads.
|
||||||
|
*
|
||||||
|
* Returns number of bytes read (normally nbyte, but may be less in
|
||||||
|
* case of EOF). In case of read errors, returns -1 and sets errno.
|
||||||
|
*/
|
||||||
|
static ssize_t
|
||||||
|
readFully(int fd, void *buf, size_t nbyte)
|
||||||
|
{
|
||||||
|
ssize_t remaining = nbyte;
|
||||||
|
for (;;) {
|
||||||
|
ssize_t n = read(fd, buf, remaining);
|
||||||
|
if (n == 0) {
|
||||||
|
return nbyte - remaining;
|
||||||
|
} else if (n > 0) {
|
||||||
|
remaining -= n;
|
||||||
|
if (remaining <= 0)
|
||||||
|
return nbyte;
|
||||||
|
/* We were interrupted in the middle of reading the bytes.
|
||||||
|
* Unlikely, but possible. */
|
||||||
|
buf = (void *) (((char *)buf) + n);
|
||||||
|
} else if (errno == EINTR) {
|
||||||
|
/* Strange signals like SIGJVM1 are possible at any time.
|
||||||
|
* See http://www.dreamsongs.com/WorseIsBetter.html */
|
||||||
|
} else {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#ifndef __solaris__
|
#ifndef __solaris__
|
||||||
#undef fork1
|
#undef fork1
|
||||||
#define fork1() fork()
|
#define fork1() fork()
|
||||||
@ -491,10 +522,8 @@ Java_java_lang_UNIXProcess_forkAndExec(JNIEnv *env,
|
|||||||
jbyteArray argBlock, jint argc,
|
jbyteArray argBlock, jint argc,
|
||||||
jbyteArray envBlock, jint envc,
|
jbyteArray envBlock, jint envc,
|
||||||
jbyteArray dir,
|
jbyteArray dir,
|
||||||
jboolean redirectErrorStream,
|
jintArray std_fds,
|
||||||
jobject stdin_fd,
|
jboolean redirectErrorStream)
|
||||||
jobject stdout_fd,
|
|
||||||
jobject stderr_fd)
|
|
||||||
{
|
{
|
||||||
int errnum;
|
int errnum;
|
||||||
int resultPid = -1;
|
int resultPid = -1;
|
||||||
@ -505,6 +534,7 @@ Java_java_lang_UNIXProcess_forkAndExec(JNIEnv *env,
|
|||||||
const char *pargBlock = getBytes(env, argBlock);
|
const char *pargBlock = getBytes(env, argBlock);
|
||||||
const char *penvBlock = getBytes(env, envBlock);
|
const char *penvBlock = getBytes(env, envBlock);
|
||||||
const char *pdir = getBytes(env, dir);
|
const char *pdir = getBytes(env, dir);
|
||||||
|
jint *fds = NULL;
|
||||||
|
|
||||||
in[0] = in[1] = out[0] = out[1] = err[0] = err[1] = fail[0] = fail[1] = -1;
|
in[0] = in[1] = out[0] = out[1] = err[0] = err[1] = fail[0] = fail[1] = -1;
|
||||||
|
|
||||||
@ -527,9 +557,13 @@ Java_java_lang_UNIXProcess_forkAndExec(JNIEnv *env,
|
|||||||
initVectorFromBlock(envv, penvBlock, envc);
|
initVectorFromBlock(envv, penvBlock, envc);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((pipe(in) < 0) ||
|
assert(std_fds != NULL);
|
||||||
(pipe(out) < 0) ||
|
fds = (*env)->GetIntArrayElements(env, std_fds, NULL);
|
||||||
(pipe(err) < 0) ||
|
if (fds == NULL) goto Catch;
|
||||||
|
|
||||||
|
if ((fds[0] == -1 && pipe(in) < 0) ||
|
||||||
|
(fds[1] == -1 && pipe(out) < 0) ||
|
||||||
|
(fds[2] == -1 && pipe(err) < 0) ||
|
||||||
(pipe(fail) < 0)) {
|
(pipe(fail) < 0)) {
|
||||||
throwIOException(env, errno, "Bad file descriptor");
|
throwIOException(env, errno, "Bad file descriptor");
|
||||||
goto Catch;
|
goto Catch;
|
||||||
@ -544,23 +578,26 @@ Java_java_lang_UNIXProcess_forkAndExec(JNIEnv *env,
|
|||||||
if (resultPid == 0) {
|
if (resultPid == 0) {
|
||||||
/* Child process */
|
/* Child process */
|
||||||
|
|
||||||
/* Close the parent sides of the pipe.
|
/* Close the parent sides of the pipes.
|
||||||
Give the child sides of the pipes the right fileno's.
|
|
||||||
Closing pipe fds here is redundant, since closeDescriptors()
|
Closing pipe fds here is redundant, since closeDescriptors()
|
||||||
would do it anyways, but a little paranoia is a good thing. */
|
would do it anyways, but a little paranoia is a good thing. */
|
||||||
|
closeSafely(in[1]);
|
||||||
|
closeSafely(out[0]);
|
||||||
|
closeSafely(err[0]);
|
||||||
|
closeSafely(fail[0]);
|
||||||
|
|
||||||
|
/* Give the child sides of the pipes the right fileno's. */
|
||||||
/* Note: it is possible for in[0] == 0 */
|
/* Note: it is possible for in[0] == 0 */
|
||||||
close(in[1]);
|
moveDescriptor(in[0] != -1 ? in[0] : fds[0], STDIN_FILENO);
|
||||||
moveDescriptor(in[0], STDIN_FILENO);
|
moveDescriptor(out[1]!= -1 ? out[1] : fds[1], STDOUT_FILENO);
|
||||||
close(out[0]);
|
|
||||||
moveDescriptor(out[1], STDOUT_FILENO);
|
|
||||||
close(err[0]);
|
|
||||||
if (redirectErrorStream) {
|
if (redirectErrorStream) {
|
||||||
close(err[1]);
|
closeSafely(err[1]);
|
||||||
dup2(STDOUT_FILENO, STDERR_FILENO);
|
dup2(STDOUT_FILENO, STDERR_FILENO);
|
||||||
} else {
|
} else {
|
||||||
moveDescriptor(err[1], STDERR_FILENO);
|
moveDescriptor(err[1] != -1 ? err[1] : fds[2], STDERR_FILENO);
|
||||||
}
|
}
|
||||||
close(fail[0]);
|
|
||||||
moveDescriptor(fail[1], FAIL_FILENO);
|
moveDescriptor(fail[1], FAIL_FILENO);
|
||||||
|
|
||||||
/* close everything */
|
/* close everything */
|
||||||
@ -600,15 +637,21 @@ Java_java_lang_UNIXProcess_forkAndExec(JNIEnv *env,
|
|||||||
/* parent process */
|
/* parent process */
|
||||||
|
|
||||||
close(fail[1]); fail[1] = -1; /* See: WhyCantJohnnyExec */
|
close(fail[1]); fail[1] = -1; /* See: WhyCantJohnnyExec */
|
||||||
if (read(fail[0], &errnum, sizeof(errnum)) != 0) {
|
|
||||||
|
switch (readFully(fail[0], &errnum, sizeof(errnum))) {
|
||||||
|
case 0: break; /* Exec succeeded */
|
||||||
|
case sizeof(errnum):
|
||||||
waitpid(resultPid, NULL, 0);
|
waitpid(resultPid, NULL, 0);
|
||||||
throwIOException(env, errnum, "Exec failed");
|
throwIOException(env, errnum, "Exec failed");
|
||||||
goto Catch;
|
goto Catch;
|
||||||
|
default:
|
||||||
|
throwIOException(env, errno, "Read failed");
|
||||||
|
goto Catch;
|
||||||
}
|
}
|
||||||
|
|
||||||
(*env)->SetIntField(env, stdin_fd, IO_fd_fdID, in [1]);
|
fds[0] = (in [1] != -1) ? in [1] : -1;
|
||||||
(*env)->SetIntField(env, stdout_fd, IO_fd_fdID, out[0]);
|
fds[1] = (out[0] != -1) ? out[0] : -1;
|
||||||
(*env)->SetIntField(env, stderr_fd, IO_fd_fdID, err[0]);
|
fds[2] = (err[0] != -1) ? err[0] : -1;
|
||||||
|
|
||||||
Finally:
|
Finally:
|
||||||
/* Always clean up the child's side of the pipes */
|
/* Always clean up the child's side of the pipes */
|
||||||
@ -628,6 +671,9 @@ Java_java_lang_UNIXProcess_forkAndExec(JNIEnv *env,
|
|||||||
releaseBytes(env, envBlock, penvBlock);
|
releaseBytes(env, envBlock, penvBlock);
|
||||||
releaseBytes(env, dir, pdir);
|
releaseBytes(env, dir, pdir);
|
||||||
|
|
||||||
|
if (fds != NULL)
|
||||||
|
(*env)->ReleaseIntArrayElements(env, std_fds, fds, 0);
|
||||||
|
|
||||||
return resultPid;
|
return resultPid;
|
||||||
|
|
||||||
Catch:
|
Catch:
|
||||||
|
@ -29,17 +29,14 @@ import java.util.concurrent.atomic.AtomicInteger;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Instances of the file descriptor class serve as an opaque handle
|
* Instances of the file descriptor class serve as an opaque handle
|
||||||
* to the underlying machine-specific structure representing an open
|
* to the underlying machine-specific structure representing an
|
||||||
* file, an open socket, or another source or sink of bytes. The
|
* open file, an open socket, or another source or sink of bytes.
|
||||||
* main practical use for a file descriptor is to create a
|
* The main practical use for a file descriptor is to create a
|
||||||
* <code>FileInputStream</code> or <code>FileOutputStream</code> to
|
* {@link FileInputStream} or {@link FileOutputStream} to contain it.
|
||||||
* contain it.
|
*
|
||||||
* <p>
|
* <p>Applications should not create their own file descriptors.
|
||||||
* Applications should not create their own file descriptors.
|
|
||||||
*
|
*
|
||||||
* @author Pavani Diwanji
|
* @author Pavani Diwanji
|
||||||
* @see java.io.FileInputStream
|
|
||||||
* @see java.io.FileOutputStream
|
|
||||||
* @since JDK1.0
|
* @since JDK1.0
|
||||||
*/
|
*/
|
||||||
public final class FileDescriptor {
|
public final class FileDescriptor {
|
||||||
@ -81,6 +78,14 @@ public final class FileDescriptor {
|
|||||||
public int get(FileDescriptor obj) {
|
public int get(FileDescriptor obj) {
|
||||||
return obj.fd;
|
return obj.fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setHandle(FileDescriptor obj, long handle) {
|
||||||
|
obj.handle = handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getHandle(FileDescriptor obj) {
|
||||||
|
return obj.handle;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -88,7 +93,7 @@ public final class FileDescriptor {
|
|||||||
/**
|
/**
|
||||||
* A handle to the standard input stream. Usually, this file
|
* A handle to the standard input stream. Usually, this file
|
||||||
* descriptor is not used directly, but rather via the input stream
|
* descriptor is not used directly, but rather via the input stream
|
||||||
* known as <code>System.in</code>.
|
* known as {@code System.in}.
|
||||||
*
|
*
|
||||||
* @see java.lang.System#in
|
* @see java.lang.System#in
|
||||||
*/
|
*/
|
||||||
@ -97,7 +102,7 @@ public final class FileDescriptor {
|
|||||||
/**
|
/**
|
||||||
* A handle to the standard output stream. Usually, this file
|
* A handle to the standard output stream. Usually, this file
|
||||||
* descriptor is not used directly, but rather via the output stream
|
* descriptor is not used directly, but rather via the output stream
|
||||||
* known as <code>System.out</code>.
|
* known as {@code System.out}.
|
||||||
* @see java.lang.System#out
|
* @see java.lang.System#out
|
||||||
*/
|
*/
|
||||||
public static final FileDescriptor out = standardStream(1);
|
public static final FileDescriptor out = standardStream(1);
|
||||||
@ -105,7 +110,7 @@ public final class FileDescriptor {
|
|||||||
/**
|
/**
|
||||||
* A handle to the standard error stream. Usually, this file
|
* A handle to the standard error stream. Usually, this file
|
||||||
* descriptor is not used directly, but rather via the output stream
|
* descriptor is not used directly, but rather via the output stream
|
||||||
* known as <code>System.err</code>.
|
* known as {@code System.err}.
|
||||||
*
|
*
|
||||||
* @see java.lang.System#err
|
* @see java.lang.System#err
|
||||||
*/
|
*/
|
||||||
@ -114,9 +119,9 @@ public final class FileDescriptor {
|
|||||||
/**
|
/**
|
||||||
* Tests if this file descriptor object is valid.
|
* Tests if this file descriptor object is valid.
|
||||||
*
|
*
|
||||||
* @return <code>true</code> if the file descriptor object represents a
|
* @return {@code true} if the file descriptor object represents a
|
||||||
* valid, open file, socket, or other active I/O connection;
|
* valid, open file, socket, or other active I/O connection;
|
||||||
* <code>false</code> otherwise.
|
* {@code false} otherwise.
|
||||||
*/
|
*/
|
||||||
public boolean valid() {
|
public boolean valid() {
|
||||||
return ((handle != -1) || (fd != -1));
|
return ((handle != -1) || (fd != -1));
|
||||||
|
@ -25,7 +25,16 @@
|
|||||||
|
|
||||||
package java.lang;
|
package java.lang;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.IOException;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.FileDescriptor;
|
||||||
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.BufferedOutputStream;
|
||||||
|
import java.lang.ProcessBuilder.Redirect;
|
||||||
|
|
||||||
/* This class is for the exclusive use of ProcessBuilder.start() to
|
/* This class is for the exclusive use of ProcessBuilder.start() to
|
||||||
* create new processes.
|
* create new processes.
|
||||||
@ -35,30 +44,82 @@ import java.io.*;
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
final class ProcessImpl extends Process {
|
final class ProcessImpl extends Process {
|
||||||
|
private static final sun.misc.JavaIOFileDescriptorAccess fdAccess
|
||||||
|
= sun.misc.SharedSecrets.getJavaIOFileDescriptorAccess();
|
||||||
|
|
||||||
// System-dependent portion of ProcessBuilder.start()
|
// System-dependent portion of ProcessBuilder.start()
|
||||||
static Process start(String cmdarray[],
|
static Process start(String cmdarray[],
|
||||||
java.util.Map<String,String> environment,
|
java.util.Map<String,String> environment,
|
||||||
String dir,
|
String dir,
|
||||||
|
ProcessBuilder.Redirect[] redirects,
|
||||||
boolean redirectErrorStream)
|
boolean redirectErrorStream)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
String envblock = ProcessEnvironment.toEnvironmentBlock(environment);
|
String envblock = ProcessEnvironment.toEnvironmentBlock(environment);
|
||||||
return new ProcessImpl(cmdarray, envblock, dir, redirectErrorStream);
|
|
||||||
|
FileInputStream f0 = null;
|
||||||
|
FileOutputStream f1 = null;
|
||||||
|
FileOutputStream f2 = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
long[] stdHandles;
|
||||||
|
if (redirects == null) {
|
||||||
|
stdHandles = new long[] { -1L, -1L, -1L };
|
||||||
|
} else {
|
||||||
|
stdHandles = new long[3];
|
||||||
|
|
||||||
|
if (redirects[0] == Redirect.PIPE)
|
||||||
|
stdHandles[0] = -1L;
|
||||||
|
else if (redirects[0] == Redirect.INHERIT)
|
||||||
|
stdHandles[0] = fdAccess.getHandle(FileDescriptor.in);
|
||||||
|
else {
|
||||||
|
f0 = new FileInputStream(redirects[0].file());
|
||||||
|
stdHandles[0] = fdAccess.getHandle(f0.getFD());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (redirects[1] == Redirect.PIPE)
|
||||||
|
stdHandles[1] = -1L;
|
||||||
|
else if (redirects[1] == Redirect.INHERIT)
|
||||||
|
stdHandles[1] = fdAccess.getHandle(FileDescriptor.out);
|
||||||
|
else {
|
||||||
|
f1 = redirects[1].toFileOutputStream();
|
||||||
|
stdHandles[1] = fdAccess.getHandle(f1.getFD());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (redirects[2] == Redirect.PIPE)
|
||||||
|
stdHandles[2] = -1L;
|
||||||
|
else if (redirects[2] == Redirect.INHERIT)
|
||||||
|
stdHandles[2] = fdAccess.getHandle(FileDescriptor.err);
|
||||||
|
else {
|
||||||
|
f2 = redirects[2].toFileOutputStream();
|
||||||
|
stdHandles[2] = fdAccess.getHandle(f2.getFD());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ProcessImpl(cmdarray, envblock, dir,
|
||||||
|
stdHandles, redirectErrorStream);
|
||||||
|
} finally {
|
||||||
|
// In theory, close() can throw IOException
|
||||||
|
// (although it is rather unlikely to happen here)
|
||||||
|
try { if (f0 != null) f0.close(); }
|
||||||
|
finally {
|
||||||
|
try { if (f1 != null) f1.close(); }
|
||||||
|
finally { if (f2 != null) f2.close(); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private long handle = 0;
|
private long handle = 0;
|
||||||
private FileDescriptor stdin_fd;
|
|
||||||
private FileDescriptor stdout_fd;
|
|
||||||
private FileDescriptor stderr_fd;
|
|
||||||
private OutputStream stdin_stream;
|
private OutputStream stdin_stream;
|
||||||
private InputStream stdout_stream;
|
private InputStream stdout_stream;
|
||||||
private InputStream stderr_stream;
|
private InputStream stderr_stream;
|
||||||
|
|
||||||
private ProcessImpl(String cmd[],
|
private ProcessImpl(final String cmd[],
|
||||||
String envblock,
|
final String envblock,
|
||||||
String path,
|
final String path,
|
||||||
boolean redirectErrorStream)
|
final long[] stdHandles,
|
||||||
|
final boolean redirectErrorStream)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
// Win32 CreateProcess requires cmd[0] to be normalized
|
// Win32 CreateProcess requires cmd[0] to be normalized
|
||||||
@ -91,25 +152,39 @@ final class ProcessImpl extends Process {
|
|||||||
}
|
}
|
||||||
String cmdstr = cmdbuf.toString();
|
String cmdstr = cmdbuf.toString();
|
||||||
|
|
||||||
stdin_fd = new FileDescriptor();
|
handle = create(cmdstr, envblock, path,
|
||||||
stdout_fd = new FileDescriptor();
|
stdHandles, redirectErrorStream);
|
||||||
stderr_fd = new FileDescriptor();
|
|
||||||
|
|
||||||
handle = create(cmdstr, envblock, path, redirectErrorStream,
|
|
||||||
stdin_fd, stdout_fd, stderr_fd);
|
|
||||||
|
|
||||||
java.security.AccessController.doPrivileged(
|
java.security.AccessController.doPrivileged(
|
||||||
new java.security.PrivilegedAction() {
|
new java.security.PrivilegedAction<Void>() {
|
||||||
public Object run() {
|
public Void run() {
|
||||||
stdin_stream =
|
if (stdHandles[0] == -1L)
|
||||||
new BufferedOutputStream(new FileOutputStream(stdin_fd));
|
stdin_stream = new ProcessBuilder.NullOutputStream();
|
||||||
stdout_stream =
|
else {
|
||||||
new BufferedInputStream(new FileInputStream(stdout_fd));
|
FileDescriptor stdin_fd = new FileDescriptor();
|
||||||
stderr_stream =
|
fdAccess.setHandle(stdin_fd, stdHandles[0]);
|
||||||
new FileInputStream(stderr_fd);
|
stdin_stream = new BufferedOutputStream(
|
||||||
return null;
|
new FileOutputStream(stdin_fd));
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
if (stdHandles[1] == -1L)
|
||||||
|
stdout_stream = new ProcessBuilder.NullInputStream();
|
||||||
|
else {
|
||||||
|
FileDescriptor stdout_fd = new FileDescriptor();
|
||||||
|
fdAccess.setHandle(stdout_fd, stdHandles[1]);
|
||||||
|
stdout_stream = new BufferedInputStream(
|
||||||
|
new FileInputStream(stdout_fd));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stdHandles[2] == -1L)
|
||||||
|
stderr_stream = new ProcessBuilder.NullInputStream();
|
||||||
|
else {
|
||||||
|
FileDescriptor stderr_fd = new FileDescriptor();
|
||||||
|
fdAccess.setHandle(stderr_fd, stdHandles[2]);
|
||||||
|
stderr_stream = new FileInputStream(stderr_fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null; }});
|
||||||
}
|
}
|
||||||
|
|
||||||
public OutputStream getOutputStream() {
|
public OutputStream getOutputStream() {
|
||||||
@ -150,13 +225,30 @@ final class ProcessImpl extends Process {
|
|||||||
public void destroy() { terminateProcess(handle); }
|
public void destroy() { terminateProcess(handle); }
|
||||||
private static native void terminateProcess(long handle);
|
private static native void terminateProcess(long handle);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a process using the win32 function CreateProcess.
|
||||||
|
*
|
||||||
|
* @param cmdstr the Windows commandline
|
||||||
|
* @param envblock NUL-separated, double-NUL-terminated list of
|
||||||
|
* environment strings in VAR=VALUE form
|
||||||
|
* @param dir the working directory of the process, or null if
|
||||||
|
* inheriting the current directory from the parent process
|
||||||
|
* @param stdHandles array of windows HANDLEs. Indexes 0, 1, and
|
||||||
|
* 2 correspond to standard input, standard output and
|
||||||
|
* standard error, respectively. On input, a value of -1
|
||||||
|
* means to create a pipe to connect child and parent
|
||||||
|
* processes. On output, a value which is not -1 is the
|
||||||
|
* parent pipe handle corresponding to the pipe which has
|
||||||
|
* been created. An element of this array is -1 on input
|
||||||
|
* if and only if it is <em>not</em> -1 on output.
|
||||||
|
* @param redirectErrorStream redirectErrorStream attribute
|
||||||
|
* @return the native subprocess HANDLE returned by CreateProcess
|
||||||
|
*/
|
||||||
private static native long create(String cmdstr,
|
private static native long create(String cmdstr,
|
||||||
String envblock,
|
String envblock,
|
||||||
String dir,
|
String dir,
|
||||||
boolean redirectErrorStream,
|
long[] stdHandles,
|
||||||
FileDescriptor in_fd,
|
boolean redirectErrorStream)
|
||||||
FileDescriptor out_fd,
|
|
||||||
FileDescriptor err_fd)
|
|
||||||
throws IOException;
|
throws IOException;
|
||||||
|
|
||||||
private static native boolean closeHandle(long handle);
|
private static native boolean closeHandle(long handle);
|
||||||
|
@ -39,8 +39,6 @@
|
|||||||
|
|
||||||
jfieldID fos_fd; /* id for jobject 'fd' in java.io.FileOutputStream */
|
jfieldID fos_fd; /* id for jobject 'fd' in java.io.FileOutputStream */
|
||||||
|
|
||||||
jfieldID fos_append;
|
|
||||||
|
|
||||||
/**************************************************************
|
/**************************************************************
|
||||||
* static methods to store field ID's in initializers
|
* static methods to store field ID's in initializers
|
||||||
*/
|
*/
|
||||||
@ -49,7 +47,6 @@ JNIEXPORT void JNICALL
|
|||||||
Java_java_io_FileOutputStream_initIDs(JNIEnv *env, jclass fosClass) {
|
Java_java_io_FileOutputStream_initIDs(JNIEnv *env, jclass fosClass) {
|
||||||
fos_fd =
|
fos_fd =
|
||||||
(*env)->GetFieldID(env, fosClass, "fd", "Ljava/io/FileDescriptor;");
|
(*env)->GetFieldID(env, fosClass, "fd", "Ljava/io/FileDescriptor;");
|
||||||
fos_append = (*env)->GetFieldID(env, fosClass, "append", "Z");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**************************************************************
|
/**************************************************************
|
||||||
@ -57,45 +54,20 @@ Java_java_io_FileOutputStream_initIDs(JNIEnv *env, jclass fosClass) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
JNIEXPORT void JNICALL
|
JNIEXPORT void JNICALL
|
||||||
Java_java_io_FileOutputStream_open(JNIEnv *env, jobject this, jstring path) {
|
Java_java_io_FileOutputStream_open(JNIEnv *env, jobject this,
|
||||||
fileOpen(env, this, path, fos_fd, O_WRONLY | O_CREAT | O_TRUNC);
|
jstring path, jboolean append) {
|
||||||
}
|
fileOpen(env, this, path, fos_fd,
|
||||||
|
O_WRONLY | O_CREAT | (append ? O_APPEND : O_TRUNC));
|
||||||
JNIEXPORT void JNICALL
|
|
||||||
Java_java_io_FileOutputStream_openAppend(JNIEnv *env, jobject this, jstring path) {
|
|
||||||
fileOpen(env, this, path, fos_fd, O_WRONLY | O_CREAT | O_APPEND);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
JNIEXPORT void JNICALL
|
JNIEXPORT void JNICALL
|
||||||
Java_java_io_FileOutputStream_write(JNIEnv *env, jobject this, jint byte) {
|
Java_java_io_FileOutputStream_write(JNIEnv *env, jobject this, jint byte) {
|
||||||
jboolean append = (*env)->GetBooleanField(env, this, fos_append);
|
|
||||||
FD fd = GET_FD(this, fos_fd);
|
|
||||||
if (fd == -1) {
|
|
||||||
JNU_ThrowIOException(env, "Stream Closed");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (append == JNI_TRUE) {
|
|
||||||
if (IO_Lseek(fd, 0L, SEEK_END) == -1) {
|
|
||||||
JNU_ThrowIOExceptionWithLastError(env, "Append failed");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
writeSingle(env, this, byte, fos_fd);
|
writeSingle(env, this, byte, fos_fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
JNIEXPORT void JNICALL
|
JNIEXPORT void JNICALL
|
||||||
Java_java_io_FileOutputStream_writeBytes(JNIEnv *env,
|
Java_java_io_FileOutputStream_writeBytes(JNIEnv *env,
|
||||||
jobject this, jbyteArray bytes, jint off, jint len) {
|
jobject this, jbyteArray bytes, jint off, jint len) {
|
||||||
jboolean append = (*env)->GetBooleanField(env, this, fos_append);
|
|
||||||
FD fd = GET_FD(this, fos_fd);
|
|
||||||
if (fd == -1) {
|
|
||||||
JNU_ThrowIOException(env, "Stream Closed");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (append == JNI_TRUE) {
|
|
||||||
if (IO_Lseek(fd, 0L, SEEK_END) == -1) {
|
|
||||||
JNU_ThrowIOExceptionWithLastError(env, "Append failed");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
writeBytes(env, this, bytes, off, len, fos_fd);
|
writeBytes(env, this, bytes, off, len, fos_fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@
|
|||||||
|
|
||||||
extern jboolean onNT = JNI_FALSE;
|
extern jboolean onNT = JNI_FALSE;
|
||||||
|
|
||||||
static int MAX_INPUT_EVENTS = 2000;
|
static DWORD MAX_INPUT_EVENTS = 2000;
|
||||||
|
|
||||||
void
|
void
|
||||||
initializeWindowsVersion() {
|
initializeWindowsVersion() {
|
||||||
@ -190,9 +190,16 @@ pathToNTPath(JNIEnv *env, jstring path, jboolean throwFNFE) {
|
|||||||
jlong
|
jlong
|
||||||
winFileHandleOpen(JNIEnv *env, jstring path, int flags)
|
winFileHandleOpen(JNIEnv *env, jstring path, int flags)
|
||||||
{
|
{
|
||||||
|
/* To implement O_APPEND, we use the strategy from
|
||||||
|
http://msdn2.microsoft.com/en-us/library/aa363858.aspx
|
||||||
|
"You can get atomic append by opening a file with
|
||||||
|
FILE_APPEND_DATA access and _without_ FILE_WRITE_DATA access.
|
||||||
|
If you do this then all writes will ignore the current file
|
||||||
|
pointer and be done at the end-of file." */
|
||||||
const DWORD access =
|
const DWORD access =
|
||||||
(flags & O_RDWR) ? (GENERIC_WRITE | GENERIC_READ) :
|
(flags & O_APPEND) ? (FILE_GENERIC_WRITE & ~FILE_WRITE_DATA) :
|
||||||
(flags & O_WRONLY) ? GENERIC_WRITE :
|
(flags & O_WRONLY) ? GENERIC_WRITE :
|
||||||
|
(flags & O_RDWR) ? (GENERIC_READ | GENERIC_WRITE) :
|
||||||
GENERIC_READ;
|
GENERIC_READ;
|
||||||
const DWORD sharing =
|
const DWORD sharing =
|
||||||
FILE_SHARE_READ | FILE_SHARE_WRITE;
|
FILE_SHARE_READ | FILE_SHARE_WRITE;
|
||||||
@ -444,24 +451,6 @@ handleSetLength(jlong fd, jlong length) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
|
||||||
handleFileSizeFD(jlong fd, jlong *size)
|
|
||||||
{
|
|
||||||
DWORD sizeLow = 0;
|
|
||||||
DWORD sizeHigh = 0;
|
|
||||||
HANDLE h = (HANDLE)fd;
|
|
||||||
if (h == INVALID_HANDLE_VALUE) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
sizeLow = GetFileSize(h, &sizeHigh);
|
|
||||||
if (sizeLow == ((DWORD)-1)) {
|
|
||||||
if (GetLastError() != ERROR_SUCCESS) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return (((jlong)sizeHigh) << 32) | sizeLow;
|
|
||||||
}
|
|
||||||
|
|
||||||
JNIEXPORT
|
JNIEXPORT
|
||||||
size_t
|
size_t
|
||||||
handleRead(jlong fd, void *buf, jint len)
|
handleRead(jlong fd, void *buf, jint len)
|
||||||
@ -513,7 +502,7 @@ handleClose(JNIEnv *env, jobject this, jfieldID fid)
|
|||||||
FD fd = GET_FD(this, fid);
|
FD fd = GET_FD(this, fid);
|
||||||
HANDLE h = (HANDLE)fd;
|
HANDLE h = (HANDLE)fd;
|
||||||
|
|
||||||
if (fd == INVALID_HANDLE_VALUE) {
|
if (h == INVALID_HANDLE_VALUE) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,7 +38,6 @@ void fileOpen(JNIEnv *env, jobject this, jstring path, jfieldID fid, int flags);
|
|||||||
int handleAvailable(jlong fd, jlong *pbytes);
|
int handleAvailable(jlong fd, jlong *pbytes);
|
||||||
JNIEXPORT int handleSync(jlong fd);
|
JNIEXPORT int handleSync(jlong fd);
|
||||||
int handleSetLength(jlong fd, jlong length);
|
int handleSetLength(jlong fd, jlong length);
|
||||||
int handleFileSizeFD(jlong fd, jlong *size);
|
|
||||||
JNIEXPORT size_t handleRead(jlong fd, void *buf, jint len);
|
JNIEXPORT size_t handleRead(jlong fd, void *buf, jint len);
|
||||||
JNIEXPORT size_t handleWrite(jlong fd, const void *buf, jint len);
|
JNIEXPORT size_t handleWrite(jlong fd, const void *buf, jint len);
|
||||||
jint handleClose(JNIEnv *env, jobject this, jfieldID fid);
|
jint handleClose(JNIEnv *env, jobject this, jfieldID fid);
|
||||||
|
@ -33,7 +33,12 @@
|
|||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <io.h>
|
#include <io.h>
|
||||||
|
|
||||||
#define PIPE_SIZE 4096
|
/* We try to make sure that we can read and write 4095 bytes (the
|
||||||
|
* fixed limit on Linux) to the pipe on all operating systems without
|
||||||
|
* deadlock. Windows 2000 inexplicably appears to need an extra 24
|
||||||
|
* bytes of slop to avoid deadlock.
|
||||||
|
*/
|
||||||
|
#define PIPE_SIZE (4096+24)
|
||||||
|
|
||||||
char *
|
char *
|
||||||
extractExecutablePath(JNIEnv *env, char *source)
|
extractExecutablePath(JNIEnv *env, char *source)
|
||||||
@ -120,7 +125,7 @@ win32Error(JNIEnv *env, const char *functionName)
|
|||||||
static void
|
static void
|
||||||
closeSafely(HANDLE handle)
|
closeSafely(HANDLE handle)
|
||||||
{
|
{
|
||||||
if (handle)
|
if (handle != INVALID_HANDLE_VALUE)
|
||||||
CloseHandle(handle);
|
CloseHandle(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,23 +134,22 @@ Java_java_lang_ProcessImpl_create(JNIEnv *env, jclass ignored,
|
|||||||
jstring cmd,
|
jstring cmd,
|
||||||
jstring envBlock,
|
jstring envBlock,
|
||||||
jstring dir,
|
jstring dir,
|
||||||
jboolean redirectErrorStream,
|
jlongArray stdHandles,
|
||||||
jobject in_fd,
|
jboolean redirectErrorStream)
|
||||||
jobject out_fd,
|
|
||||||
jobject err_fd)
|
|
||||||
{
|
{
|
||||||
HANDLE inRead = 0;
|
HANDLE inRead = INVALID_HANDLE_VALUE;
|
||||||
HANDLE inWrite = 0;
|
HANDLE inWrite = INVALID_HANDLE_VALUE;
|
||||||
HANDLE outRead = 0;
|
HANDLE outRead = INVALID_HANDLE_VALUE;
|
||||||
HANDLE outWrite = 0;
|
HANDLE outWrite = INVALID_HANDLE_VALUE;
|
||||||
HANDLE errRead = 0;
|
HANDLE errRead = INVALID_HANDLE_VALUE;
|
||||||
HANDLE errWrite = 0;
|
HANDLE errWrite = INVALID_HANDLE_VALUE;
|
||||||
SECURITY_ATTRIBUTES sa;
|
SECURITY_ATTRIBUTES sa;
|
||||||
PROCESS_INFORMATION pi;
|
PROCESS_INFORMATION pi;
|
||||||
STARTUPINFO si;
|
STARTUPINFO si;
|
||||||
LPTSTR pcmd = NULL;
|
LPTSTR pcmd = NULL;
|
||||||
LPCTSTR pdir = NULL;
|
LPCTSTR pdir = NULL;
|
||||||
LPVOID penvBlock = NULL;
|
LPVOID penvBlock = NULL;
|
||||||
|
jlong *handles = NULL;
|
||||||
jlong ret = 0;
|
jlong ret = 0;
|
||||||
OSVERSIONINFO ver;
|
OSVERSIONINFO ver;
|
||||||
jboolean onNT = JNI_FALSE;
|
jboolean onNT = JNI_FALSE;
|
||||||
@ -156,17 +160,6 @@ Java_java_lang_ProcessImpl_create(JNIEnv *env, jclass ignored,
|
|||||||
if (ver.dwPlatformId == VER_PLATFORM_WIN32_NT)
|
if (ver.dwPlatformId == VER_PLATFORM_WIN32_NT)
|
||||||
onNT = JNI_TRUE;
|
onNT = JNI_TRUE;
|
||||||
|
|
||||||
sa.nLength = sizeof(sa);
|
|
||||||
sa.lpSecurityDescriptor = 0;
|
|
||||||
sa.bInheritHandle = TRUE;
|
|
||||||
|
|
||||||
if (!(CreatePipe(&inRead, &inWrite, &sa, PIPE_SIZE) &&
|
|
||||||
CreatePipe(&outRead, &outWrite, &sa, PIPE_SIZE) &&
|
|
||||||
CreatePipe(&errRead, &errWrite, &sa, PIPE_SIZE))) {
|
|
||||||
win32Error(env, "CreatePipe");
|
|
||||||
goto Catch;
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(cmd != NULL);
|
assert(cmd != NULL);
|
||||||
pcmd = (LPTSTR) JNU_GetStringPlatformChars(env, cmd, NULL);
|
pcmd = (LPTSTR) JNU_GetStringPlatformChars(env, cmd, NULL);
|
||||||
if (pcmd == NULL) goto Catch;
|
if (pcmd == NULL) goto Catch;
|
||||||
@ -184,19 +177,62 @@ Java_java_lang_ProcessImpl_create(JNIEnv *env, jclass ignored,
|
|||||||
if (penvBlock == NULL) goto Catch;
|
if (penvBlock == NULL) goto Catch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert(stdHandles != NULL);
|
||||||
|
handles = (*env)->GetLongArrayElements(env, stdHandles, NULL);
|
||||||
|
if (handles == NULL) goto Catch;
|
||||||
|
|
||||||
memset(&si, 0, sizeof(si));
|
memset(&si, 0, sizeof(si));
|
||||||
si.cb = sizeof(si);
|
si.cb = sizeof(si);
|
||||||
si.dwFlags = STARTF_USESTDHANDLES;
|
si.dwFlags = STARTF_USESTDHANDLES;
|
||||||
|
|
||||||
|
sa.nLength = sizeof(sa);
|
||||||
|
sa.lpSecurityDescriptor = 0;
|
||||||
|
sa.bInheritHandle = TRUE;
|
||||||
|
|
||||||
|
if (handles[0] != (jlong) -1) {
|
||||||
|
si.hStdInput = (HANDLE) handles[0];
|
||||||
|
handles[0] = (jlong) -1;
|
||||||
|
} else {
|
||||||
|
if (! CreatePipe(&inRead, &inWrite, &sa, PIPE_SIZE)) {
|
||||||
|
win32Error(env, "CreatePipe");
|
||||||
|
goto Catch;
|
||||||
|
}
|
||||||
si.hStdInput = inRead;
|
si.hStdInput = inRead;
|
||||||
si.hStdOutput = outWrite;
|
|
||||||
si.hStdError = redirectErrorStream ? outWrite : errWrite;
|
|
||||||
|
|
||||||
SetHandleInformation(inWrite, HANDLE_FLAG_INHERIT, FALSE);
|
SetHandleInformation(inWrite, HANDLE_FLAG_INHERIT, FALSE);
|
||||||
SetHandleInformation(outRead, HANDLE_FLAG_INHERIT, FALSE);
|
handles[0] = (jlong) inWrite;
|
||||||
SetHandleInformation(errRead, HANDLE_FLAG_INHERIT, FALSE);
|
}
|
||||||
|
SetHandleInformation(si.hStdInput, HANDLE_FLAG_INHERIT, TRUE);
|
||||||
|
|
||||||
if (redirectErrorStream)
|
if (handles[1] != (jlong) -1) {
|
||||||
SetHandleInformation(errWrite, HANDLE_FLAG_INHERIT, FALSE);
|
si.hStdOutput = (HANDLE) handles[1];
|
||||||
|
handles[1] = (jlong) -1;
|
||||||
|
} else {
|
||||||
|
if (! CreatePipe(&outRead, &outWrite, &sa, PIPE_SIZE)) {
|
||||||
|
win32Error(env, "CreatePipe");
|
||||||
|
goto Catch;
|
||||||
|
}
|
||||||
|
si.hStdOutput = outWrite;
|
||||||
|
SetHandleInformation(outRead, HANDLE_FLAG_INHERIT, FALSE);
|
||||||
|
handles[1] = (jlong) outRead;
|
||||||
|
}
|
||||||
|
SetHandleInformation(si.hStdOutput, HANDLE_FLAG_INHERIT, TRUE);
|
||||||
|
|
||||||
|
if (redirectErrorStream) {
|
||||||
|
si.hStdError = si.hStdOutput;
|
||||||
|
handles[2] = (jlong) -1;
|
||||||
|
} else if (handles[2] != (jlong) -1) {
|
||||||
|
si.hStdError = (HANDLE) handles[2];
|
||||||
|
handles[2] = (jlong) -1;
|
||||||
|
} else {
|
||||||
|
if (! CreatePipe(&errRead, &errWrite, &sa, PIPE_SIZE)) {
|
||||||
|
win32Error(env, "CreatePipe");
|
||||||
|
goto Catch;
|
||||||
|
}
|
||||||
|
si.hStdError = errWrite;
|
||||||
|
SetHandleInformation(errRead, HANDLE_FLAG_INHERIT, FALSE);
|
||||||
|
handles[2] = (jlong) errRead;
|
||||||
|
}
|
||||||
|
SetHandleInformation(si.hStdError, HANDLE_FLAG_INHERIT, TRUE);
|
||||||
|
|
||||||
if (onNT)
|
if (onNT)
|
||||||
processFlag = CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT;
|
processFlag = CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT;
|
||||||
@ -232,9 +268,6 @@ Java_java_lang_ProcessImpl_create(JNIEnv *env, jclass ignored,
|
|||||||
|
|
||||||
CloseHandle(pi.hThread);
|
CloseHandle(pi.hThread);
|
||||||
ret = (jlong)pi.hProcess;
|
ret = (jlong)pi.hProcess;
|
||||||
(*env)->SetLongField(env, in_fd, IO_handle_fdID, (jlong)inWrite);
|
|
||||||
(*env)->SetLongField(env, out_fd, IO_handle_fdID, (jlong)outRead);
|
|
||||||
(*env)->SetLongField(env, err_fd, IO_handle_fdID, (jlong)errRead);
|
|
||||||
|
|
||||||
Finally:
|
Finally:
|
||||||
/* Always clean up the child's side of the pipes */
|
/* Always clean up the child's side of the pipes */
|
||||||
@ -252,6 +285,9 @@ Java_java_lang_ProcessImpl_create(JNIEnv *env, jclass ignored,
|
|||||||
else
|
else
|
||||||
JNU_ReleaseStringPlatformChars(env, dir, (char *) penvBlock);
|
JNU_ReleaseStringPlatformChars(env, dir, (char *) penvBlock);
|
||||||
}
|
}
|
||||||
|
if (handles != NULL)
|
||||||
|
(*env)->ReleaseLongArrayElements(env, stdHandles, handles, 0);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
Catch:
|
Catch:
|
||||||
|
@ -673,13 +673,13 @@ GetJavaProperties(JNIEnv* env)
|
|||||||
/* OS properties */
|
/* OS properties */
|
||||||
{
|
{
|
||||||
char buf[100];
|
char buf[100];
|
||||||
OSVERSIONINFO ver;
|
OSVERSIONINFOEX ver;
|
||||||
ver.dwOSVersionInfoSize = sizeof(ver);
|
ver.dwOSVersionInfoSize = sizeof(ver);
|
||||||
GetVersionEx(&ver);
|
GetVersionEx((OSVERSIONINFO *) &ver);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* From msdn page on OSVERSIONINFOEX, current as of this
|
* From msdn page on OSVERSIONINFOEX, current as of this
|
||||||
* writing decoding of dwMajorVersion and dwMinorVersion.
|
* writing, decoding of dwMajorVersion and dwMinorVersion.
|
||||||
*
|
*
|
||||||
* Operating system dwMajorVersion dwMinorVersion
|
* Operating system dwMajorVersion dwMinorVersion
|
||||||
* ================== ============== ==============
|
* ================== ============== ==============
|
||||||
@ -692,7 +692,7 @@ GetJavaProperties(JNIEnv* env)
|
|||||||
* Windows 2000 5 0
|
* Windows 2000 5 0
|
||||||
* Windows XP 5 1
|
* Windows XP 5 1
|
||||||
* Windows Server 2003 family 5 2
|
* Windows Server 2003 family 5 2
|
||||||
* Windows Vista 6 0
|
* Windows Vista family 6 0
|
||||||
*
|
*
|
||||||
* This mapping will presumably be augmented as new Windows
|
* This mapping will presumably be augmented as new Windows
|
||||||
* versions are released.
|
* versions are released.
|
||||||
@ -724,7 +724,20 @@ GetJavaProperties(JNIEnv* env)
|
|||||||
default: sprops.os_name = "Windows NT (unknown)"; break;
|
default: sprops.os_name = "Windows NT (unknown)"; break;
|
||||||
}
|
}
|
||||||
} else if (ver.dwMajorVersion == 6) {
|
} else if (ver.dwMajorVersion == 6) {
|
||||||
|
/*
|
||||||
|
* From MSDN OSVERSIONINFOEX documentation:
|
||||||
|
*
|
||||||
|
* "Because the version numbers for Windows Server 2008
|
||||||
|
* and Windows Vista are identical, you must also test
|
||||||
|
* whether the wProductType member is VER_NT_WORKSTATION.
|
||||||
|
* If wProductType is VER_NT_WORKSTATION, the operating
|
||||||
|
* system is Windows Vista; otherwise, it is Windows
|
||||||
|
* Server 2008."
|
||||||
|
*/
|
||||||
|
if (ver.wProductType == VER_NT_WORKSTATION)
|
||||||
sprops.os_name = "Windows Vista";
|
sprops.os_name = "Windows Vista";
|
||||||
|
else
|
||||||
|
sprops.os_name = "Windows Server 2008";
|
||||||
} else {
|
} else {
|
||||||
sprops.os_name = "Windows NT (unknown)";
|
sprops.os_name = "Windows NT (unknown)";
|
||||||
}
|
}
|
||||||
|
81
jdk/test/java/io/FileOutputStream/AtomicAppend.java
Normal file
81
jdk/test/java/io/FileOutputStream/AtomicAppend.java
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2007 Sun Microsystems, Inc. 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
||||||
|
* CA 95054 USA or visit www.sun.com if you need additional information or
|
||||||
|
* have any questions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @test
|
||||||
|
* @bug 6631352
|
||||||
|
* @summary Check that appends are atomic
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
public class AtomicAppend {
|
||||||
|
// Before the fix for
|
||||||
|
// 6631352: Implement atomic append mode using FILE_APPEND_DATA (win)
|
||||||
|
// this would fail intermittently on windows
|
||||||
|
void test(String[] args) throws Throwable {
|
||||||
|
final int nThreads = 10;
|
||||||
|
final int writes = 1000;
|
||||||
|
final File file = new File("foo");
|
||||||
|
file.delete();
|
||||||
|
try {
|
||||||
|
final ExecutorService es = Executors.newFixedThreadPool(nThreads);
|
||||||
|
for (int i = 0; i < nThreads; i++)
|
||||||
|
es.execute(new Runnable() { public void run() {
|
||||||
|
try {
|
||||||
|
FileOutputStream s = new FileOutputStream(file, true);
|
||||||
|
for (int j = 0; j < 1000; j++) {
|
||||||
|
s.write((int) 'x');
|
||||||
|
s.flush();
|
||||||
|
}
|
||||||
|
s.close();
|
||||||
|
} catch (Throwable t) { unexpected(t); }}});
|
||||||
|
es.shutdown();
|
||||||
|
es.awaitTermination(10L, TimeUnit.MINUTES);
|
||||||
|
equal(file.length(), (long) (nThreads * writes));
|
||||||
|
} finally {
|
||||||
|
file.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//--------------------- Infrastructure ---------------------------
|
||||||
|
volatile int passed = 0, failed = 0;
|
||||||
|
void pass() {passed++;}
|
||||||
|
void fail() {failed++; Thread.dumpStack();}
|
||||||
|
void fail(String msg) {System.err.println(msg); fail();}
|
||||||
|
void unexpected(Throwable t) {failed++; t.printStackTrace();}
|
||||||
|
void check(boolean cond) {if (cond) pass(); else fail();}
|
||||||
|
void equal(Object x, Object y) {
|
||||||
|
if (x == null ? y == null : x.equals(y)) pass();
|
||||||
|
else fail(x + " not equal to " + y);}
|
||||||
|
public static void main(String[] args) throws Throwable {
|
||||||
|
new AtomicAppend().instanceMain(args);}
|
||||||
|
void instanceMain(String[] args) throws Throwable {
|
||||||
|
try {test(args);} catch (Throwable t) {unexpected(t);}
|
||||||
|
System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
|
||||||
|
if (failed > 0) throw new AssertionError("Some tests failed");}
|
||||||
|
}
|
@ -25,12 +25,15 @@
|
|||||||
* @test
|
* @test
|
||||||
* @bug 4199068 4738465 4937983 4930681 4926230 4931433 4932663 4986689
|
* @bug 4199068 4738465 4937983 4930681 4926230 4931433 4932663 4986689
|
||||||
* 5026830 5023243 5070673 4052517 4811767 6192449 6397034 6413313
|
* 5026830 5023243 5070673 4052517 4811767 6192449 6397034 6413313
|
||||||
* 6464154 6523983 6206031
|
* 6464154 6523983 6206031 4960438 6631352 6631966
|
||||||
* @summary Basic tests for Process and Environment Variable code
|
* @summary Basic tests for Process and Environment Variable code
|
||||||
* @run main/othervm Basic
|
* @run main/othervm Basic
|
||||||
* @author Martin Buchholz
|
* @author Martin Buchholz
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import java.lang.ProcessBuilder.Redirect;
|
||||||
|
import static java.lang.ProcessBuilder.Redirect.*;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.security.*;
|
import java.security.*;
|
||||||
@ -257,7 +260,29 @@ public class Basic {
|
|||||||
public static class JavaChild {
|
public static class JavaChild {
|
||||||
public static void main(String args[]) throws Throwable {
|
public static void main(String args[]) throws Throwable {
|
||||||
String action = args[0];
|
String action = args[0];
|
||||||
if (action.equals("System.getenv(String)")) {
|
if (action.equals("testIO")) {
|
||||||
|
String expected = "standard input";
|
||||||
|
char[] buf = new char[expected.length()+1];
|
||||||
|
int n = new InputStreamReader(System.in).read(buf,0,buf.length);
|
||||||
|
if (n != expected.length())
|
||||||
|
System.exit(5);
|
||||||
|
if (! new String(buf,0,n).equals(expected))
|
||||||
|
System.exit(5);
|
||||||
|
System.err.print("standard error");
|
||||||
|
System.out.print("standard output");
|
||||||
|
} else if (action.equals("testInheritIO")) {
|
||||||
|
List<String> childArgs = new ArrayList<String>(javaChildArgs);
|
||||||
|
childArgs.add("testIO");
|
||||||
|
ProcessBuilder pb = new ProcessBuilder(childArgs);
|
||||||
|
pb.inheritIO();
|
||||||
|
ProcessResults r = run(pb);
|
||||||
|
if (! r.out().equals(""))
|
||||||
|
System.exit(7);
|
||||||
|
if (! r.err().equals(""))
|
||||||
|
System.exit(8);
|
||||||
|
if (r.exitValue() != 0)
|
||||||
|
System.exit(9);
|
||||||
|
} else if (action.equals("System.getenv(String)")) {
|
||||||
String val = System.getenv(args[1]);
|
String val = System.getenv(args[1]);
|
||||||
printUTF8(val == null ? "null" : val);
|
printUTF8(val == null ? "null" : val);
|
||||||
} else if (action.equals("System.getenv(\\u1234)")) {
|
} else if (action.equals("System.getenv(\\u1234)")) {
|
||||||
@ -599,6 +624,333 @@ public class Basic {
|
|||||||
} catch (Throwable t) { unexpected(t); }
|
} catch (Throwable t) { unexpected(t); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void checkRedirects(ProcessBuilder pb,
|
||||||
|
Redirect in, Redirect out, Redirect err) {
|
||||||
|
equal(pb.redirectInput(), in);
|
||||||
|
equal(pb.redirectOutput(), out);
|
||||||
|
equal(pb.redirectError(), err);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void redirectIO(ProcessBuilder pb,
|
||||||
|
Redirect in, Redirect out, Redirect err) {
|
||||||
|
pb.redirectInput(in);
|
||||||
|
pb.redirectOutput(out);
|
||||||
|
pb.redirectError(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void setFileContents(File file, String contents) {
|
||||||
|
try {
|
||||||
|
Writer w = new FileWriter(file);
|
||||||
|
w.write(contents);
|
||||||
|
w.close();
|
||||||
|
} catch (Throwable t) { unexpected(t); }
|
||||||
|
}
|
||||||
|
|
||||||
|
static String fileContents(File file) {
|
||||||
|
try {
|
||||||
|
Reader r = new FileReader(file);
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
char[] buffer = new char[1024];
|
||||||
|
int n;
|
||||||
|
while ((n = r.read(buffer)) != -1)
|
||||||
|
sb.append(buffer,0,n);
|
||||||
|
r.close();
|
||||||
|
return new String(sb);
|
||||||
|
} catch (Throwable t) { unexpected(t); return ""; }
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testIORedirection() throws Throwable {
|
||||||
|
final File ifile = new File("ifile");
|
||||||
|
final File ofile = new File("ofile");
|
||||||
|
final File efile = new File("efile");
|
||||||
|
ifile.delete();
|
||||||
|
ofile.delete();
|
||||||
|
efile.delete();
|
||||||
|
|
||||||
|
//----------------------------------------------------------------
|
||||||
|
// Check mutual inequality of different types of Redirect
|
||||||
|
//----------------------------------------------------------------
|
||||||
|
Redirect[] redirects =
|
||||||
|
{ PIPE,
|
||||||
|
INHERIT,
|
||||||
|
Redirect.from(ifile),
|
||||||
|
Redirect.to(ifile),
|
||||||
|
Redirect.appendTo(ifile),
|
||||||
|
Redirect.from(ofile),
|
||||||
|
Redirect.to(ofile),
|
||||||
|
Redirect.appendTo(ofile),
|
||||||
|
};
|
||||||
|
for (int i = 0; i < redirects.length; i++)
|
||||||
|
for (int j = 0; j < redirects.length; j++)
|
||||||
|
equal(redirects[i].equals(redirects[j]), (i == j));
|
||||||
|
|
||||||
|
//----------------------------------------------------------------
|
||||||
|
// Check basic properties of different types of Redirect
|
||||||
|
//----------------------------------------------------------------
|
||||||
|
equal(PIPE.type(), Redirect.Type.PIPE);
|
||||||
|
equal(PIPE.toString(), "PIPE");
|
||||||
|
equal(PIPE.file(), null);
|
||||||
|
|
||||||
|
equal(INHERIT.type(), Redirect.Type.INHERIT);
|
||||||
|
equal(INHERIT.toString(), "INHERIT");
|
||||||
|
equal(INHERIT.file(), null);
|
||||||
|
|
||||||
|
equal(Redirect.from(ifile).type(), Redirect.Type.READ);
|
||||||
|
equal(Redirect.from(ifile).toString(),
|
||||||
|
"redirect to read from file \"ifile\"");
|
||||||
|
equal(Redirect.from(ifile).file(), ifile);
|
||||||
|
equal(Redirect.from(ifile),
|
||||||
|
Redirect.from(ifile));
|
||||||
|
equal(Redirect.from(ifile).hashCode(),
|
||||||
|
Redirect.from(ifile).hashCode());
|
||||||
|
|
||||||
|
equal(Redirect.to(ofile).type(), Redirect.Type.WRITE);
|
||||||
|
equal(Redirect.to(ofile).toString(),
|
||||||
|
"redirect to write to file \"ofile\"");
|
||||||
|
equal(Redirect.to(ofile).file(), ofile);
|
||||||
|
equal(Redirect.to(ofile),
|
||||||
|
Redirect.to(ofile));
|
||||||
|
equal(Redirect.to(ofile).hashCode(),
|
||||||
|
Redirect.to(ofile).hashCode());
|
||||||
|
|
||||||
|
equal(Redirect.appendTo(ofile).type(), Redirect.Type.APPEND);
|
||||||
|
equal(Redirect.appendTo(efile).toString(),
|
||||||
|
"redirect to append to file \"efile\"");
|
||||||
|
equal(Redirect.appendTo(efile).file(), efile);
|
||||||
|
equal(Redirect.appendTo(efile),
|
||||||
|
Redirect.appendTo(efile));
|
||||||
|
equal(Redirect.appendTo(efile).hashCode(),
|
||||||
|
Redirect.appendTo(efile).hashCode());
|
||||||
|
|
||||||
|
//----------------------------------------------------------------
|
||||||
|
// Check initial values of redirects
|
||||||
|
//----------------------------------------------------------------
|
||||||
|
List<String> childArgs = new ArrayList<String>(javaChildArgs);
|
||||||
|
childArgs.add("testIO");
|
||||||
|
final ProcessBuilder pb = new ProcessBuilder(childArgs);
|
||||||
|
checkRedirects(pb, PIPE, PIPE, PIPE);
|
||||||
|
|
||||||
|
//----------------------------------------------------------------
|
||||||
|
// Check inheritIO
|
||||||
|
//----------------------------------------------------------------
|
||||||
|
pb.inheritIO();
|
||||||
|
checkRedirects(pb, INHERIT, INHERIT, INHERIT);
|
||||||
|
|
||||||
|
//----------------------------------------------------------------
|
||||||
|
// Check setters and getters agree
|
||||||
|
//----------------------------------------------------------------
|
||||||
|
pb.redirectInput(ifile);
|
||||||
|
equal(pb.redirectInput().file(), ifile);
|
||||||
|
equal(pb.redirectInput(), Redirect.from(ifile));
|
||||||
|
|
||||||
|
pb.redirectOutput(ofile);
|
||||||
|
equal(pb.redirectOutput().file(), ofile);
|
||||||
|
equal(pb.redirectOutput(), Redirect.to(ofile));
|
||||||
|
|
||||||
|
pb.redirectError(efile);
|
||||||
|
equal(pb.redirectError().file(), efile);
|
||||||
|
equal(pb.redirectError(), Redirect.to(efile));
|
||||||
|
|
||||||
|
THROWS(IllegalArgumentException.class,
|
||||||
|
new Fun(){void f() {
|
||||||
|
pb.redirectInput(Redirect.to(ofile)); }},
|
||||||
|
new Fun(){void f() {
|
||||||
|
pb.redirectInput(Redirect.appendTo(ofile)); }},
|
||||||
|
new Fun(){void f() {
|
||||||
|
pb.redirectOutput(Redirect.from(ifile)); }},
|
||||||
|
new Fun(){void f() {
|
||||||
|
pb.redirectError(Redirect.from(ifile)); }});
|
||||||
|
|
||||||
|
THROWS(IOException.class,
|
||||||
|
// Input file does not exist
|
||||||
|
new Fun(){void f() throws Throwable { pb.start(); }});
|
||||||
|
setFileContents(ifile, "standard input");
|
||||||
|
|
||||||
|
//----------------------------------------------------------------
|
||||||
|
// Writing to non-existent files
|
||||||
|
//----------------------------------------------------------------
|
||||||
|
{
|
||||||
|
ProcessResults r = run(pb);
|
||||||
|
equal(r.exitValue(), 0);
|
||||||
|
equal(fileContents(ofile), "standard output");
|
||||||
|
equal(fileContents(efile), "standard error");
|
||||||
|
equal(r.out(), "");
|
||||||
|
equal(r.err(), "");
|
||||||
|
ofile.delete();
|
||||||
|
efile.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------
|
||||||
|
// Both redirectErrorStream + redirectError
|
||||||
|
//----------------------------------------------------------------
|
||||||
|
{
|
||||||
|
pb.redirectErrorStream(true);
|
||||||
|
ProcessResults r = run(pb);
|
||||||
|
equal(r.exitValue(), 0);
|
||||||
|
equal(fileContents(ofile),
|
||||||
|
"standard error" + "standard output");
|
||||||
|
equal(fileContents(efile), "");
|
||||||
|
equal(r.out(), "");
|
||||||
|
equal(r.err(), "");
|
||||||
|
ofile.delete();
|
||||||
|
efile.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------
|
||||||
|
// Appending to existing files
|
||||||
|
//----------------------------------------------------------------
|
||||||
|
{
|
||||||
|
setFileContents(ofile, "ofile-contents");
|
||||||
|
setFileContents(efile, "efile-contents");
|
||||||
|
pb.redirectOutput(Redirect.appendTo(ofile));
|
||||||
|
pb.redirectError(Redirect.appendTo(efile));
|
||||||
|
pb.redirectErrorStream(false);
|
||||||
|
ProcessResults r = run(pb);
|
||||||
|
equal(r.exitValue(), 0);
|
||||||
|
equal(fileContents(ofile),
|
||||||
|
"ofile-contents" + "standard output");
|
||||||
|
equal(fileContents(efile),
|
||||||
|
"efile-contents" + "standard error");
|
||||||
|
equal(r.out(), "");
|
||||||
|
equal(r.err(), "");
|
||||||
|
ofile.delete();
|
||||||
|
efile.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------
|
||||||
|
// Replacing existing files
|
||||||
|
//----------------------------------------------------------------
|
||||||
|
{
|
||||||
|
setFileContents(ofile, "ofile-contents");
|
||||||
|
setFileContents(efile, "efile-contents");
|
||||||
|
pb.redirectOutput(ofile);
|
||||||
|
pb.redirectError(Redirect.to(efile));
|
||||||
|
ProcessResults r = run(pb);
|
||||||
|
equal(r.exitValue(), 0);
|
||||||
|
equal(fileContents(ofile), "standard output");
|
||||||
|
equal(fileContents(efile), "standard error");
|
||||||
|
equal(r.out(), "");
|
||||||
|
equal(r.err(), "");
|
||||||
|
ofile.delete();
|
||||||
|
efile.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------
|
||||||
|
// Appending twice to the same file?
|
||||||
|
//----------------------------------------------------------------
|
||||||
|
{
|
||||||
|
setFileContents(ofile, "ofile-contents");
|
||||||
|
setFileContents(efile, "efile-contents");
|
||||||
|
Redirect appender = Redirect.appendTo(ofile);
|
||||||
|
pb.redirectOutput(appender);
|
||||||
|
pb.redirectError(appender);
|
||||||
|
ProcessResults r = run(pb);
|
||||||
|
equal(r.exitValue(), 0);
|
||||||
|
equal(fileContents(ofile),
|
||||||
|
"ofile-contents" +
|
||||||
|
"standard error" +
|
||||||
|
"standard output");
|
||||||
|
equal(fileContents(efile), "efile-contents");
|
||||||
|
equal(r.out(), "");
|
||||||
|
equal(r.err(), "");
|
||||||
|
ifile.delete();
|
||||||
|
ofile.delete();
|
||||||
|
efile.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------
|
||||||
|
// Testing INHERIT is harder.
|
||||||
|
// Note that this requires __FOUR__ nested JVMs involved in one test,
|
||||||
|
// if you count the harness JVM.
|
||||||
|
//----------------------------------------------------------------
|
||||||
|
{
|
||||||
|
redirectIO(pb, PIPE, PIPE, PIPE);
|
||||||
|
List<String> command = pb.command();
|
||||||
|
command.set(command.size() - 1, "testInheritIO");
|
||||||
|
Process p = pb.start();
|
||||||
|
new PrintStream(p.getOutputStream()).print("standard input");
|
||||||
|
p.getOutputStream().close();
|
||||||
|
ProcessResults r = run(p);
|
||||||
|
equal(r.exitValue(), 0);
|
||||||
|
equal(r.out(), "standard output");
|
||||||
|
equal(r.err(), "standard error");
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------
|
||||||
|
// Test security implications of I/O redirection
|
||||||
|
//----------------------------------------------------------------
|
||||||
|
|
||||||
|
// Read access to current directory is always granted;
|
||||||
|
// So create a tmpfile for input instead.
|
||||||
|
final File tmpFile = File.createTempFile("Basic", "tmp");
|
||||||
|
setFileContents(tmpFile, "standard input");
|
||||||
|
|
||||||
|
final Policy policy = new Policy();
|
||||||
|
Policy.setPolicy(policy);
|
||||||
|
System.setSecurityManager(new SecurityManager());
|
||||||
|
try {
|
||||||
|
final Permission xPermission
|
||||||
|
= new FilePermission("<<ALL FILES>>", "execute");
|
||||||
|
final Permission rxPermission
|
||||||
|
= new FilePermission("<<ALL FILES>>", "read,execute");
|
||||||
|
final Permission wxPermission
|
||||||
|
= new FilePermission("<<ALL FILES>>", "write,execute");
|
||||||
|
final Permission rwxPermission
|
||||||
|
= new FilePermission("<<ALL FILES>>", "read,write,execute");
|
||||||
|
|
||||||
|
THROWS(SecurityException.class,
|
||||||
|
new Fun() { void f() throws IOException {
|
||||||
|
policy.setPermissions(xPermission);
|
||||||
|
redirectIO(pb, from(tmpFile), PIPE, PIPE);
|
||||||
|
pb.start();}},
|
||||||
|
new Fun() { void f() throws IOException {
|
||||||
|
policy.setPermissions(rxPermission);
|
||||||
|
redirectIO(pb, PIPE, to(ofile), PIPE);
|
||||||
|
pb.start();}},
|
||||||
|
new Fun() { void f() throws IOException {
|
||||||
|
policy.setPermissions(rxPermission);
|
||||||
|
redirectIO(pb, PIPE, PIPE, to(efile));
|
||||||
|
pb.start();}});
|
||||||
|
|
||||||
|
{
|
||||||
|
policy.setPermissions(rxPermission);
|
||||||
|
redirectIO(pb, from(tmpFile), PIPE, PIPE);
|
||||||
|
ProcessResults r = run(pb);
|
||||||
|
equal(r.out(), "standard output");
|
||||||
|
equal(r.err(), "standard error");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
policy.setPermissions(wxPermission);
|
||||||
|
redirectIO(pb, PIPE, to(ofile), to(efile));
|
||||||
|
Process p = pb.start();
|
||||||
|
new PrintStream(p.getOutputStream()).print("standard input");
|
||||||
|
p.getOutputStream().close();
|
||||||
|
ProcessResults r = run(p);
|
||||||
|
policy.setPermissions(rwxPermission);
|
||||||
|
equal(fileContents(ofile), "standard output");
|
||||||
|
equal(fileContents(efile), "standard error");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
policy.setPermissions(rwxPermission);
|
||||||
|
redirectIO(pb, from(tmpFile), to(ofile), to(efile));
|
||||||
|
ProcessResults r = run(pb);
|
||||||
|
policy.setPermissions(rwxPermission);
|
||||||
|
equal(fileContents(ofile), "standard output");
|
||||||
|
equal(fileContents(efile), "standard error");
|
||||||
|
}
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
policy.setPermissions(new RuntimePermission("setSecurityManager"));
|
||||||
|
System.setSecurityManager(null);
|
||||||
|
tmpFile.delete();
|
||||||
|
ifile.delete();
|
||||||
|
ofile.delete();
|
||||||
|
efile.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static void realMain(String[] args) throws Throwable {
|
private static void realMain(String[] args) throws Throwable {
|
||||||
if (Windows.is())
|
if (Windows.is())
|
||||||
System.out.println("This appears to be a Windows system.");
|
System.out.println("This appears to be a Windows system.");
|
||||||
@ -607,6 +959,9 @@ public class Basic {
|
|||||||
if (UnicodeOS.is())
|
if (UnicodeOS.is())
|
||||||
System.out.println("This appears to be a Unicode-based OS.");
|
System.out.println("This appears to be a Unicode-based OS.");
|
||||||
|
|
||||||
|
try { testIORedirection(); }
|
||||||
|
catch (Throwable t) { unexpected(t); }
|
||||||
|
|
||||||
//----------------------------------------------------------------
|
//----------------------------------------------------------------
|
||||||
// Basic tests for setting, replacing and deleting envvars
|
// Basic tests for setting, replacing and deleting envvars
|
||||||
//----------------------------------------------------------------
|
//----------------------------------------------------------------
|
||||||
@ -1354,7 +1709,8 @@ public class Basic {
|
|||||||
execPermission);
|
execPermission);
|
||||||
ProcessBuilder pb = new ProcessBuilder("env");
|
ProcessBuilder pb = new ProcessBuilder("env");
|
||||||
pb.environment().put("foo","bar");
|
pb.environment().put("foo","bar");
|
||||||
pb.start();
|
Process p = pb.start();
|
||||||
|
closeStreams(p);
|
||||||
} catch (IOException e) { // OK
|
} catch (IOException e) { // OK
|
||||||
} catch (Throwable t) { unexpected(t); }
|
} catch (Throwable t) { unexpected(t); }
|
||||||
|
|
||||||
@ -1378,6 +1734,14 @@ public class Basic {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void closeStreams(Process p) {
|
||||||
|
try {
|
||||||
|
p.getOutputStream().close();
|
||||||
|
p.getInputStream().close();
|
||||||
|
p.getErrorStream().close();
|
||||||
|
} catch (Throwable t) { unexpected(t); }
|
||||||
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------
|
//----------------------------------------------------------------
|
||||||
// A Policy class designed to make permissions fiddling very easy.
|
// A Policy class designed to make permissions fiddling very easy.
|
||||||
//----------------------------------------------------------------
|
//----------------------------------------------------------------
|
||||||
@ -1432,10 +1796,19 @@ public class Basic {
|
|||||||
}
|
}
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
throwable = t;
|
throwable = t;
|
||||||
|
} finally {
|
||||||
|
try { is.close(); }
|
||||||
|
catch (Throwable t) { throwable = t; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static ProcessResults run(ProcessBuilder pb) {
|
||||||
|
try {
|
||||||
|
return run(pb.start());
|
||||||
|
} catch (Throwable t) { unexpected(t); return null; }
|
||||||
|
}
|
||||||
|
|
||||||
private static ProcessResults run(Process p) {
|
private static ProcessResults run(Process p) {
|
||||||
Throwable throwable = null;
|
Throwable throwable = null;
|
||||||
int exitValue = -1;
|
int exitValue = -1;
|
||||||
|
Loading…
Reference in New Issue
Block a user