8187443: Forest Consolidation: Move files to unified layout

Reviewed-by: darcy, ihse
This commit is contained in:
Erik Joelsson 2017-09-12 19:03:39 +02:00
parent 270fe13182
commit 3789983e89
56923 changed files with 3 additions and 15727 deletions

View file

@ -0,0 +1,40 @@
/*
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.io;
/**
*
* @since 1.8
*/
class DefaultFileSystem {
/**
* Return the FileSystem object for Unix-based platform.
*/
public static FileSystem getFileSystem() {
return new UnixFileSystem();
}
}

View file

@ -0,0 +1,247 @@
/*
* Copyright (c) 1995, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.io;
import java.util.ArrayList;
import java.util.List;
import jdk.internal.misc.JavaIOFileDescriptorAccess;
import jdk.internal.misc.SharedSecrets;
/**
* Instances of the file descriptor class serve as an opaque handle
* to the underlying machine-specific structure representing an open
* file, an open socket, or another source or sink of bytes. The
* main practical use for a file descriptor is to create a
* <code>FileInputStream</code> or <code>FileOutputStream</code> to
* contain it.
* <p>
* Applications should not create their own file descriptors.
*
* @author Pavani Diwanji
* @see java.io.FileInputStream
* @see java.io.FileOutputStream
* @since 1.0
*/
public final class FileDescriptor {
private int fd;
private Closeable parent;
private List<Closeable> otherParents;
private boolean closed;
/**
* true, if file is opened for appending.
*/
private boolean append;
/**
* Constructs an (invalid) FileDescriptor
* object.
*/
public FileDescriptor() {
fd = -1;
}
private FileDescriptor(int fd) {
this.fd = fd;
this.append = getAppend(fd);
}
/**
* A handle to the standard input stream. Usually, this file
* descriptor is not used directly, but rather via the input stream
* known as <code>System.in</code>.
*
* @see java.lang.System#in
*/
public static final FileDescriptor in = new FileDescriptor(0);
/**
* A handle to the standard output stream. Usually, this file
* descriptor is not used directly, but rather via the output stream
* known as <code>System.out</code>.
* @see java.lang.System#out
*/
public static final FileDescriptor out = new FileDescriptor(1);
/**
* A handle to the standard error stream. Usually, this file
* descriptor is not used directly, but rather via the output stream
* known as <code>System.err</code>.
*
* @see java.lang.System#err
*/
public static final FileDescriptor err = new FileDescriptor(2);
/**
* Tests if this file descriptor object is valid.
*
* @return <code>true</code> if the file descriptor object represents a
* valid, open file, socket, or other active I/O connection;
* <code>false</code> otherwise.
*/
public boolean valid() {
return fd != -1;
}
/**
* Force all system buffers to synchronize with the underlying
* device. This method returns after all modified data and
* attributes of this FileDescriptor have been written to the
* relevant device(s). In particular, if this FileDescriptor
* refers to a physical storage medium, such as a file in a file
* system, sync will not return until all in-memory modified copies
* of buffers associated with this FileDescriptor have been
* written to the physical medium.
*
* sync is meant to be used by code that requires physical
* storage (such as a file) to be in a known state For
* example, a class that provided a simple transaction facility
* might use sync to ensure that all changes to a file caused
* by a given transaction were recorded on a storage medium.
*
* sync only affects buffers downstream of this FileDescriptor. If
* any in-memory buffering is being done by the application (for
* example, by a BufferedOutputStream object), those buffers must
* be flushed into the FileDescriptor (for example, by invoking
* OutputStream.flush) before that data will be affected by sync.
*
* @exception SyncFailedException
* Thrown when the buffers cannot be flushed,
* or because the system cannot guarantee that all the
* buffers have been synchronized with physical media.
* @since 1.1
*/
public native void sync() throws SyncFailedException;
/* This routine initializes JNI field offsets for the class */
private static native void initIDs();
static {
initIDs();
}
// Set up JavaIOFileDescriptorAccess in SharedSecrets
static {
SharedSecrets.setJavaIOFileDescriptorAccess(
new JavaIOFileDescriptorAccess() {
public void set(FileDescriptor obj, int fd) {
obj.fd = fd;
}
public int get(FileDescriptor obj) {
return obj.fd;
}
public void setAppend(FileDescriptor obj, boolean append) {
obj.append = append;
}
public boolean getAppend(FileDescriptor obj) {
return obj.append;
}
public void setHandle(FileDescriptor obj, long handle) {
throw new UnsupportedOperationException();
}
public long getHandle(FileDescriptor obj) {
throw new UnsupportedOperationException();
}
}
);
}
/**
* Returns true, if the file was opened for appending.
*/
private static native boolean getAppend(int fd);
/*
* Package private methods to track referents.
* If multiple streams point to the same FileDescriptor, we cycle
* through the list of all referents and call close()
*/
/**
* Attach a Closeable to this FD for tracking.
* parent reference is added to otherParents when
* needed to make closeAll simpler.
*/
synchronized void attach(Closeable c) {
if (parent == null) {
// first caller gets to do this
parent = c;
} else if (otherParents == null) {
otherParents = new ArrayList<>();
otherParents.add(parent);
otherParents.add(c);
} else {
otherParents.add(c);
}
}
/**
* Cycle through all Closeables sharing this FD and call
* close() on each one.
*
* The caller closeable gets to call close0().
*/
@SuppressWarnings("try")
synchronized void closeAll(Closeable releaser) throws IOException {
if (!closed) {
closed = true;
IOException ioe = null;
try (releaser) {
if (otherParents != null) {
for (Closeable referent : otherParents) {
try {
referent.close();
} catch(IOException x) {
if (ioe == null) {
ioe = x;
} else {
ioe.addSuppressed(x);
}
}
}
}
} catch(IOException ex) {
/*
* If releaser close() throws IOException
* add other exceptions as suppressed.
*/
if (ioe != null)
ex.addSuppressed(ioe);
ioe = ex;
} finally {
if (ioe != null)
throw ioe;
}
}
}
}

View file

