001: /*
002: * Copyright 2003-2004 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: /* We use APIs that access the standard Unix environ array, which
027: * is defined by UNIX98 to look like:
028: *
029: * char **environ;
030: *
031: * These are unsorted, case-sensitive, null-terminated arrays of bytes
032: * of the form FOO=BAR\000 which are usually encoded in the user's
033: * default encoding (file.encoding is an excellent choice for
034: * encoding/decoding these). However, even though the user cannot
035: * directly access the underlying byte representation, we take pains
036: * to pass on the child the exact byte representation we inherit from
037: * the parent process for any environment name or value not created by
038: * Javaland. So we keep track of all the byte representations.
039: *
040: * Internally, we define the types Variable and Value that exhibit
041: * String/byteArray duality. The internal representation of the
042: * environment then looks like a Map<Variable,Value>. But we don't
043: * expose this to the user -- we only provide a Map<String,String>
044: * view, although we could also provide a Map<byte[],byte[]> view.
045: *
046: * The non-private methods in this class are not for general use even
047: * within this package. Instead, they are the system-dependent parts
048: * of the system-independent method of the same name. Don't even
049: * think of using this class unless your method's name appears below.
050: *
051: * @author Martin Buchholz
052: * @version 1.11, 07/05/05
053: * @since 1.5
054: */
055:
056: package java.lang;
057:
058: import java.io.*;
059: import java.util.*;
060:
061: final class ProcessEnvironment {
062: private static final HashMap<Variable, Value> theEnvironment;
063: private static final Map<String, String> theUnmodifiableEnvironment;
064: static final int MIN_NAME_LENGTH = 0;
065:
066: static {
067: // We cache the C environment. This means that subsequent calls
068: // to putenv/setenv from C will not be visible from Java code.
069: byte[][] environ = environ();
070: theEnvironment = new HashMap<Variable, Value>(
071: environ.length / 2 + 3);
072: // Read environment variables back to front,
073: // so that earlier variables override later ones.
074: for (int i = environ.length - 1; i > 0; i -= 2)
075: theEnvironment.put(Variable.valueOf(environ[i - 1]), Value
076: .valueOf(environ[i]));
077:
078: theUnmodifiableEnvironment = Collections
079: .unmodifiableMap(new StringEnvironment(theEnvironment));
080: }
081:
082: /* Only for use by System.getenv(String) */
083: static String getenv(String name) {
084: return theUnmodifiableEnvironment.get(name);
085: }
086:
087: /* Only for use by System.getenv() */
088: static Map<String, String> getenv() {
089: return theUnmodifiableEnvironment;
090: }
091:
092: /* Only for use by ProcessBuilder.environment() */
093: static Map<String, String> environment() {
094: return new StringEnvironment(
095: (Map<Variable, Value>) (theEnvironment.clone()));
096: }
097:
098: /* Only for use by Runtime.exec(...String[]envp...) */
099: static Map<String, String> emptyEnvironment(int capacity) {
100: return new StringEnvironment(new HashMap<Variable, Value>(
101: capacity));
102: }
103:
104: private static native byte[][] environ();
105:
106: // This class is not instantiable.
107: private ProcessEnvironment() {
108: }
109:
110: // Check that name is suitable for insertion into Environment map
111: private static void validateVariable(String name) {
112: if (name.indexOf('=') != -1 || name.indexOf('\u0000') != -1)
113: throw new IllegalArgumentException(
114: "Invalid environment variable name: \"" + name
115: + "\"");
116: }
117:
118: // Check that value is suitable for insertion into Environment map
119: private static void validateValue(String value) {
120: if (value.indexOf('\u0000') != -1)
121: throw new IllegalArgumentException(
122: "Invalid environment variable value: \"" + value
123: + "\"");
124: }
125:
126: // A class hiding the byteArray-String duality of
127: // text data on Unixoid operating systems.
128: private static abstract class ExternalData {
129: protected final String str;
130: protected final byte[] bytes;
131:
132: protected ExternalData(String str, byte[] bytes) {
133: this .str = str;
134: this .bytes = bytes;
135: }
136:
137: public byte[] getBytes() {
138: return bytes;
139: }
140:
141: public String toString() {
142: return str;
143: }
144:
145: public boolean equals(Object o) {
146: return o instanceof ExternalData
147: && arrayEquals(getBytes(), ((ExternalData) o)
148: .getBytes());
149: }
150:
151: public int hashCode() {
152: return arrayHash(getBytes());
153: }
154: }
155:
156: private static class Variable extends ExternalData implements
157: Comparable<Variable> {
158: protected Variable(String str, byte[] bytes) {
159: super (str, bytes);
160: }
161:
162: public static Variable valueOfQueryOnly(Object str) {
163: return valueOfQueryOnly((String) str);
164: }
165:
166: public static Variable valueOfQueryOnly(String str) {
167: return new Variable(str, str.getBytes());
168: }
169:
170: public static Variable valueOf(String str) {
171: validateVariable(str);
172: return valueOfQueryOnly(str);
173: }
174:
175: public static Variable valueOf(byte[] bytes) {
176: return new Variable(new String(bytes), bytes);
177: }
178:
179: public int compareTo(Variable variable) {
180: return arrayCompare(getBytes(), variable.getBytes());
181: }
182:
183: public boolean equals(Object o) {
184: return o instanceof Variable && super .equals(o);
185: }
186: }
187:
188: private static class Value extends ExternalData implements
189: Comparable<Value> {
190: protected Value(String str, byte[] bytes) {
191: super (str, bytes);
192: }
193:
194: public static Value valueOfQueryOnly(Object str) {
195: return valueOfQueryOnly((String) str);
196: }
197:
198: public static Value valueOfQueryOnly(String str) {
199: return new Value(str, str.getBytes());
200: }
201:
202: public static Value valueOf(String str) {
203: validateValue(str);
204: return valueOfQueryOnly(str);
205: }
206:
207: public static Value valueOf(byte[] bytes) {
208: return new Value(new String(bytes), bytes);
209: }
210:
211: public int compareTo(Value value) {
212: return arrayCompare(getBytes(), value.getBytes());
213: }
214:
215: public boolean equals(Object o) {
216: return o instanceof Value && super .equals(o);
217: }
218: }
219:
220: // This implements the String map view the user sees.
221: private static class StringEnvironment extends
222: AbstractMap<String, String> {
223: private Map<Variable, Value> m;
224:
225: private static String toString(Value v) {
226: return v == null ? null : v.toString();
227: }
228:
229: public StringEnvironment(Map<Variable, Value> m) {
230: this .m = m;
231: }
232:
233: public int size() {
234: return m.size();
235: }
236:
237: public boolean isEmpty() {
238: return m.isEmpty();
239: }
240:
241: public void clear() {
242: m.clear();
243: }
244:
245: public boolean containsKey(Object key) {
246: return m.containsKey(Variable.valueOfQueryOnly(key));
247: }
248:
249: public boolean containsValue(Object value) {
250: return m.containsValue(Value.valueOfQueryOnly(value));
251: }
252:
253: public String get(Object key) {
254: return toString(m.get(Variable.valueOfQueryOnly(key)));
255: }
256:
257: public String put(String key, String value) {
258: return toString(m.put(Variable.valueOf(key), Value
259: .valueOf(value)));
260: }
261:
262: public String remove(Object key) {
263: return toString(m.remove(Variable.valueOfQueryOnly(key)));
264: }
265:
266: public Set<String> keySet() {
267: return new StringKeySet(m.keySet());
268: }
269:
270: public Set<Map.Entry<String, String>> entrySet() {
271: return new StringEntrySet(m.entrySet());
272: }
273:
274: public Collection<String> values() {
275: return new StringValues(m.values());
276: }
277:
278: // It is technically feasible to provide a byte-oriented view
279: // as follows:
280: // public Map<byte[],byte[]> asByteArrayMap() {
281: // return new ByteArrayEnvironment(m);
282: // }
283:
284: // Convert to Unix style environ as a monolithic byte array
285: // inspired by the Windows Environment Block, except we work
286: // exclusively with bytes instead of chars, and we need only
287: // one trailing NUL on Unix.
288: // This keeps the JNI as simple and efficient as possible.
289: public byte[] toEnvironmentBlock(int[] envc) {
290: int count = m.size() * 2; // For added '=' and NUL
291: for (Map.Entry<Variable, Value> entry : m.entrySet()) {
292: count += entry.getKey().getBytes().length;
293: count += entry.getValue().getBytes().length;
294: }
295:
296: byte[] block = new byte[count];
297:
298: int i = 0;
299: for (Map.Entry<Variable, Value> entry : m.entrySet()) {
300: byte[] key = entry.getKey().getBytes();
301: byte[] value = entry.getValue().getBytes();
302: System.arraycopy(key, 0, block, i, key.length);
303: i += key.length;
304: block[i++] = (byte) '=';
305: System.arraycopy(value, 0, block, i, value.length);
306: i += value.length + 1;
307: // No need to write NUL byte explicitly
308: //block[i++] = (byte) '\u0000';
309: }
310: envc[0] = m.size();
311: return block;
312: }
313: }
314:
315: static byte[] toEnvironmentBlock(Map<String, String> map, int[] envc) {
316: return map == null ? null : ((StringEnvironment) map)
317: .toEnvironmentBlock(envc);
318: }
319:
320: private static class StringEntry implements
321: Map.Entry<String, String> {
322: private final Map.Entry<Variable, Value> e;
323:
324: public StringEntry(Map.Entry<Variable, Value> e) {
325: this .e = e;
326: }
327:
328: public String getKey() {
329: return e.getKey().toString();
330: }
331:
332: public String getValue() {
333: return e.getValue().toString();
334: }
335:
336: public String setValue(String newValue) {
337: return e.setValue(Value.valueOf(newValue)).toString();
338: }
339:
340: public String toString() {
341: return getKey() + "=" + getValue();
342: }
343:
344: public boolean equals(Object o) {
345: return o instanceof StringEntry
346: && e.equals(((StringEntry) o).e);
347: }
348:
349: public int hashCode() {
350: return e.hashCode();
351: }
352: }
353:
354: private static class StringEntrySet extends
355: AbstractSet<Map.Entry<String, String>> {
356: private final Set<Map.Entry<Variable, Value>> s;
357:
358: public StringEntrySet(Set<Map.Entry<Variable, Value>> s) {
359: this .s = s;
360: }
361:
362: public int size() {
363: return s.size();
364: }
365:
366: public boolean isEmpty() {
367: return s.isEmpty();
368: }
369:
370: public void clear() {
371: s.clear();
372: }
373:
374: public Iterator<Map.Entry<String, String>> iterator() {
375: return new Iterator<Map.Entry<String, String>>() {
376: Iterator<Map.Entry<Variable, Value>> i = s.iterator();
377:
378: public boolean hasNext() {
379: return i.hasNext();
380: }
381:
382: public Map.Entry<String, String> next() {
383: return new StringEntry(i.next());
384: }
385:
386: public void remove() {
387: i.remove();
388: }
389: };
390: }
391:
392: private static Map.Entry<Variable, Value> vvEntry(final Object o) {
393: if (o instanceof StringEntry)
394: return ((StringEntry) o).e;
395: return new Map.Entry<Variable, Value>() {
396: public Variable getKey() {
397: return Variable.valueOfQueryOnly(((Map.Entry) o)
398: .getKey());
399: }
400:
401: public Value getValue() {
402: return Value.valueOfQueryOnly(((Map.Entry) o)
403: .getValue());
404: }
405:
406: public Value setValue(Value value) {
407: throw new UnsupportedOperationException();
408: }
409: };
410: }
411:
412: public boolean contains(Object o) {
413: return s.contains(vvEntry(o));
414: }
415:
416: public boolean remove(Object o) {
417: return s.remove(vvEntry(o));
418: }
419:
420: public boolean equals(Object o) {
421: return o instanceof StringEntrySet
422: && s.equals(((StringEntrySet) o).s);
423: }
424:
425: public int hashCode() {
426: return s.hashCode();
427: }
428: }
429:
430: private static class StringValues extends
431: AbstractCollection<String> {
432: private final Collection<Value> c;
433:
434: public StringValues(Collection<Value> c) {
435: this .c = c;
436: }
437:
438: public int size() {
439: return c.size();
440: }
441:
442: public boolean isEmpty() {
443: return c.isEmpty();
444: }
445:
446: public void clear() {
447: c.clear();
448: }
449:
450: public Iterator<String> iterator() {
451: return new Iterator<String>() {
452: Iterator<Value> i = c.iterator();
453:
454: public boolean hasNext() {
455: return i.hasNext();
456: }
457:
458: public String next() {
459: return i.next().toString();
460: }
461:
462: public void remove() {
463: i.remove();
464: }
465: };
466: }
467:
468: public boolean contains(Object o) {
469: return c.contains(Value.valueOfQueryOnly(o));
470: }
471:
472: public boolean remove(Object o) {
473: return c.remove(Value.valueOfQueryOnly(o));
474: }
475:
476: public boolean equals(Object o) {
477: return o instanceof StringValues
478: && c.equals(((StringValues) o).c);
479: }
480:
481: public int hashCode() {
482: return c.hashCode();
483: }
484: }
485:
486: private static class StringKeySet extends AbstractSet<String> {
487: private final Set<Variable> s;
488:
489: public StringKeySet(Set<Variable> s) {
490: this .s = s;
491: }
492:
493: public int size() {
494: return s.size();
495: }
496:
497: public boolean isEmpty() {
498: return s.isEmpty();
499: }
500:
501: public void clear() {
502: s.clear();
503: }
504:
505: public Iterator<String> iterator() {
506: return new Iterator<String>() {
507: Iterator<Variable> i = s.iterator();
508:
509: public boolean hasNext() {
510: return i.hasNext();
511: }
512:
513: public String next() {
514: return i.next().toString();
515: }
516:
517: public void remove() {
518: i.remove();
519: }
520: };
521: }
522:
523: public boolean contains(Object o) {
524: return s.contains(Variable.valueOfQueryOnly(o));
525: }
526:
527: public boolean remove(Object o) {
528: return s.remove(Variable.valueOfQueryOnly(o));
529: }
530: }
531:
532: // Replace with general purpose method someday
533: private static int arrayCompare(byte[] x, byte[] y) {
534: int min = x.length < y.length ? x.length : y.length;
535: for (int i = 0; i < min; i++)
536: if (x[i] != y[i])
537: return x[i] - y[i];
538: return x.length - y.length;
539: }
540:
541: // Replace with general purpose method someday
542: private static boolean arrayEquals(byte[] x, byte[] y) {
543: if (x.length != y.length)
544: return false;
545: for (int i = 0; i < x.length; i++)
546: if (x[i] != y[i])
547: return false;
548: return true;
549: }
550:
551: // Replace with general purpose method someday
552: private static int arrayHash(byte[] x) {
553: int hash = 0;
554: for (int i = 0; i < x.length; i++)
555: hash = 31 * hash + x[i];
556: return hash;
557: }
558:
559: }
|