[External] : Re: My experience using Java to do CLI Scripting
Roger Riggs
roger.riggs at oracle.com
Tue Apr 15 22:04:35 UTC 2025
Hi,
The cleanup of Process state is handled by the implementation.
On Linux it waits for the exitStatus and when the process exits, it
captures the status, drains any pending input into local buffers and
closes the file descriptors. When the streams and Process implementation
are GC'd any remaining cleanup is done. A Cleaner is registered for each
stream to close the file descriptor.
On Windows, the cleaner of the Process object is used to close the
Windows handle. Each stream has its own Cleaner registration to close
its handle when the objects are GC'd. If you've seen cases of leaking
file descriptors, zombie processes, or handles, that's a bug (hopefully
reproducible).
I'm not sure using T-W-R would make it more robust, there would be
additional complexity in mixing synchronous cleanup with the
asynchronous cleanup.
Most of awkwardness of the Process API is related to independent states
of the streams and the exit status itself and whether the application
interprets a non-zero exit status in relation to the end-of-input
streams; that interpretation is related to the particular application
spawned.
The Process API also doesn't help, except for the timed `waitFor()` with
a process that doesn't terminate, an empty standard input but waiting
for input, etc.
The doc could be clearer that it is unnecessary to explicitly handle all
of the process elements; ignore the exit status from the application
spawned unless it means something. One unfortunate aspect of streams is
the lack of a distinction between a premature end-of-stream vs an
explicit we're-done-here. Ignore the error stream if there's no reason
to believe it has any useful information, that also depends on what
application was spawned.
The simple cases can be easier to code and should be and the more
involved cases should take better advantage of more recent language and
runtime features.
YMMV, Roger
On 4/15/25 8:30 AM, Ron Pressler wrote:
>
>> On 15 Apr 2025, at 10:51, forax at univ-mlv.fr wrote:
>>
>> ----- Original Message -----
>>> From: "Stuart Marks" <stuart.marks at oracle.com>
>>> To: "Remi Forax" <forax at univ-mlv.fr>, "Ron Pressler" <ron.pressler at oracle.com>, "David Alayachew"
>>> <davidalayachew at gmail.com>
>>> Cc: "cay horstmann" <cay.horstmann at gmail.com>, "core-libs-dev" <core-libs-dev at openjdk.org>
>>> Sent: Tuesday, April 15, 2025 12:10:54 AM
>>> Subject: Re: My experience using Java to do CLI Scripting
>>>
>>> A few points here...
>>>
>>> It might be possible to get away without any Process-related cleanup. For one,
>>> there's no "close" method on Process or ProcessBuilder. The destroy() method
>>> will
>>> certainly clean up, but you don't want that; and waitFor() merely waits for
>>> termination but doesn't actually clean anything up.
>>>
>>> At least in the Unix ProcessImpl class, there's a bunch of infrastructure to
>>> handle
>>> exit of the underlying process, drain input from its stdout/stderr, and close
>>> the
>>> pipes. (I didn't look on Windows.) So setting aside termination, IOException,
>>> and
>>> what to do with stderr, it seems possible to just get the inputReader() and do
>>> something with the characters it emits.
>> You mean by making the Process Closeable ?
> It could make sense to make Process AutoCloseable similar to how ExecutorService now works, i.e. by having the close method call waitFor.
>
>>> Getting a stream of lines with lines() seems like a reasonable thing to do if
>>> you
>>> want to process the output line by line.
>> with the caveat that you have to close() the returned stream (like Files.lines()), something people will often forget.
>>
> The three Process streams are created (by default) whether you ever use them or not, so if you did have to close them, you’d need to do it every time you use Process. The reason is that the IO scaffolding (including ensuring cleanup) needs to be set up as soon as the process is started, before we know if you’re interested in reading the output. I think that if you want the streams to *not* be opened, you need to ask ProcessBuilder explicitly with ProcessBuilder.Redirect.DISCARD.
>
> It seems to me that on Unix, the pipes are all closed as soon as the process terminates. On Windows it’s more complicated and there may be Cleaners involved (and the buffering done by the OS rather than Java?), but on either platform it seems that it doesn’t matter whether you actually use the streams.
>
> Perhaps Roger can clarify, or correct me if I’m wrong.
>
>>> There are other possibilities, taking a nod from Files, which has methods
>>> readAllLines() and readString(). Putting similar methods on some class in the
>>> Reader
>>> family might help considerably here. Or you could call Reader.transferTo(Writer)
>>> that will send the characters to any Writer that might have a useful
>>> destination.
>> +1 for readAllInputLines() and readInputString() on Process.
> Wouldn’t it make more sense to add these to BufferedReader?
>
> — Ron
>
>
More information about the core-libs-dev
mailing list