@ -0,0 +1,327 @@
/*
* Copyright (c) 1998, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.io;
import java.util.Properties;
import sun.security.action.GetPropertyAction;
class UnixFileSystem extends FileSystem {
private final char slash;
private final char colon;
private final String javaHome;
public UnixFileSystem() {
Properties props = GetPropertyAction.privilegedGetProperties();
slash = props.getProperty("file.separator").charAt(0);
colon = props.getProperty("path.separator").charAt(0);
javaHome = props.getProperty("java.home");
}
/* -- Normalization and construction -- */
public char getSeparator() {
return slash;
}
public char getPathSeparator() {
return colon;
}
/* A normal Unix pathname contains no duplicate slashes and does not end
with a slash. It may be the empty string. */
/* Normalize the given pathname, whose length is len, starting at the given
offset; everything before this offset is already normal. */
private String normalize(String pathname, int len, int off) {
if (len == 0) return pathname;
int n = len;
while ((n > 0) && (pathname.charAt(n - 1) == '/')) n--;
if (n == 0) return "/";
StringBuilder sb = new StringBuilder(pathname.length());
if (off > 0) sb.append(pathname, 0, off);
char prevChar = 0;
for (int i = off; i < n; i++) {
char c = pathname.charAt(i);
if ((prevChar == '/') && (c == '/')) continue;
sb.append(c);
prevChar = c;
}
return sb.toString();
}
/* Check that the given pathname is normal. If not, invoke the real
normalizer on the part of the pathname that requires normalization.
This way we iterate through the whole pathname string only once. */
public String normalize(String pathname) {
int n = pathname.length();
char prevChar = 0;
for (int i = 0; i < n; i++) {
char c = pathname.charAt(i);
if ((prevChar == '/') && (c == '/'))
return normalize(pathname, n, i - 1);
prevChar = c;
}
if (prevChar == '/') return normalize(pathname, n, n - 1);
return pathname;
}
public int prefixLength(String pathname) {
if (pathname.length() == 0) return 0;
return (pathname.charAt(0) == '/') ? 1 : 0;
}
public String resolve(String parent, String child) {
if (child.equals("")) return parent;
if (child.charAt(0) == '/') {
if (parent.equals("/")) return child;
return parent + child;
}
if (parent.equals("/")) return parent + child;
return parent + '/' + child;
}
public String getDefaultParent() {
return "/";
}
public String fromURIPath(String path) {
String p = path;
if (p.endsWith("/") && (p.length() > 1)) {
// "/foo/" --> "/foo", but "/" --> "/"
p = p.substring(0, p.length() - 1);
}
return p;
}
/* -- Path operations -- */
public boolean isAbsolute(File f) {
return (f.getPrefixLength() != 0);
}
public String resolve(File f) {
if (isAbsolute(f)) return f.getPath();
return resolve(System.getProperty("user.dir"), f.getPath());
}
// Caches for canonicalization results to improve startup performance.
// The first cache handles repeated canonicalizations of the same path
// name. The prefix cache handles repeated canonicalizations within the
// same directory, and must not create results differing from the true
// canonicalization algorithm in canonicalize_md.c. For this reason the
// prefix cache is conservative and is not used for complex path names.
private ExpiringCache cache = new ExpiringCache();
// On Unix symlinks can jump anywhere in the file system, so we only
// treat prefixes in java.home as trusted and cacheable in the
// canonicalization algorithm
private ExpiringCache javaHomePrefixCache = new ExpiringCache();
public String canonicalize(String path) throws IOException {
if (!useCanonCaches) {
return canonicalize0(path);
} else {
String res = cache.get(path);
if (res == null) {
String dir = null;
String resDir = null;
if (useCanonPrefixCache) {
// Note that this can cause symlinks that should
// be resolved to a destination directory to be
// resolved to the directory they're contained in
dir = parentOrNull(path);
if (dir != null) {
resDir = javaHomePrefixCache.get(dir);
if (resDir != null) {
// Hit only in prefix cache; full path is canonical
String filename = path.substring(1 + dir.length());
res = resDir + slash + filename;
cache.put(dir + slash + filename, res);
}
}
}
if (res == null) {
res = canonicalize0(path);
cache.put(path, res);
if (useCanonPrefixCache &&
dir != null && dir.startsWith(javaHome)) {
resDir = parentOrNull(res);
// Note that we don't allow a resolved symlink
// to elsewhere in java.home to pollute the
// prefix cache (java.home prefix cache could
// just as easily be a set at this point)
if (resDir != null && resDir.equals(dir)) {
File f = new File(res);
if (f.exists() && !f.isDirectory()) {
javaHomePrefixCache.put(dir, resDir);
}
}
}
}
}
return res;
}
}
private native String canonicalize0(String path) throws IOException;
// Best-effort attempt to get parent of this path; used for
// optimization of filename canonicalization. This must return null for
// any cases where the code in canonicalize_md.c would throw an
// exception or otherwise deal with non-simple pathnames like handling
// of "." and "..". It may conservatively return null in other
// situations as well. Returning null will cause the underlying
// (expensive) canonicalization routine to be called.
static String parentOrNull(String path) {
if (path == null) return null;
char sep = File.separatorChar;
int last = path.length() - 1;
int idx = last;
int adjacentDots = 0;
int nonDotCount = 0;
while (idx > 0) {
char c = path.charAt(idx);
if (c == '.') {
if (++adjacentDots >= 2) {
// Punt on pathnames containing . and ..
return null;
}
} else if (c == sep) {
if (adjacentDots == 1 && nonDotCount == 0) {
// Punt on pathnames containing . and ..
return null;
}
if (idx == 0 ||
idx >= last - 1 ||
path.charAt(idx - 1) == sep) {
// Punt on pathnames containing adjacent slashes
// toward the end
return null;
}
return path.substring(0, idx);
} else {
++nonDotCount;
adjacentDots = 0;
}
--idx;
}
return null;
}
/* -- Attribute accessors -- */
public native int getBooleanAttributes0(File f);
public int getBooleanAttributes(File f) {
int rv = getBooleanAttributes0(f);
String name = f.getName();
boolean hidden = (name.length() > 0) && (name.charAt(0) == '.');
return rv | (hidden ? BA_HIDDEN : 0);
}
public native boolean checkAccess(File f, int access);
public native long getLastModifiedTime(File f);
public native long getLength(File f);
public native boolean setPermission(File f, int access, boolean enable, boolean owneronly);
/* -- File operations -- */
public native boolean createFileExclusively(String path)
throws IOException;
public boolean delete(File f) {
// Keep canonicalization caches in sync after file deletion
// and renaming operations. Could be more clever than this
// (i.e., only remove/update affected entries) but probably
// not worth it since these entries expire after 30 seconds
// anyway.
cache.clear();
javaHomePrefixCache.clear();
return delete0(f);
}
private native boolean delete0(File f);
public native String[] list(File f);
public native boolean createDirectory(File f);
public boolean rename(File f1, File f2) {
// Keep canonicalization caches in sync after file deletion
// and renaming operations. Could be more clever than this
// (i.e., only remove/update affected entries) but probably
// not worth it since these entries expire after 30 seconds
// anyway.
cache.clear();
javaHomePrefixCache.clear();
return rename0(f1, f2);
}
private native boolean rename0(File f1, File f2);
public native boolean setLastModifiedTime(File f, long time);
public native boolean setReadOnly(File f);
/* -- Filesystem interface -- */
public File[] listRoots() {
try {
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkRead("/");
}
return new File[] { new File("/") };
} catch (SecurityException x) {
return new File[0];
}
}
/* -- Disk usage -- */
public native long getSpace(File f, int t);
/* -- Basic infrastructure -- */
private native long getNameMax0(String path);
public int getNameMax(String path) {
long nameMax = getNameMax0(path);
if (nameMax > Integer.MAX_VALUE) {
nameMax = Integer.MAX_VALUE;
}
return (int)nameMax;
}
public int compare(File f1, File f2) {
return f1.getPath().compareTo(f2.getPath());
}
public int hashCode(File f) {
return f.getPath().hashCode() ^ 1234321;
}
private static native void initIDs();
static {
initIDs();
}
}

View file

@ -0,0 +1,48 @@
/*
* Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.lang;
import java.io.File;
class ClassLoaderHelper {
private ClassLoaderHelper() {}
/**
* Indicates, whether PATH env variable is allowed to contain quoted entries.
*/
static final boolean allowsQuotedPathElements = false;
/**
* Returns an alternate path name for the given file
* such that if the original pathname did not exist, then the
* file may be located at the alternate location.
* For most platforms, this behavior is not supported and returns null.
*/
static File mapAlternativeName(File lib) {
return null;
}
}

View file

