This commit is contained in:
Luis Miguel Alventosa 2008-03-10 23:31:50 +01:00
commit cc358443d5
23 changed files with 1870 additions and 608 deletions

View File

@ -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;

View File

@ -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

View File

@ -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 {

View File

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

View File

@ -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&lt;String, String&gt; 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

View File

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

View File

@ -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;

View File

@ -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) {

View File

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

View File

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

View File

@ -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)

View File

@ -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
} }

View File

@ -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

View File

@ -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:

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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:

View File

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

View 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");}
}

View File

@ -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;