1
1
/*
2
- * Copyright (c) 1995, 2019 , Oracle and/or its affiliates. All rights reserved.
2
+ * Copyright (c) 1995, 2021 , Oracle and/or its affiliates. All rights reserved.
3
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
4
*
5
5
* This code is free software; you can redistribute it and/or modify it
25
25
26
26
package java .lang ;
27
27
28
+ import jdk .internal .util .StaticProperty ;
29
+
28
30
import java .io .*;
29
31
import java .lang .ProcessBuilder .Redirect ;
32
+ import java .nio .charset .Charset ;
33
+ import java .nio .charset .UnsupportedCharsetException ;
34
+ import java .util .Objects ;
30
35
import java .util .concurrent .CompletableFuture ;
31
36
import java .util .concurrent .ForkJoinPool ;
32
37
import java .util .concurrent .TimeUnit ;
57
62
* {@link #getOutputStream()},
58
63
* {@link #getInputStream()}, and
59
64
* {@link #getErrorStream()}.
65
+ * The I/O streams of characters and lines can be written and read using the methods
66
+ * {@link #outputWriter()}, {@link #outputWriter(Charset)}},
67
+ * {@link #inputReader()}, {@link #inputReader(Charset)},
68
+ * {@link #errorReader()}, and {@link #errorReader(Charset)}.
60
69
* The parent process uses these streams to feed input to and get output
61
70
* from the process. Because some native platforms only provide
62
71
* limited buffer size for standard input and output streams, failure
90
99
* @since 1.0
91
100
*/
92
101
public abstract class Process {
102
+
103
+ // Readers and Writers created for this process; so repeated calls return the same object
104
+ // All updates must be done while synchronized on this Process.
105
+ private BufferedWriter outputWriter ;
106
+ private Charset outputCharset ;
107
+ private BufferedReader inputReader ;
108
+ private Charset inputCharset ;
109
+ private BufferedReader errorReader ;
110
+ private Charset errorCharset ;
111
+
93
112
/**
94
113
* Default constructor for Process.
95
114
*/
@@ -106,7 +125,13 @@ public Process() {}
106
125
* then this method will return a
107
126
* <a href="ProcessBuilder.html#redirect-input">null output stream</a>.
108
127
*
109
- * <p>Implementation note: It is a good idea for the returned
128
+ * @apiNote
129
+ * When writing to both {@link #getOutputStream()} and either {@link #outputWriter()}
130
+ * or {@link #outputWriter(Charset)}, {@link BufferedWriter#flush BufferedWriter.flush}
131
+ * should be called before writes to the {@code OutputStream}.
132
+ *
133
+ * @implNote
134
+ * Implementation note: It is a good idea for the returned
110
135
* output stream to be buffered.
111
136
*
112
137
* @return the output stream connected to the normal input of the
@@ -132,7 +157,12 @@ public Process() {}
132
157
* then the input stream returned by this method will receive the
133
158
* merged standard output and the standard error of the process.
134
159
*
135
- * <p>Implementation note: It is a good idea for the returned
160
+ * @apiNote
161
+ * Use {@link #getInputStream} and {@link #inputReader} with extreme care.
162
+ * The {@code BufferedReader} may have buffered input from the input stream.
163
+ *
164
+ * @implNote
165
+ * Implementation note: It is a good idea for the returned
136
166
* input stream to be buffered.
137
167
*
138
168
* @return the input stream connected to the normal output of the
@@ -153,14 +183,235 @@ public Process() {}
153
183
* then this method will return a
154
184
* <a href="ProcessBuilder.html#redirect-output">null input stream</a>.
155
185
*
156
- * <p>Implementation note: It is a good idea for the returned
186
+ * @apiNote
187
+ * Use {@link #getInputStream} and {@link #inputReader} with extreme care.
188
+ * The {@code BufferedReader} may have buffered input from the input stream.
189
+ *
190
+ * @implNote
191
+ * Implementation note: It is a good idea for the returned
157
192
* input stream to be buffered.
158
193
*
159
194
* @return the input stream connected to the error output of
160
195
* the process
161
196
*/
162
197
public abstract InputStream getErrorStream ();
163
198
199
+ /**
200
+ * Returns a {@link BufferedReader BufferedReader} connected to the standard
201
+ * output of the process. The {@link Charset} for the native encoding is used
202
+ * to read characters, lines, or stream lines from standard output.
203
+ *
204
+ * <p>This method delegates to {@link #inputReader(Charset)} using the
205
+ * {@link Charset} named by the {@code native.encoding} system property.
206
+ * If the {@code native.encoding} is not a valid charset name or not supported
207
+ * the {@link Charset#defaultCharset()} is used.
208
+ *
209
+ * @return a {@link BufferedReader BufferedReader} using the
210
+ * {@code native.encoding} if supported, otherwise, the
211
+ * {@link Charset#defaultCharset()}
212
+ * @since 17
213
+ */
214
+ public final BufferedReader inputReader () {
215
+ return inputReader (CharsetHolder .nativeCharset ());
216
+ }
217
+
218
+ /**
219
+ * Returns a {@link BufferedReader BufferedReader} connected to the
220
+ * standard output of this process using a Charset.
221
+ * The {@code BufferedReader} can be used to read characters, lines,
222
+ * or stream lines of the standard output.
223
+ *
224
+ * <p>Characters are read by an InputStreamReader that reads and decodes bytes
225
+ * from this process {@link #getInputStream()}. Bytes are decoded to characters
226
+ * using the {@code charset}; malformed-input and unmappable-character
227
+ * sequences are replaced with the charset's default replacement.
228
+ * The {@code BufferedReader} reads and buffers characters from the InputStreamReader.
229
+ *
230
+ * <p>The first call to this method creates the {@link BufferedReader BufferedReader},
231
+ * if called again with the same {@code charset} the same {@code BufferedReader} is returned.
232
+ * It is an error to call this method again with a different {@code charset}.
233
+ *
234
+ * <p>If the standard output of the process has been redirected using
235
+ * {@link ProcessBuilder#redirectOutput(Redirect) ProcessBuilder.redirectOutput}
236
+ * then the {@code InputStreamReader} will be reading from a
237
+ * <a href="ProcessBuilder.html#redirect-output">null input stream</a>.
238
+ *
239
+ * <p>Otherwise, if the standard error of the process has been redirected using
240
+ * {@link ProcessBuilder#redirectErrorStream(boolean)
241
+ * ProcessBuilder.redirectErrorStream} then the input reader returned by
242
+ * this method will receive the merged standard output and the standard error
243
+ * of the process.
244
+ *
245
+ * @apiNote
246
+ * Using both {@link #getInputStream} and {@link #inputReader(Charset)} has
247
+ * unpredictable behavior since the buffered reader reads ahead from the
248
+ * input stream.
249
+ *
250
+ * <p>When the process has terminated, and the standard input has not been redirected,
251
+ * reading of the bytes available from the underlying stream is on a best effort basis and
252
+ * may be unpredictable.
253
+ *
254
+ * @param charset the {@code Charset} used to decode bytes to characters
255
+ * @return a {@code BufferedReader} for the standard output of the process using the {@code charset}
256
+ * @throws NullPointerException if the {@code charset} is {@code null}
257
+ * @throws IllegalStateException if called more than once with different charset arguments
258
+ * @since 17
259
+ */
260
+ public final BufferedReader inputReader (Charset charset ) {
261
+ Objects .requireNonNull (charset , "charset" );
262
+ synchronized (this ) {
263
+ if (inputReader == null ) {
264
+ inputCharset = charset ;
265
+ inputReader = new BufferedReader (new InputStreamReader (getInputStream (), charset ));
266
+ } else {
267
+ if (!inputCharset .equals (charset ))
268
+ throw new IllegalStateException ("BufferedReader was created with charset: " + inputCharset );
269
+ }
270
+ return inputReader ;
271
+ }
272
+ }
273
+
274
+ /**
275
+ * Returns a {@link BufferedReader BufferedReader} connected to the standard
276
+ * error of the process. The {@link Charset} for the native encoding is used
277
+ * to read characters, lines, or stream lines from standard error.
278
+ *
279
+ * <p>This method delegates to {@link #errorReader(Charset)} using the
280
+ * {@link Charset} named by the {@code native.encoding} system property.
281
+ * If the {@code native.encoding} is not a valid charset name or not supported
282
+ * the {@link Charset#defaultCharset()} is used.
283
+ *
284
+ * @return a {@link BufferedReader BufferedReader} using the
285
+ * {@code native.encoding} if supported, otherwise, the
286
+ * {@link Charset#defaultCharset()}
287
+ * @since 17
288
+ */
289
+ public final BufferedReader errorReader () {
290
+ return errorReader (CharsetHolder .nativeCharset ());
291
+ }
292
+
293
+ /**
294
+ * Returns a {@link BufferedReader BufferedReader} connected to the
295
+ * standard error of this process using a Charset.
296
+ * The {@code BufferedReader} can be used to read characters, lines,
297
+ * or stream lines of the standard error.
298
+ *
299
+ * <p>Characters are read by an InputStreamReader that reads and decodes bytes
300
+ * from this process {@link #getErrorStream()}. Bytes are decoded to characters
301
+ * using the {@code charset}; malformed-input and unmappable-character
302
+ * sequences are replaced with the charset's default replacement.
303
+ * The {@code BufferedReader} reads and buffers characters from the InputStreamReader.
304
+ *
305
+ * <p>The first call to this method creates the {@link BufferedReader BufferedReader},
306
+ * if called again with the same {@code charset} the same {@code BufferedReader} is returned.
307
+ * It is an error to call this method again with a different {@code charset}.
308
+ *
309
+ * <p>If the standard error of the process has been redirected using
310
+ * {@link ProcessBuilder#redirectError(Redirect) ProcessBuilder.redirectError} or
311
+ * {@link ProcessBuilder#redirectErrorStream(boolean) ProcessBuilder.redirectErrorStream}
312
+ * then the {@code InputStreamReader} will be reading from a
313
+ * <a href="ProcessBuilder.html#redirect-output">null input stream</a>.
314
+ *
315
+ * @apiNote
316
+ * Using both {@link #getErrorStream} and {@link #errorReader(Charset)} has
317
+ * unpredictable behavior since the buffered reader reads ahead from the
318
+ * error stream.
319
+ *
320
+ * <p>When the process has terminated, and the standard error has not been redirected,
321
+ * reading of the bytes available from the underlying stream is on a best effort basis and
322
+ * may be unpredictable.
323
+ *
324
+ * @param charset the {@code Charset} used to decode bytes to characters
325
+ * @return a {@code BufferedReader} for the standard error of the process using the {@code charset}
326
+ * @throws NullPointerException if the {@code charset} is {@code null}
327
+ * @throws IllegalStateException if called more than once with different charset arguments
328
+ * @since 17
329
+ */
330
+ public final BufferedReader errorReader (Charset charset ) {
331
+ Objects .requireNonNull (charset , "charset" );
332
+ synchronized (this ) {
333
+ if (errorReader == null ) {
334
+ errorCharset = charset ;
335
+ errorReader = new BufferedReader (new InputStreamReader (getErrorStream (), charset ));
336
+ } else {
337
+ if (!errorCharset .equals (charset ))
338
+ throw new IllegalStateException ("BufferedReader was created with charset: " + errorCharset );
339
+ }
340
+ return errorReader ;
341
+ }
342
+ }
343
+
344
+ /**
345
+ * Returns a {@code BufferedWriter} connected to the normal input of the process
346
+ * using the native encoding.
347
+ * Writes text to a character-output stream, buffering characters so as to provide
348
+ * for the efficient writing of single characters, arrays, and strings.
349
+ *
350
+ * <p>This method delegates to {@link #outputWriter(Charset)} using the
351
+ * {@link Charset} named by the {@code native.encoding} system property.
352
+ * If the {@code native.encoding} is not a valid charset name or not supported
353
+ * the {@link Charset#defaultCharset()} is used.
354
+ *
355
+ * @return a {@code BufferedWriter} to the standard input of the process using the charset
356
+ * for the {@code native.encoding} system property
357
+ * @since 17
358
+ */
359
+ public final BufferedWriter outputWriter () {
360
+ return outputWriter (CharsetHolder .nativeCharset ());
361
+ }
362
+
363
+ /**
364
+ * Returns a {@code BufferedWriter} connected to the normal input of the process
365
+ * using a Charset.
366
+ * Writes text to a character-output stream, buffering characters so as to provide
367
+ * for the efficient writing of single characters, arrays, and strings.
368
+ *
369
+ * <p>Characters written by the writer are encoded to bytes using {@link OutputStreamWriter}
370
+ * and the {@link Charset} are written to the standard input of the process represented
371
+ * by this {@code Process}.
372
+ * Malformed-input and unmappable-character sequences are replaced with the charset's
373
+ * default replacement.
374
+ *
375
+ * <p>The first call to this method creates the {@link BufferedWriter BufferedWriter},
376
+ * if called again with the same {@code charset} the same {@code BufferedWriter} is returned.
377
+ * It is an error to call this method again with a different {@code charset}.
378
+ *
379
+ * <p>If the standard input of the process has been redirected using
380
+ * {@link ProcessBuilder#redirectInput(Redirect)
381
+ * ProcessBuilder.redirectInput} then the {@code OutputStreamWriter} writes to a
382
+ * <a href="ProcessBuilder.html#redirect-input">null output stream</a>.
383
+ *
384
+ * @apiNote
385
+ * A {@linkplain BufferedWriter} writes characters, arrays of characters, and strings.
386
+ * Wrapping the {@link BufferedWriter} with a {@link PrintWriter} provides
387
+ * efficient buffering and formatting of primitives and objects as well as support
388
+ * for auto-flush on line endings.
389
+ * Call the {@link BufferedWriter#flush()} method to flush buffered output to the process.
390
+ * <p>
391
+ * When writing to both {@link #getOutputStream()} and either {@link #outputWriter()}
392
+ * or {@link #outputWriter(Charset)}, {@linkplain BufferedWriter#flush BufferedWriter.flush}
393
+ * should be called before writes to the {@code OutputStream}.
394
+ *
395
+ * @param charset the {@code Charset} to encode characters to bytes
396
+ * @return a {@code BufferedWriter} to the standard input of the process using the {@code charset}
397
+ * @throws NullPointerException if the {@code charset} is {@code null}
398
+ * @throws IllegalStateException if called more than once with different charset arguments
399
+ * @since 17
400
+ */
401
+ public final BufferedWriter outputWriter (Charset charset ) {
402
+ Objects .requireNonNull (charset , "charset" );
403
+ synchronized (this ) {
404
+ if (outputWriter == null ) {
405
+ outputCharset = charset ;
406
+ outputWriter = new BufferedWriter (new OutputStreamWriter (getOutputStream (), charset ));
407
+ } else {
408
+ if (!outputCharset .equals (charset ))
409
+ throw new IllegalStateException ("BufferedWriter was created with charset: " + outputCharset );
410
+ }
411
+ return outputWriter ;
412
+ }
413
+ }
414
+
164
415
/**
165
416
* Causes the current thread to wait, if necessary, until the
166
417
* process represented by this {@code Process} object has
@@ -261,7 +512,7 @@ public boolean waitFor(long timeout, TimeUnit unit)
261
512
* when the process has terminated.
262
513
* <p>
263
514
* Invoking this method on {@code Process} objects returned by
264
- * {@link ProcessBuilder#start} and {@link Runtime#exec} forcibly terminate
515
+ * {@link ProcessBuilder#start() } and {@link Runtime#exec} forcibly terminate
265
516
* the process.
266
517
*
267
518
* @implSpec
@@ -292,7 +543,7 @@ public Process destroyForcibly() {
292
543
* forcibly and immediately terminates the process.
293
544
* <p>
294
545
* Invoking this method on {@code Process} objects returned by
295
- * {@link ProcessBuilder#start} and {@link Runtime#exec} return
546
+ * {@link ProcessBuilder#start() } and {@link Runtime#exec} return
296
547
* {@code true} or {@code false} depending on the platform implementation.
297
548
*
298
549
* @implSpec
@@ -371,7 +622,7 @@ public long pid() {
371
622
* {@linkplain java.util.concurrent.CompletableFuture#cancel(boolean) Cancelling}
372
623
* the CompletableFuture does not affect the Process.
373
624
* <p>
374
- * Processes returned from {@link ProcessBuilder#start} override the
625
+ * Processes returned from {@link ProcessBuilder#start() } override the
375
626
* default implementation to provide an efficient mechanism to wait
376
627
* for process exit.
377
628
*
@@ -463,7 +714,7 @@ public boolean isReleasable() {
463
714
/**
464
715
* Returns a ProcessHandle for the Process.
465
716
*
466
- * {@code Process} objects returned by {@link ProcessBuilder#start} and
717
+ * {@code Process} objects returned by {@link ProcessBuilder#start() } and
467
718
* {@link Runtime#exec} implement {@code toHandle} as the equivalent of
468
719
* {@link ProcessHandle#of(long) ProcessHandle.of(pid)} including the
469
720
* check for a SecurityManager and {@code RuntimePermission("manageProcess")}.
@@ -589,4 +840,27 @@ public long skip(long n) throws IOException {
589
840
return n - remaining ;
590
841
}
591
842
}
843
+
844
+ /**
845
+ * A nested class to delay looking up the Charset for the native encoding.
846
+ */
847
+ private static class CharsetHolder {
848
+ private final static Charset nativeCharset ;
849
+ static {
850
+ Charset cs ;
851
+ try {
852
+ cs = Charset .forName (StaticProperty .nativeEncoding ());
853
+ } catch (UnsupportedCharsetException uce ) {
854
+ cs = Charset .defaultCharset ();
855
+ }
856
+ nativeCharset = cs ;
857
+ }
858
+
859
+ /**
860
+ * Charset for the native encoding or {@link Charset#defaultCharset().
861
+ */
862
+ static Charset nativeCharset () {
863
+ return nativeCharset ;
864
+ }
865
+ }
592
866
}
0 commit comments