@ -0,0 +1,440 @@
/*
* Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/* We use APIs that access the standard Unix environ array, which
* is defined by UNIX98 to look like:
*
* char **environ;
*
* These are unsorted, case-sensitive, null-terminated arrays of bytes
* of the form FOO=BAR\000 which are usually encoded in the user's
* default encoding (file.encoding is an excellent choice for
* encoding/decoding these). However, even though the user cannot
* directly access the underlying byte representation, we take pains
* to pass on the child the exact byte representation we inherit from
* the parent process for any environment name or value not created by
* Javaland. So we keep track of all the byte representations.
*
* Internally, we define the types Variable and Value that exhibit
* String/byteArray duality. The internal representation of the
* environment then looks like a Map<Variable,Value>. But we don't
* expose this to the user -- we only provide a Map<String,String>
* view, although we could also provide a Map<byte[],byte[]> view.
*
* The non-private methods in this class are not for general use even
* within this package. Instead, they are the system-dependent parts
* of the system-independent method of the same name. Don't even
* think of using this class unless your method's name appears below.
*
* @author Martin Buchholz
* @since 1.5
*/
package java.lang;
import java.io.*;
import java.util.*;
final class ProcessEnvironment
{
private static final HashMap<Variable,Value> theEnvironment;
private static final Map<String,String> theUnmodifiableEnvironment;
static final int MIN_NAME_LENGTH = 0;
static {
// We cache the C environment. This means that subsequent calls
// to putenv/setenv from C will not be visible from Java code.
byte[][] environ = environ();
theEnvironment = new HashMap<>(environ.length/2 + 3);
// Read environment variables back to front,
// so that earlier variables override later ones.
for (int i = environ.length-1; i > 0; i-=2)
theEnvironment.put(Variable.valueOf(environ[i-1]),
Value.valueOf(environ[i]));
theUnmodifiableEnvironment
= Collections.unmodifiableMap
(new StringEnvironment(theEnvironment));
}
/* Only for use by System.getenv(String) */
static String getenv(String name) {
return theUnmodifiableEnvironment.get(name);
}
/* Only for use by System.getenv() */
static Map<String,String> getenv() {
return theUnmodifiableEnvironment;
}
/* Only for use by ProcessBuilder.environment() */
@SuppressWarnings("unchecked")
static Map<String,String> environment() {
return new StringEnvironment
((Map<Variable,Value>)(theEnvironment.clone()));
}
/* Only for use by Runtime.exec(...String[]envp...) */
static Map<String,String> emptyEnvironment(int capacity) {
return new StringEnvironment(new HashMap<>(capacity));
}
private static native byte[][] environ();
// This class is not instantiable.
private ProcessEnvironment() {}
// Check that name is suitable for insertion into Environment map
private static void validateVariable(String name) {
if (name.indexOf('=') != -1 ||
name.indexOf('\u0000') != -1)
throw new IllegalArgumentException
("Invalid environment variable name: \"" + name + "\"");
}
// Check that value is suitable for insertion into Environment map
private static void validateValue(String value) {
if (value.indexOf('\u0000') != -1)
throw new IllegalArgumentException
("Invalid environment variable value: \"" + value + "\"");
}
// A class hiding the byteArray-String duality of
// text data on Unixoid operating systems.
private abstract static class ExternalData {
protected final String str;
protected final byte[] bytes;
protected ExternalData(String str, byte[] bytes) {
this.str = str;
this.bytes = bytes;
}
public byte[] getBytes() {
return bytes;
}
public String toString() {
return str;
}
public boolean equals(Object o) {
return o instanceof ExternalData
&& arrayEquals(getBytes(), ((ExternalData) o).getBytes());
}
public int hashCode() {
return arrayHash(getBytes());
}
}
private static class Variable
extends ExternalData implements Comparable<Variable>
{
protected Variable(String str, byte[] bytes) {
super(str, bytes);
}
public static Variable valueOfQueryOnly(Object str) {
return valueOfQueryOnly((String) str);
}
public static Variable valueOfQueryOnly(String str) {
return new Variable(str, str.getBytes());
}
public static Variable valueOf(String str) {
validateVariable(str);
return valueOfQueryOnly(str);
}
public static Variable valueOf(byte[] bytes) {
return new Variable(new String(bytes), bytes);
}
public int compareTo(Variable variable) {
return arrayCompare(getBytes(), variable.getBytes());
}
public boolean equals(Object o) {
return o instanceof Variable && super.equals(o);
}
}
private static class Value
extends ExternalData implements Comparable<Value>
{
protected Value(String str, byte[] bytes) {
super(str, bytes);
}
public static Value valueOfQueryOnly(Object str) {
return valueOfQueryOnly((String) str);
}
public static Value valueOfQueryOnly(String str) {
return new Value(str, str.getBytes());
}
public static Value valueOf(String str) {
validateValue(str);
return valueOfQueryOnly(str);
}
public static Value valueOf(byte[] bytes) {
return new Value(new String(bytes), bytes);
}
public int compareTo(Value value) {
return arrayCompare(getBytes(), value.getBytes());
}
public boolean equals(Object o) {
return o instanceof Value && super.equals(o);
}
}
// This implements the String map view the user sees.
private static class StringEnvironment
extends AbstractMap<String,String>
{
private Map<Variable,Value> m;
private static String toString(Value v) {
return v == null ? null : v.toString();
}
public StringEnvironment(Map<Variable,Value> m) {this.m = m;}
public int size() {return m.size();}
public boolean isEmpty() {return m.isEmpty();}
public void clear() { m.clear();}
public boolean containsKey(Object key) {
return m.containsKey(Variable.valueOfQueryOnly(key));
}
public boolean containsValue(Object value) {
return m.containsValue(Value.valueOfQueryOnly(value));
}
public String get(Object key) {
return toString(m.get(Variable.valueOfQueryOnly(key)));
}
public String put(String key, String value) {
return toString(m.put(Variable.valueOf(key),
Value.valueOf(value)));
}
public String remove(Object key) {
return toString(m.remove(Variable.valueOfQueryOnly(key)));
}
public Set<String> keySet() {
return new StringKeySet(m.keySet());
}
public Set<Map.Entry<String,String>> entrySet() {
return new StringEntrySet(m.entrySet());
}
public Collection<String> values() {
return new StringValues(m.values());
}
// It is technically feasible to provide a byte-oriented view
// as follows:
// public Map<byte[],byte[]> asByteArrayMap() {
// return new ByteArrayEnvironment(m);
// }
// Convert to Unix style environ as a monolithic byte array
// inspired by the Windows Environment Block, except we work
// exclusively with bytes instead of chars, and we need only
// one trailing NUL on Unix.
// This keeps the JNI as simple and efficient as possible.
public byte[] toEnvironmentBlock(int[]envc) {
int count = m.size() * 2; // For added '=' and NUL
for (Map.Entry<Variable,Value> entry : m.entrySet()) {
count += entry.getKey().getBytes().length;
count += entry.getValue().getBytes().length;
}
byte[] block = new byte[count];
int i = 0;
for (Map.Entry<Variable,Value> entry : m.entrySet()) {
byte[] key = entry.getKey ().getBytes();
byte[] value = entry.getValue().getBytes();
System.arraycopy(key, 0, block, i, key.length);
i+=key.length;
block[i++] = (byte) '=';
System.arraycopy(value, 0, block, i, value.length);
i+=value.length + 1;
// No need to write NUL byte explicitly
//block[i++] = (byte) '\u0000';
}
envc[0] = m.size();
return block;
}
}
static byte[] toEnvironmentBlock(Map<String,String> map, int[]envc) {
return map == null ? null :
((StringEnvironment)map).toEnvironmentBlock(envc);
}
private static class StringEntry
implements Map.Entry<String,String>
{
private final Map.Entry<Variable,Value> e;
public StringEntry(Map.Entry<Variable,Value> e) {this.e = e;}
public String getKey() {return e.getKey().toString();}
public String getValue() {return e.getValue().toString();}
public String setValue(String newValue) {
return e.setValue(Value.valueOf(newValue)).toString();
}
public String toString() {return getKey() + "=" + getValue();}
public boolean equals(Object o) {
return o instanceof StringEntry
&& e.equals(((StringEntry)o).e);
}
public int hashCode() {return e.hashCode();}
}
private static class StringEntrySet
extends AbstractSet<Map.Entry<String,String>>
{
private final Set<Map.Entry<Variable,Value>> s;
public StringEntrySet(Set<Map.Entry<Variable,Value>> s) {this.s = s;}
public int size() {return s.size();}
public boolean isEmpty() {return s.isEmpty();}
public void clear() { s.clear();}
public Iterator<Map.Entry<String,String>> iterator() {
return new Iterator<Map.Entry<String,String>>() {
Iterator<Map.Entry<Variable,Value>> i = s.iterator();
public boolean hasNext() {return i.hasNext();}
public Map.Entry<String,String> next() {
return new StringEntry(i.next());
}
public void remove() {i.remove();}
};
}
private static Map.Entry<Variable,Value> vvEntry(final Object o) {
if (o instanceof StringEntry)
return ((StringEntry)o).e;
return new Map.Entry<Variable,Value>() {
public Variable getKey() {
return Variable.valueOfQueryOnly(((Map.Entry)o).getKey());
}
public Value getValue() {
return Value.valueOfQueryOnly(((Map.Entry)o).getValue());
}
public Value setValue(Value value) {
throw new UnsupportedOperationException();
}
};
}
public boolean contains(Object o) { return s.contains(vvEntry(o)); }
public boolean remove(Object o) { return s.remove(vvEntry(o)); }
public boolean equals(Object o) {
return o instanceof StringEntrySet
&& s.equals(((StringEntrySet) o).s);
}
public int hashCode() {return s.hashCode();}
}
private static class StringValues
extends AbstractCollection<String>
{
private final Collection<Value> c;
public StringValues(Collection<Value> c) {this.c = c;}
public int size() {return c.size();}
public boolean isEmpty() {return c.isEmpty();}
public void clear() { c.clear();}
public Iterator<String> iterator() {
return new Iterator<String>() {
Iterator<Value> i = c.iterator();
public boolean hasNext() {return i.hasNext();}
public String next() {return i.next().toString();}
public void remove() {i.remove();}
};
}
public boolean contains(Object o) {
return c.contains(Value.valueOfQueryOnly(o));
}
public boolean remove(Object o) {
return c.remove(Value.valueOfQueryOnly(o));
}
public boolean equals(Object o) {
return o instanceof StringValues
&& c.equals(((StringValues)o).c);
}
public int hashCode() {return c.hashCode();}
}
private static class StringKeySet extends AbstractSet<String> {
private final Set<Variable> s;
public StringKeySet(Set<Variable> s) {this.s = s;}
public int size() {return s.size();}
public boolean isEmpty() {return s.isEmpty();}
public void clear() { s.clear();}
public Iterator<String> iterator() {
return new Iterator<String>() {
Iterator<Variable> i = s.iterator();
public boolean hasNext() {return i.hasNext();}
public String next() {return i.next().toString();}
public void remove() { i.remove();}
};
}
public boolean contains(Object o) {
return s.contains(Variable.valueOfQueryOnly(o));
}
public boolean remove(Object o) {
return s.remove(Variable.valueOfQueryOnly(o));
}
}
// Replace with general purpose method someday
private static int arrayCompare(byte[]x, byte[] y) {
int min = x.length < y.length ? x.length : y.length;
for (int i = 0; i < min; i++)
if (x[i] != y[i])
return x[i] - y[i];
return x.length - y.length;
}
// Replace with general purpose method someday
private static boolean arrayEquals(byte[] x, byte[] y) {
if (x.length != y.length)
return false;
for (int i = 0; i < x.length; i++)
if (x[i] != y[i])
return false;
return true;
}
// Replace with general purpose method someday
private static int arrayHash(byte[] x) {
int hash = 0;
for (int i = 0; i < x.length; i++)
hash = 31 * hash + x[i];
return hash;
}
}

