Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/solaris/classes/java/lang/ProcessEnvironment.java
32287 views
/*1* Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425/* We use APIs that access the standard Unix environ array, which26* is defined by UNIX98 to look like:27*28* char **environ;29*30* These are unsorted, case-sensitive, null-terminated arrays of bytes31* of the form FOO=BAR\000 which are usually encoded in the user's32* default encoding (file.encoding is an excellent choice for33* encoding/decoding these). However, even though the user cannot34* directly access the underlying byte representation, we take pains35* to pass on the child the exact byte representation we inherit from36* the parent process for any environment name or value not created by37* Javaland. So we keep track of all the byte representations.38*39* Internally, we define the types Variable and Value that exhibit40* String/byteArray duality. The internal representation of the41* environment then looks like a Map<Variable,Value>. But we don't42* expose this to the user -- we only provide a Map<String,String>43* view, although we could also provide a Map<byte[],byte[]> view.44*45* The non-private methods in this class are not for general use even46* within this package. Instead, they are the system-dependent parts47* of the system-independent method of the same name. Don't even48* think of using this class unless your method's name appears below.49*50* @author Martin Buchholz51* @since 1.552*/5354package java.lang;5556import java.io.*;57import java.util.*;585960final class ProcessEnvironment61{62private static final HashMap<Variable,Value> theEnvironment;63private static final Map<String,String> theUnmodifiableEnvironment;64static final int MIN_NAME_LENGTH = 0;6566static {67// We cache the C environment. This means that subsequent calls68// to putenv/setenv from C will not be visible from Java code.69byte[][] environ = environ();70theEnvironment = new HashMap<>(environ.length/2 + 3);71// Read environment variables back to front,72// so that earlier variables override later ones.73for (int i = environ.length-1; i > 0; i-=2)74theEnvironment.put(Variable.valueOf(environ[i-1]),75Value.valueOf(environ[i]));7677theUnmodifiableEnvironment78= Collections.unmodifiableMap79(new StringEnvironment(theEnvironment));80}8182/* Only for use by System.getenv(String) */83static String getenv(String name) {84return theUnmodifiableEnvironment.get(name);85}8687/* Only for use by System.getenv() */88static Map<String,String> getenv() {89return theUnmodifiableEnvironment;90}9192/* Only for use by ProcessBuilder.environment() */93@SuppressWarnings("unchecked")94static Map<String,String> environment() {95return new StringEnvironment96((Map<Variable,Value>)(theEnvironment.clone()));97}9899/* Only for use by Runtime.exec(...String[]envp...) */100static Map<String,String> emptyEnvironment(int capacity) {101return new StringEnvironment(new HashMap<Variable,Value>(capacity));102}103104private static native byte[][] environ();105106// This class is not instantiable.107private ProcessEnvironment() {}108109// Check that name is suitable for insertion into Environment map110private static void validateVariable(String name) {111if (name.indexOf('=') != -1 ||112name.indexOf('\u0000') != -1)113throw new IllegalArgumentException114("Invalid environment variable name: \"" + name + "\"");115}116117// Check that value is suitable for insertion into Environment map118private static void validateValue(String value) {119if (value.indexOf('\u0000') != -1)120throw new IllegalArgumentException121("Invalid environment variable value: \"" + value + "\"");122}123124// A class hiding the byteArray-String duality of125// text data on Unixoid operating systems.126private static abstract class ExternalData {127protected final String str;128protected final byte[] bytes;129130protected ExternalData(String str, byte[] bytes) {131this.str = str;132this.bytes = bytes;133}134135public byte[] getBytes() {136return bytes;137}138139public String toString() {140return str;141}142143public boolean equals(Object o) {144return o instanceof ExternalData145&& arrayEquals(getBytes(), ((ExternalData) o).getBytes());146}147148public int hashCode() {149return arrayHash(getBytes());150}151}152153private static class Variable154extends ExternalData implements Comparable<Variable>155{156protected Variable(String str, byte[] bytes) {157super(str, bytes);158}159160public static Variable valueOfQueryOnly(Object str) {161return valueOfQueryOnly((String) str);162}163164public static Variable valueOfQueryOnly(String str) {165return new Variable(str, str.getBytes());166}167168public static Variable valueOf(String str) {169validateVariable(str);170return valueOfQueryOnly(str);171}172173public static Variable valueOf(byte[] bytes) {174return new Variable(new String(bytes), bytes);175}176177public int compareTo(Variable variable) {178return arrayCompare(getBytes(), variable.getBytes());179}180181public boolean equals(Object o) {182return o instanceof Variable && super.equals(o);183}184}185186private static class Value187extends ExternalData implements Comparable<Value>188{189protected Value(String str, byte[] bytes) {190super(str, bytes);191}192193public static Value valueOfQueryOnly(Object str) {194return valueOfQueryOnly((String) str);195}196197public static Value valueOfQueryOnly(String str) {198return new Value(str, str.getBytes());199}200201public static Value valueOf(String str) {202validateValue(str);203return valueOfQueryOnly(str);204}205206public static Value valueOf(byte[] bytes) {207return new Value(new String(bytes), bytes);208}209210public int compareTo(Value value) {211return arrayCompare(getBytes(), value.getBytes());212}213214public boolean equals(Object o) {215return o instanceof Value && super.equals(o);216}217}218219// This implements the String map view the user sees.220private static class StringEnvironment221extends AbstractMap<String,String>222{223private Map<Variable,Value> m;224private static String toString(Value v) {225return v == null ? null : v.toString();226}227public StringEnvironment(Map<Variable,Value> m) {this.m = m;}228public int size() {return m.size();}229public boolean isEmpty() {return m.isEmpty();}230public void clear() { m.clear();}231public boolean containsKey(Object key) {232return m.containsKey(Variable.valueOfQueryOnly(key));233}234public boolean containsValue(Object value) {235return m.containsValue(Value.valueOfQueryOnly(value));236}237public String get(Object key) {238return toString(m.get(Variable.valueOfQueryOnly(key)));239}240public String put(String key, String value) {241return toString(m.put(Variable.valueOf(key),242Value.valueOf(value)));243}244public String remove(Object key) {245return toString(m.remove(Variable.valueOfQueryOnly(key)));246}247public Set<String> keySet() {248return new StringKeySet(m.keySet());249}250public Set<Map.Entry<String,String>> entrySet() {251return new StringEntrySet(m.entrySet());252}253public Collection<String> values() {254return new StringValues(m.values());255}256257// It is technically feasible to provide a byte-oriented view258// as follows:259// public Map<byte[],byte[]> asByteArrayMap() {260// return new ByteArrayEnvironment(m);261// }262263264// Convert to Unix style environ as a monolithic byte array265// inspired by the Windows Environment Block, except we work266// exclusively with bytes instead of chars, and we need only267// one trailing NUL on Unix.268// This keeps the JNI as simple and efficient as possible.269public byte[] toEnvironmentBlock(int[]envc) {270int count = m.size() * 2; // For added '=' and NUL271for (Map.Entry<Variable,Value> entry : m.entrySet()) {272count += entry.getKey().getBytes().length;273count += entry.getValue().getBytes().length;274}275276byte[] block = new byte[count];277278int i = 0;279for (Map.Entry<Variable,Value> entry : m.entrySet()) {280byte[] key = entry.getKey ().getBytes();281byte[] value = entry.getValue().getBytes();282System.arraycopy(key, 0, block, i, key.length);283i+=key.length;284block[i++] = (byte) '=';285System.arraycopy(value, 0, block, i, value.length);286i+=value.length + 1;287// No need to write NUL byte explicitly288//block[i++] = (byte) '\u0000';289}290envc[0] = m.size();291return block;292}293}294295static byte[] toEnvironmentBlock(Map<String,String> map, int[]envc) {296return map == null ? null :297((StringEnvironment)map).toEnvironmentBlock(envc);298}299300301private static class StringEntry302implements Map.Entry<String,String>303{304private final Map.Entry<Variable,Value> e;305public StringEntry(Map.Entry<Variable,Value> e) {this.e = e;}306public String getKey() {return e.getKey().toString();}307public String getValue() {return e.getValue().toString();}308public String setValue(String newValue) {309return e.setValue(Value.valueOf(newValue)).toString();310}311public String toString() {return getKey() + "=" + getValue();}312public boolean equals(Object o) {313return o instanceof StringEntry314&& e.equals(((StringEntry)o).e);315}316public int hashCode() {return e.hashCode();}317}318319private static class StringEntrySet320extends AbstractSet<Map.Entry<String,String>>321{322private final Set<Map.Entry<Variable,Value>> s;323public StringEntrySet(Set<Map.Entry<Variable,Value>> s) {this.s = s;}324public int size() {return s.size();}325public boolean isEmpty() {return s.isEmpty();}326public void clear() { s.clear();}327public Iterator<Map.Entry<String,String>> iterator() {328return new Iterator<Map.Entry<String,String>>() {329Iterator<Map.Entry<Variable,Value>> i = s.iterator();330public boolean hasNext() {return i.hasNext();}331public Map.Entry<String,String> next() {332return new StringEntry(i.next());333}334public void remove() {i.remove();}335};336}337private static Map.Entry<Variable,Value> vvEntry(final Object o) {338if (o instanceof StringEntry)339return ((StringEntry)o).e;340return new Map.Entry<Variable,Value>() {341public Variable getKey() {342return Variable.valueOfQueryOnly(((Map.Entry)o).getKey());343}344public Value getValue() {345return Value.valueOfQueryOnly(((Map.Entry)o).getValue());346}347public Value setValue(Value value) {348throw new UnsupportedOperationException();349}350};351}352public boolean contains(Object o) { return s.contains(vvEntry(o)); }353public boolean remove(Object o) { return s.remove(vvEntry(o)); }354public boolean equals(Object o) {355return o instanceof StringEntrySet356&& s.equals(((StringEntrySet) o).s);357}358public int hashCode() {return s.hashCode();}359}360361private static class StringValues362extends AbstractCollection<String>363{364private final Collection<Value> c;365public StringValues(Collection<Value> c) {this.c = c;}366public int size() {return c.size();}367public boolean isEmpty() {return c.isEmpty();}368public void clear() { c.clear();}369public Iterator<String> iterator() {370return new Iterator<String>() {371Iterator<Value> i = c.iterator();372public boolean hasNext() {return i.hasNext();}373public String next() {return i.next().toString();}374public void remove() {i.remove();}375};376}377public boolean contains(Object o) {378return c.contains(Value.valueOfQueryOnly(o));379}380public boolean remove(Object o) {381return c.remove(Value.valueOfQueryOnly(o));382}383public boolean equals(Object o) {384return o instanceof StringValues385&& c.equals(((StringValues)o).c);386}387public int hashCode() {return c.hashCode();}388}389390private static class StringKeySet extends AbstractSet<String> {391private final Set<Variable> s;392public StringKeySet(Set<Variable> s) {this.s = s;}393public int size() {return s.size();}394public boolean isEmpty() {return s.isEmpty();}395public void clear() { s.clear();}396public Iterator<String> iterator() {397return new Iterator<String>() {398Iterator<Variable> i = s.iterator();399public boolean hasNext() {return i.hasNext();}400public String next() {return i.next().toString();}401public void remove() { i.remove();}402};403}404public boolean contains(Object o) {405return s.contains(Variable.valueOfQueryOnly(o));406}407public boolean remove(Object o) {408return s.remove(Variable.valueOfQueryOnly(o));409}410}411412// Replace with general purpose method someday413private static int arrayCompare(byte[]x, byte[] y) {414int min = x.length < y.length ? x.length : y.length;415for (int i = 0; i < min; i++)416if (x[i] != y[i])417return x[i] - y[i];418return x.length - y.length;419}420421// Replace with general purpose method someday422private static boolean arrayEquals(byte[] x, byte[] y) {423if (x.length != y.length)424return false;425for (int i = 0; i < x.length; i++)426if (x[i] != y[i])427return false;428return true;429}430431// Replace with general purpose method someday432private static int arrayHash(byte[] x) {433int hash = 0;434for (int i = 0; i < x.length; i++)435hash = 31 * hash + x[i];436return hash;437}438439}440441442