View file

@ -0,0 +1,978 @@
/*
* Copyright (c) 2003, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.lang;
import java.lang.ProcessBuilder.Redirect;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.Locale;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.security.AccessController;
import static java.security.AccessController.doPrivileged;
import java.security.PrivilegedAction;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.Properties;
import jdk.internal.misc.JavaIOFileDescriptorAccess;
import jdk.internal.misc.SharedSecrets;
import sun.security.action.GetPropertyAction;
/**
* java.lang.Process subclass in the UNIX environment.
*
* @author Mario Wolczko and Ross Knippel.
* @author Konstantin Kladko (ported to Linux and Bsd)
* @author Martin Buchholz
* @author Volker Simonis (ported to AIX)
* @since 1.5
*/
final class ProcessImpl extends Process {
private static final JavaIOFileDescriptorAccess fdAccess
= SharedSecrets.getJavaIOFileDescriptorAccess();
// Linux platforms support a normal (non-forcible) kill signal.
static final boolean SUPPORTS_NORMAL_TERMINATION = true;
private final int pid;
private final ProcessHandleImpl processHandle;
private int exitcode;
private boolean hasExited;
private /* final */ OutputStream stdin;
private /* final */ InputStream stdout;
private /* final */ InputStream stderr;
// only used on Solaris
private /* final */ DeferredCloseInputStream stdout_inner_stream;
private static enum LaunchMechanism {
// order IS important!
FORK,
POSIX_SPAWN,
VFORK
}
private static enum Platform {
LINUX(LaunchMechanism.VFORK, LaunchMechanism.FORK),
BSD(LaunchMechanism.POSIX_SPAWN, LaunchMechanism.FORK),
SOLARIS(LaunchMechanism.POSIX_SPAWN, LaunchMechanism.FORK),
AIX(LaunchMechanism.POSIX_SPAWN, LaunchMechanism.FORK);
final LaunchMechanism defaultLaunchMechanism;
final Set<LaunchMechanism> validLaunchMechanisms;
Platform(LaunchMechanism ... launchMechanisms) {
this.defaultLaunchMechanism = launchMechanisms[0];
this.validLaunchMechanisms =
EnumSet.copyOf(Arrays.asList(launchMechanisms));
}
@SuppressWarnings("fallthrough")
private String helperPath(String javahome, String osArch) {
switch (this) {
case SOLARIS:
// fall through...
case LINUX:
case AIX:
case BSD:
return javahome + "/lib/jspawnhelper";
default:
throw new AssertionError("Unsupported platform: " + this);
}
}
String helperPath() {
Properties props = GetPropertyAction.privilegedGetProperties();
return helperPath(props.getProperty("java.home"),
props.getProperty("os.arch"));
}
LaunchMechanism launchMechanism() {
return AccessController.doPrivileged(
(PrivilegedAction<LaunchMechanism>) () -> {
String s = System.getProperty(
"jdk.lang.Process.launchMechanism");
LaunchMechanism lm;
if (s == null) {
lm = defaultLaunchMechanism;
s = lm.name().toLowerCase(Locale.ENGLISH);
} else {
try {
lm = LaunchMechanism.valueOf(
s.toUpperCase(Locale.ENGLISH));
} catch (IllegalArgumentException e) {
lm = null;
}
}
if (lm == null || !validLaunchMechanisms.contains(lm)) {
throw new Error(
s + " is not a supported " +
"process launch mechanism on this platform."
);
}
return lm;
}
);
}
static Platform get() {
String osName = GetPropertyAction.privilegedGetProperty("os.name");
if (osName.equals("Linux")) { return LINUX; }
if (osName.contains("OS X")) { return BSD; }
if (osName.equals("SunOS")) { return SOLARIS; }
if (osName.equals("AIX")) { return AIX; }
throw new Error(osName + " is not a supported OS platform.");
}
}
private static final Platform platform = Platform.get();
private static final LaunchMechanism launchMechanism = platform.launchMechanism();
private static final byte[] helperpath = toCString(platform.helperPath());
private static byte[] toCString(String s) {
if (s == null)
return null;
byte[] bytes = s.getBytes();
byte[] result = new byte[bytes.length + 1];
System.arraycopy(bytes, 0,
result, 0,
bytes.length);
result[result.length-1] = (byte)0;
return result;
}
// Only for use by ProcessBuilder.start()
static Process start(String[] cmdarray,
java.util.Map<String,String> environment,
String dir,
ProcessBuilder.Redirect[] redirects,
boolean redirectErrorStream)
throws IOException
{
assert cmdarray != null && cmdarray.length > 0;
// Convert arguments to a contiguous block; it's easier to do
// memory management in Java than in C.
byte[][] args = new byte[cmdarray.length-1][];
int size = args.length; // For added NUL bytes
for (int i = 0; i < args.length; i++) {
args[i] = cmdarray[i+1].getBytes();
size += args[i].length;
}
byte[] argBlock = new byte[size];
int i = 0;
for (byte[] arg : args) {
System.arraycopy(arg, 0, argBlock, i, arg.length);
i += arg.length + 1;
// No need to write NUL bytes explicitly
}
int[] envc = new int[1];
byte[] envBlock = ProcessEnvironment.toEnvironmentBlock(environment, envc);
int[] std_fds;
FileInputStream f0 = null;
FileOutputStream f1 = null;
FileOutputStream f2 = null;
try {
boolean forceNullOutputStream = false;
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 if (redirects[0] instanceof ProcessBuilder.RedirectPipeImpl) {
std_fds[0] = fdAccess.get(((ProcessBuilder.RedirectPipeImpl) redirects[0]).getFd());
} 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 if (redirects[1] instanceof ProcessBuilder.RedirectPipeImpl) {
std_fds[1] = fdAccess.get(((ProcessBuilder.RedirectPipeImpl) redirects[1]).getFd());
// Force getInputStream to return a null stream,
// the fd is directly assigned to the next process.
forceNullOutputStream = true;
} else {
f1 = new FileOutputStream(redirects[1].file(),
redirects[1].append());
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 if (redirects[2] instanceof ProcessBuilder.RedirectPipeImpl) {
std_fds[2] = fdAccess.get(((ProcessBuilder.RedirectPipeImpl) redirects[2]).getFd());
} else {
f2 = new FileOutputStream(redirects[2].file(),
redirects[2].append());
std_fds[2] = fdAccess.get(f2.getFD());
}
}
Process p = new ProcessImpl
(toCString(cmdarray[0]),
argBlock, args.length,
envBlock, envc[0],
toCString(dir),
std_fds,
forceNullOutputStream,
redirectErrorStream);
if (redirects != null) {
// Copy the fd's if they are to be redirected to another process
if (std_fds[0] >= 0 &&
redirects[0] instanceof ProcessBuilder.RedirectPipeImpl) {
fdAccess.set(((ProcessBuilder.RedirectPipeImpl) redirects[0]).getFd(), std_fds[0]);
}
if (std_fds[1] >= 0 &&
redirects[1] instanceof ProcessBuilder.RedirectPipeImpl) {
fdAccess.set(((ProcessBuilder.RedirectPipeImpl) redirects[1]).getFd(), std_fds[1]);
}
if (std_fds[2] >= 0 &&
redirects[2] instanceof ProcessBuilder.RedirectPipeImpl) {
fdAccess.set(((ProcessBuilder.RedirectPipeImpl) redirects[2]).getFd(), std_fds[2]);
}
}
return p;
} 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(); }
}
}
}
/**
* Creates a process. Depending on the {@code mode} flag, this is done by
* one of the following mechanisms:
* <pre>
* 1 - fork(2) and exec(2)
* 2 - posix_spawn(3P)
* 3 - vfork(2) and exec(2)
* </pre>
* @param fds an array of three 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(int mode, byte[] helperpath,
byte[] prog,
byte[] argBlock, int argc,
byte[] envBlock, int envc,
byte[] dir,
int[] fds,
boolean redirectErrorStream)
throws IOException;
private ProcessImpl(final byte[] prog,
final byte[] argBlock, final int argc,
final byte[] envBlock, final int envc,
final byte[] dir,
final int[] fds,
final boolean forceNullOutputStream,
final boolean redirectErrorStream)
throws IOException {
pid = forkAndExec(launchMechanism.ordinal() + 1,
helperpath,
prog,
argBlock, argc,
envBlock, envc,
dir,
fds,
redirectErrorStream);
processHandle = ProcessHandleImpl.getInternal(pid);
try {
doPrivileged((PrivilegedExceptionAction<Void>) () -> {
initStreams(fds, forceNullOutputStream);
return null;
});
} catch (PrivilegedActionException ex) {
throw (IOException) ex.getException();
}
}
static FileDescriptor newFileDescriptor(int fd) {
FileDescriptor fileDescriptor = new FileDescriptor();
fdAccess.set(fileDescriptor, fd);
return fileDescriptor;
}
/**
* Initialize the streams from the file descriptors.
* @param fds array of stdin, stdout, stderr fds
* @param forceNullOutputStream true if the stdout is being directed to
* a subsequent process. The stdout stream should be a null output stream .
* @throws IOException
*/
void initStreams(int[] fds, boolean forceNullOutputStream) throws IOException {
switch (platform) {
case LINUX:
case BSD:
stdin = (fds[0] == -1) ?
ProcessBuilder.NullOutputStream.INSTANCE :
new ProcessPipeOutputStream(fds[0]);
stdout = (fds[1] == -1 || forceNullOutputStream) ?
ProcessBuilder.NullInputStream.INSTANCE :
new ProcessPipeInputStream(fds[1]);
stderr = (fds[2] == -1) ?
ProcessBuilder.NullInputStream.INSTANCE :
new ProcessPipeInputStream(fds[2]);
ProcessHandleImpl.completion(pid, true).handle((exitcode, throwable) -> {
synchronized (this) {
this.exitcode = (exitcode == null) ? -1 : exitcode.intValue();
this.hasExited = true;
this.notifyAll();
}
if (stdout instanceof ProcessPipeInputStream)
((ProcessPipeInputStream) stdout).processExited();
if (stderr instanceof ProcessPipeInputStream)
((ProcessPipeInputStream) stderr).processExited();
if (stdin instanceof ProcessPipeOutputStream)
((ProcessPipeOutputStream) stdin).processExited();
return null;
});
break;
case SOLARIS:
stdin = (fds[0] == -1) ?
ProcessBuilder.NullOutputStream.INSTANCE :
new BufferedOutputStream(
new FileOutputStream(newFileDescriptor(fds[0])));
stdout = (fds[1] == -1) ?
ProcessBuilder.NullInputStream.INSTANCE :
new BufferedInputStream(
stdout_inner_stream =
new DeferredCloseInputStream(
newFileDescriptor(fds[1])));
stderr = (fds[2] == -1) ?
ProcessBuilder.NullInputStream.INSTANCE :
new DeferredCloseInputStream(newFileDescriptor(fds[2]));
/*
* For each subprocess forked a corresponding reaper task
* is submitted. That task is the only thread which waits
* for the subprocess to terminate and it doesn't hold any
* locks while doing so. This design allows waitFor() and
* exitStatus() to be safely executed in parallel (and they
* need no native code).
*/
ProcessHandleImpl.completion(pid, true).handle((exitcode, throwable) -> {
synchronized (this) {
this.exitcode = (exitcode == null) ? -1 : exitcode.intValue();
this.hasExited = true;
this.notifyAll();
}
return null;
});
break;
case AIX:
stdin = (fds[0] == -1) ?
ProcessBuilder.NullOutputStream.INSTANCE :
new ProcessPipeOutputStream(fds[0]);
stdout = (fds[1] == -1) ?
ProcessBuilder.NullInputStream.INSTANCE :
new DeferredCloseProcessPipeInputStream(fds[1]);
stderr = (fds[2] == -1) ?
ProcessBuilder.NullInputStream.INSTANCE :
new DeferredCloseProcessPipeInputStream(fds[2]);
ProcessHandleImpl.completion(pid, true).handle((exitcode, throwable) -> {
synchronized (this) {
this.exitcode = (exitcode == null) ? -1 : exitcode.intValue();
this.hasExited = true;
this.notifyAll();
}
if (stdout instanceof DeferredCloseProcessPipeInputStream)
((DeferredCloseProcessPipeInputStream) stdout).processExited();
if (stderr instanceof DeferredCloseProcessPipeInputStream)
((DeferredCloseProcessPipeInputStream) stderr).processExited();
if (stdin instanceof ProcessPipeOutputStream)
((ProcessPipeOutputStream) stdin).processExited();
return null;
});
break;
default: throw new AssertionError("Unsupported platform: " + platform);
}
}
public OutputStream getOutputStream() {
return stdin;
}
public InputStream getInputStream() {
return stdout;
}
public InputStream getErrorStream() {
return stderr;
}
public synchronized int waitFor() throws InterruptedException {
while (!hasExited) {
wait();
}
return exitcode;
}
@Override
public synchronized boolean waitFor(long timeout, TimeUnit unit)
throws InterruptedException
{
long remainingNanos = unit.toNanos(timeout); // throw NPE before other conditions
if (hasExited) return true;
if (timeout <= 0) return false;
long deadline = System.nanoTime() + remainingNanos;
do {
// Round up to next millisecond
wait(TimeUnit.NANOSECONDS.toMillis(remainingNanos + 999_999L));
if (hasExited) {
return true;
}
remainingNanos = deadline - System.nanoTime();
} while (remainingNanos > 0);
return hasExited;
}
public synchronized int exitValue() {
if (!hasExited) {
throw new IllegalThreadStateException("process hasn't exited");
}
return exitcode;
}
private void destroy(boolean force) {
switch (platform) {
case LINUX:
case BSD:
case AIX:
// There is a risk that pid will be recycled, causing us to
// kill the wrong process! So we only terminate processes
// that appear to still be running. Even with this check,
// there is an unavoidable race condition here, but the window
// is very small, and OSes try hard to not recycle pids too
// soon, so this is quite safe.
synchronized (this) {
if (!hasExited)
processHandle.destroyProcess(force);
}
try { stdin.close(); } catch (IOException ignored) {}
try { stdout.close(); } catch (IOException ignored) {}
try { stderr.close(); } catch (IOException ignored) {}
break;
case SOLARIS:
// There is a risk that pid will be recycled, causing us to
// kill the wrong process! So we only terminate processes
// that appear to still be running. Even with this check,
// there is an unavoidable race condition here, but the window
// is very small, and OSes try hard to not recycle pids too
// soon, so this is quite safe.
synchronized (this) {
if (!hasExited)
processHandle.destroyProcess(force);
try {
stdin.close();
if (stdout_inner_stream != null)
stdout_inner_stream.closeDeferred(stdout);
if (stderr instanceof DeferredCloseInputStream)
((DeferredCloseInputStream) stderr)
.closeDeferred(stderr);
} catch (IOException e) {
// ignore
}
}
break;
default: throw new AssertionError("Unsupported platform: " + platform);
}
}
@Override
public CompletableFuture<Process> onExit() {
return ProcessHandleImpl.completion(pid, false)
.handleAsync((unusedExitStatus, unusedThrowable) -> {
boolean interrupted = false;
while (true) {
// Ensure that the concurrent task setting the exit status has completed
try {
waitFor();
break;
} catch (InterruptedException ie) {
interrupted = true;
}
}
if (interrupted) {
Thread.currentThread().interrupt();
}
return this;
});
}
@Override
public ProcessHandle toHandle() {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(new RuntimePermission("manageProcess"));
}
return processHandle;
}
@Override
public boolean supportsNormalTermination() {
return ProcessImpl.SUPPORTS_NORMAL_TERMINATION;
}
@Override
public void destroy() {
destroy(false);
}
@Override
public Process destroyForcibly() {
destroy(true);
return this;
}
@Override
public long pid() {
return pid;
}
@Override
public synchronized boolean isAlive() {
return !hasExited;
}
/**
* The {@code toString} method returns a string consisting of
* the native process ID of the process and the exit value of the process.
*
* @return a string representation of the object.
*/
@Override
public String toString() {
return new StringBuilder("Process[pid=").append(pid)
.append(", exitValue=").append(hasExited ? exitcode : "\"not exited\"")
.append("]").toString();
}
private static native void init();
static {
init();
}
/**
* A buffered input stream for a subprocess pipe file descriptor
* that allows the underlying file descriptor to be reclaimed when
* the process exits, via the processExited hook.
*
* This is tricky because we do not want the user-level InputStream to be
* closed until the user invokes close(), and we need to continue to be
* able to read any buffered data lingering in the OS pipe buffer.
*/
private static class ProcessPipeInputStream extends BufferedInputStream {
private final Object closeLock = new Object();
ProcessPipeInputStream(int fd) {
super(new PipeInputStream(newFileDescriptor(fd)));
}
private static byte[] drainInputStream(InputStream in)
throws IOException {
int n = 0;
int j;
byte[] a = null;
while ((j = in.available()) > 0) {
a = (a == null) ? new byte[j] : Arrays.copyOf(a, n + j);
n += in.read(a, n, j);
}
return (a == null || n == a.length) ? a : Arrays.copyOf(a, n);
}
/** Called by the process reaper thread when the process exits. */
synchronized void processExited() {
synchronized (closeLock) {
try {
InputStream in = this.in;
// this stream is closed if and only if: in == null
if (in != null) {
byte[] stragglers = drainInputStream(in);
in.close();
this.in = (stragglers == null) ?
ProcessBuilder.NullInputStream.INSTANCE :
new ByteArrayInputStream(stragglers);
}
} catch (IOException ignored) {}
}
}
@Override
public void close() throws IOException {
// BufferedInputStream#close() is not synchronized unlike most other
// methods. Synchronizing helps avoid race with processExited().
synchronized (closeLock) {
super.close();
}
}
}
/**
* A buffered output stream for a subprocess pipe file descriptor
* that allows the underlying file descriptor to be reclaimed when
* the process exits, via the processExited hook.
*/
private static class ProcessPipeOutputStream extends BufferedOutputStream {
ProcessPipeOutputStream(int fd) {
super(new FileOutputStream(newFileDescriptor(fd)));
}
/** Called by the process reaper thread when the process exits. */
synchronized void processExited() {
OutputStream out = this.out;
if (out != null) {
try {
out.close();
} catch (IOException ignored) {
// We know of no reason to get an IOException, but if
// we do, there's nothing else to do but carry on.
}
this.out = ProcessBuilder.NullOutputStream.INSTANCE;
}
}
}
// A FileInputStream that supports the deferment of the actual close
// operation until the last pending I/O operation on the stream has
// finished. This is required on Solaris because we must close the stdin
// and stdout streams in the destroy method in order to reclaim the
// underlying file descriptors. Doing so, however, causes any thread
// currently blocked in a read on one of those streams to receive an
// IOException("Bad file number"), which is incompatible with historical
// behavior. By deferring the close we allow any pending reads to see -1
// (EOF) as they did before.
//
private static class DeferredCloseInputStream extends PipeInputStream {
DeferredCloseInputStream(FileDescriptor fd) {
super(fd);
}
private Object lock = new Object(); // For the following fields
private boolean closePending = false;
private int useCount = 0;
private InputStream streamToClose;
private void raise() {
synchronized (lock) {
useCount++;
}
}
private void lower() throws IOException {
synchronized (lock) {
useCount--;
if (useCount == 0 && closePending) {
streamToClose.close();
}
}
}
// stc is the actual stream to be closed; it might be this object, or
// it might be an upstream object for which this object is downstream.
//
private void closeDeferred(InputStream stc) throws IOException {
synchronized (lock) {
if (useCount == 0) {
stc.close();
} else {
closePending = true;
streamToClose = stc;
}
}
}
public void close() throws IOException {
synchronized (lock) {
useCount = 0;
closePending = false;
}
super.close();
}
public int read() throws IOException {
raise();
try {
return super.read();
} finally {
lower();
}
}
public int read(byte[] b) throws IOException {
raise();
try {
return super.read(b);
} finally {
lower();
}
}
public int read(byte[] b, int off, int len) throws IOException {
raise();
try {
return super.read(b, off, len);
} finally {
lower();
}
}
public long skip(long n) throws IOException {
raise();
try {
return super.skip(n);
} finally {
lower();
}
}
public int available() throws IOException {
raise();
try {
return super.available();
} finally {
lower();
}
}
}
/**
* A buffered input stream for a subprocess pipe file descriptor
* that allows the underlying file descriptor to be reclaimed when
* the process exits, via the processExited hook.
*
* This is tricky because we do not want the user-level InputStream to be
* closed until the user invokes close(), and we need to continue to be
* able to read any buffered data lingering in the OS pipe buffer.
*
* On AIX this is especially tricky, because the 'close()' system call
* will block if another thread is at the same time blocked in a file
* operation (e.g. 'read()') on the same file descriptor. We therefore
* combine 'ProcessPipeInputStream' approach used on Linux and Bsd
* with the DeferredCloseInputStream approach used on Solaris. This means
* that every potentially blocking operation on the file descriptor
* increments a counter before it is executed and decrements it once it
* finishes. The 'close()' operation will only be executed if there are
* no pending operations. Otherwise it is deferred after the last pending
* operation has finished.
*
*/
private static class DeferredCloseProcessPipeInputStream
extends BufferedInputStream {
private final Object closeLock = new Object();
private int useCount = 0;
private boolean closePending = false;
DeferredCloseProcessPipeInputStream(int fd) {
super(new PipeInputStream(newFileDescriptor(fd)));
}
private InputStream drainInputStream(InputStream in)
throws IOException {
int n = 0;
int j;
byte[] a = null;
synchronized (closeLock) {
if (buf == null) // asynchronous close()?
return null; // discard
j = in.available();
}
while (j > 0) {
a = (a == null) ? new byte[j] : Arrays.copyOf(a, n + j);
synchronized (closeLock) {
if (buf == null) // asynchronous close()?
return null; // discard
n += in.read(a, n, j);
j = in.available();
}
}
return (a == null) ?
ProcessBuilder.NullInputStream.INSTANCE :
new ByteArrayInputStream(n == a.length ? a : Arrays.copyOf(a, n));
}
/** Called by the process reaper thread when the process exits. */
synchronized void processExited() {
try {
InputStream in = this.in;
if (in != null) {
InputStream stragglers = drainInputStream(in);
in.close();
this.in = stragglers;
}
} catch (IOException ignored) { }
}
private void raise() {
synchronized (closeLock) {
useCount++;
}
}
private void lower() throws IOException {
synchronized (closeLock) {
useCount--;
if (useCount == 0 && closePending) {
closePending = false;
super.close();
}
}
}
@Override
public int read() throws IOException {
raise();
try {
return super.read();
} finally {
lower();
}
}
@Override
public int read(byte[] b) throws IOException {
raise();
try {
return super.read(b);
} finally {
lower();
}
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
raise();
try {
return super.read(b, off, len);
} finally {
lower();
}
}
@Override
public long skip(long n) throws IOException {
raise();
try {
return super.skip(n);
} finally {
lower();
}
}
@Override
public int available() throws IOException {
raise();
try {
return super.available();
} finally {
lower();
}
}
@Override
public void close() throws IOException {
// BufferedInputStream#close() is not synchronized unlike most other
// methods. Synchronizing helps avoid racing with drainInputStream().
synchronized (closeLock) {
if (useCount == 0) {
super.close();
}
else {
closePending = true;
}
}
}
}
}

View file

@ -0,0 +1,78 @@
/*
* Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.lang;
import jdk.internal.misc.Signal;
/**
* Package-private utility class for setting up and tearing down
* platform-specific support for termination-triggered shutdowns.
*
* @author Mark Reinhold
* @since 1.3
*/
class Terminator {
private static Signal.Handler handler = null;
/* Invocations of setup and teardown are already synchronized
* on the shutdown lock, so no further synchronization is needed here
*/
static void setup() {
if (handler != null) return;
Signal.Handler sh = new Signal.Handler() {
public void handle(Signal sig) {
Shutdown.exit(sig.getNumber() + 0200);
}
};
handler = sh;
// When -Xrs is specified the user is responsible for
// ensuring that shutdown hooks are run by calling
// System.exit()
try {
Signal.handle(new Signal("HUP"), sh);
} catch (IllegalArgumentException e) {
}
try {
Signal.handle(new Signal("INT"), sh);
} catch (IllegalArgumentException e) {
}
try {
Signal.handle(new Signal("TERM"), sh);
} catch (IllegalArgumentException e) {
}
}
static void teardown() {
/* The current sun.misc.Signal class does not support
* the cancellation of handlers
*/
}
}

View file

@ -0,0 +1,75 @@
/*
* Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.net;
import sun.security.action.GetPropertyAction;
/**
* This class defines a factory for creating DatagramSocketImpls. It defaults
* to creating plain DatagramSocketImpls, but may create other DatagramSocketImpls
* by setting the impl.prefix system property.
*
* @author Chris Hegarty
*/
class DefaultDatagramSocketImplFactory {
static Class<?> prefixImplClass = null;
static {
String prefix = null;
try {
prefix = GetPropertyAction.privilegedGetProperty("impl.prefix");
if (prefix != null)
prefixImplClass = Class.forName("java.net."+prefix+"DatagramSocketImpl");
} catch (Exception e) {
System.err.println("Can't find class: java.net." +
prefix +
"DatagramSocketImpl: check impl.prefix property");
//prefixImplClass = null;
}
}
/**
* Creates a new <code>DatagramSocketImpl</code> instance.
*
* @param isMulticast true if this impl if for a MutlicastSocket
* @return a new instance of a <code>DatagramSocketImpl</code>.
*/
static DatagramSocketImpl createDatagramSocketImpl(boolean isMulticast /*unused on unix*/)
throws SocketException {
if (prefixImplClass != null) {
try {
@SuppressWarnings("deprecation")
DatagramSocketImpl result = (DatagramSocketImpl)prefixImplClass.newInstance();
return result;
} catch (Exception e) {
throw new SocketException("can't instantiate DatagramSocketImpl");
}
} else {
return new java.net.PlainDatagramSocketImpl();
}
}
}

View file

@ -0,0 +1,41 @@
/*
* Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.net;
/**
* Choose a network interface to be the default for
* outgoing IPv6 traffic that does not specify a scope_id (and which needs one).
*
* Platforms that do not require a default interface may return null
* which is what this implementation does.
*/
class DefaultInterface {
static NetworkInterface getDefault() {
return null;
}
}

View file

@ -0,0 +1,152 @@
/*
* Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.net;
import java.io.IOException;
import java.util.Set;
import java.util.HashSet;
import sun.net.ext.ExtendedSocketOptions;
/*
* On Unix systems we simply delegate to native methods.
*
* @author Chris Hegarty
*/
class PlainDatagramSocketImpl extends AbstractPlainDatagramSocketImpl
{
static {
init();
}
static final ExtendedSocketOptions extendedOptions =
ExtendedSocketOptions.getInstance();
protected <T> void setOption(SocketOption<T> name, T value) throws IOException {
if (!extendedOptions.isOptionSupported(name)) {
if (!name.equals(StandardSocketOptions.SO_REUSEPORT)) {
super.setOption(name, value);
} else {
if (supportedOptions().contains(name)) {
super.setOption(name, value);
} else {
throw new UnsupportedOperationException("unsupported option");
}
}
} else {
if (isClosed()) {
throw new SocketException("Socket closed");
}
extendedOptions.setOption(fd, name, value);
}
}
@SuppressWarnings("unchecked")
protected <T> T getOption(SocketOption<T> name) throws IOException {
if (!extendedOptions.isOptionSupported(name)) {
if (!name.equals(StandardSocketOptions.SO_REUSEPORT)) {
return super.getOption(name);
} else {
if (supportedOptions().contains(name)) {
return super.getOption(name);
} else {
throw new UnsupportedOperationException("unsupported option");
}
}
} else {
if (isClosed()) {
throw new SocketException("Socket closed");
}
return (T) extendedOptions.getOption(fd, name);
}
}
protected Set<SocketOption<?>> supportedOptions() {
HashSet<SocketOption<?>> options = new HashSet<>(super.supportedOptions());
options.addAll(extendedOptions.options());
return options;
}
protected void socketSetOption(int opt, Object val) throws SocketException {
if (opt == SocketOptions.SO_REUSEPORT &&
!supportedOptions().contains(StandardSocketOptions.SO_REUSEPORT)) {
throw new UnsupportedOperationException("unsupported option");
}
try {
socketSetOption0(opt, val);
} catch (SocketException se) {
if (!connected)
throw se;
}
}
protected synchronized native void bind0(int lport, InetAddress laddr)
throws SocketException;
protected native void send(DatagramPacket p) throws IOException;
protected synchronized native int peek(InetAddress i) throws IOException;
protected synchronized native int peekData(DatagramPacket p) throws IOException;
protected synchronized native void receive0(DatagramPacket p)
throws IOException;
protected native void setTimeToLive(int ttl) throws IOException;
protected native int getTimeToLive() throws IOException;
@Deprecated
protected native void setTTL(byte ttl) throws IOException;
@Deprecated
protected native byte getTTL() throws IOException;
protected native void join(InetAddress inetaddr, NetworkInterface netIf)
throws IOException;
protected native void leave(InetAddress inetaddr, NetworkInterface netIf)
throws IOException;
protected native void datagramSocketCreate() throws SocketException;
protected native void datagramSocketClose();
protected native void socketSetOption0(int opt, Object val)
throws SocketException;
protected native Object socketGetOption(int opt) throws SocketException;
protected native void connect0(InetAddress address, int port) throws SocketException;
protected native void disconnect0(int family);
native int dataAvailable();
/**
* Perform class load-time initializations.
*/
private static native void init();
}

View file

@ -0,0 +1,152 @@
/*
* Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.net;
import java.io.IOException;
import java.io.FileDescriptor;
import java.util.Set;
import java.util.HashSet;
import sun.net.ext.ExtendedSocketOptions;
/*
* On Unix systems we simply delegate to native methods.
*
* @author Chris Hegarty
*/
class PlainSocketImpl extends AbstractPlainSocketImpl
{
static {
initProto();
}
/**
* Constructs an empty instance.
*/
PlainSocketImpl() { }
/**
* Constructs an instance with the given file descriptor.
*/
PlainSocketImpl(FileDescriptor fd) {
this.fd = fd;
}
static final ExtendedSocketOptions extendedOptions =
ExtendedSocketOptions.getInstance();
protected <T> void setOption(SocketOption<T> name, T value) throws IOException {
if (!extendedOptions.isOptionSupported(name)) {
if (!name.equals(StandardSocketOptions.SO_REUSEPORT)) {
super.setOption(name, value);
} else {
if (supportedOptions().contains(name)) {
super.setOption(name, value);
} else {
throw new UnsupportedOperationException("unsupported option");
}
}
} else {
if (getSocket() == null) {
throw new UnsupportedOperationException("unsupported option");
}
if (isClosedOrPending()) {
throw new SocketException("Socket closed");
}
extendedOptions.setOption(fd, name, value);
}
}
@SuppressWarnings("unchecked")
protected <T> T getOption(SocketOption<T> name) throws IOException {
if (!extendedOptions.isOptionSupported(name)) {
if (!name.equals(StandardSocketOptions.SO_REUSEPORT)) {
return super.getOption(name);
} else {
if (supportedOptions().contains(name)) {
return super.getOption(name);
} else {
throw new UnsupportedOperationException("unsupported option");
}
}
} else {
if (getSocket() == null) {
throw new UnsupportedOperationException("unsupported option");
}
if (isClosedOrPending()) {
throw new SocketException("Socket closed");
}
return (T) extendedOptions.getOption(fd, name);
}
}
protected Set<SocketOption<?>> supportedOptions() {
HashSet<SocketOption<?>> options = new HashSet<>(super.supportedOptions());
if (getSocket() != null) {
options.addAll(extendedOptions.options());
}
return options;
}
protected void socketSetOption(int opt, boolean b, Object val) throws SocketException {
if (opt == SocketOptions.SO_REUSEPORT &&
!supportedOptions().contains(StandardSocketOptions.SO_REUSEPORT)) {
throw new UnsupportedOperationException("unsupported option");
}
try {
socketSetOption0(opt, b, val);
} catch (SocketException se) {
if (socket == null || !socket.isConnected())
throw se;
}
}
native void socketCreate(boolean isServer) throws IOException;
native void socketConnect(InetAddress address, int port, int timeout)
throws IOException;
native void socketBind(InetAddress address, int port)
throws IOException;
native void socketListen(int count) throws IOException;
native void socketAccept(SocketImpl s) throws IOException;
native int socketAvailable() throws IOException;
native void socketClose0(boolean useDeferredClose) throws IOException;
native void socketShutdown(int howto) throws IOException;
static native void initProto();
native void socketSetOption0(int cmd, boolean on, Object value)
throws SocketException;
native int socketGetOption(int opt, Object iaContainerObj) throws SocketException;
native void socketSendUrgentData(int data) throws IOException;
}