Class RemoteProcess

java.lang.Object
org.metricshub.winrm.RemoteProcess
All Implemented Interfaces:
AutoCloseable

public final class RemoteProcess extends Object implements AutoCloseable
A running remote command, created by CommandRequest.start() — the streaming counterpart of CommandRequest.execute(), shaped like Process. Output can be consumed while the command is still running:

 try (RemoteProcess process = client.command("wevtutil qe System /f:text").start()) {
 	try (BufferedReader out = process.stdout()) {
 		out.lines().forEach(System.out::println);
 	}
 	int exitCode = process.waitFor();
 }
 

Lifecycle. The process owns the client's serial connection until the command completes or the process is closed: other operations on the same client block in the meantime (the same contract as a JDBC ResultSet on its connection). Closing before completion sends the WinRM terminate Signal, which stops the remote command. Always close the process — use try-with-resources. Closing the readers returned by stdout()/stderr() does not close the process.

Reading. Both channels are fed by the same WSMan Receive loop: reading either channel (or calling waitFor()) advances the loop, and output that arrives for the channel not being read is buffered in memory until it is read — so memory is bounded by the unread channel, not by the total output. Output is decoded incrementally with the request's charset; a multibyte character split across protocol chunks is decoded correctly.

Timeout. The request timeout acts as an inactivity timeout: the longest silence tolerated from the server between two responses, not an overall deadline — a command may run (and stream) far longer than the timeout as long as it keeps producing output. Reads and waits throw WinRMTimeoutException when the command stays silent for a whole timeout; use waitFor(Duration) for an overall deadline.

Writing. stdin() feeds the command's standard input: flush() carries the written text to the host as a WSMan Send, close() marks the end of input (the remote stdin then reaches EOF). Writes and reads alternate on the caller's thread, exactly like Process pipes — including the classic deadlock, which is the caller's to avoid: blocking on a read while the remote command itself is blocked waiting for input (or the reverse) hangs both sides until the inactivity timeout fires.

Threading. A process is not thread-safe: read, write, wait and close from one thread at a time.

Failures during consumption are reported through the unchecked WinRMClientException hierarchy, including from the readers' read() methods.

  • Method Summary

    Modifier and Type
    Method
    Description
    void
    Terminate the command (when it is still running) and release the client's connection.
    int
    Get the exit code of the completed command.
    void
    Interrupt the command the way a console Ctrl+C would — the WSMan ctrl_c Signal.
    boolean
    poll(Duration maxWait)
    Advance the stream by at most one bounded protocol round trip: ask the server to answer within the given wait — with output when it has any, with the protocol's "nothing yet" otherwise — buffer whatever arrives for the readers, and report whether the command has completed.
    Get the standard error of the remote command, decoded incrementally: lines can be read while the command is still running.
    Get the standard input of the remote command.
    Get the standard output of the remote command, decoded incrementally: lines can be read while the command is still running.
    int
    Wait for the command to complete, buffering any unread output in the meantime (read it afterward from stdout()/stderr()).
    boolean
    waitFor(Duration deadline)
    Wait at most the given duration for the command to complete — an overall deadline, on top of the per-response inactivity timeout.

    Methods inherited from class java.lang.Object

    equals, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
  • Method Details

    • stdout

      public BufferedReader stdout()
      Get the standard output of the remote command, decoded incrementally: lines can be read while the command is still running. Always the same reader instance; closing it does not affect the process.
      Returns:
      the standard output reader
    • stderr

      public BufferedReader stderr()
      Get the standard error of the remote command, decoded incrementally: lines can be read while the command is still running. Always the same reader instance; closing it does not affect the process.
      Returns:
      the standard error reader
    • stdin

      public BufferedWriter stdin()
      Get the standard input of the remote command. Written text is buffered locally until flush(), which carries it to the host as one WSMan Send (encoded with the request's charset); close() sends the final chunk flagged as the end of input, after which the remote stdin reaches EOF. Always the same writer instance; closing it does not close the process — but unlike the readers it does talk to the host, so close it before (or via) the process itself, not after.

      When the request pre-supplied the input (stdin(...) on the builder), that input was already delivered in full: this writer is then closed from the start.

      A command that must observe the EOF — a filter like sort that only acts once its input ends — needs pipe semantics: declare the interactive input with the no-argument CommandRequest.stdin() on the builder. Without it the remote stdin keeps the historical console semantics, where writes are delivered but the end of input is not.

      Failures while sending are reported through the unchecked WinRMClientException hierarchy; writing after the end of input or after the command completed throws IllegalStateException.

      Returns:
      the standard input writer
    • interrupt

      public void interrupt()
      Interrupt the command the way a console Ctrl+C would — the WSMan ctrl_c Signal. It interrupts the command's child process without terminating the command or this process handle: the process stays fully usable, which is what an interactive session needs. A no-op once the command has completed or the process is closed.
      Throws:
      WinRMTimeoutException - when the server does not answer the Signal in time
      WinRMClientException - for any other failure
    • waitFor

      public int waitFor()
      Wait for the command to complete, buffering any unread output in the meantime (read it afterward from stdout()/stderr()).
      Returns:
      the command's exit code
      Throws:
      WinRMTimeoutException - when the command stays silent for a whole inactivity timeout
      WinRMClientException - for any other failure
    • poll

      public boolean poll(Duration maxWait)
      Advance the stream by at most one bounded protocol round trip: ask the server to answer within the given wait — with output when it has any, with the protocol's "nothing yet" otherwise — buffer whatever arrives for the readers, and report whether the command has completed. An empty answer is not a failure: the process stays fully usable, so a polling consumer (e.g. an interactive session pump) never trips the inactivity timeout while idle. Unlike waitFor(Duration), which keeps polling until its deadline, this returns as soon as the server answers: output that is ready arrives (and can be read) immediately.

      The wait is the polling cadence, not a hard bound on the round trip: how long the answer itself may take to arrive is governed by the request timeout (the inactivity timeout), so one slow answer from a loaded server does not fail the stream — use waitFor(Duration) when a hard deadline is needed. The request timeout also caps the cadence, and a resulting budget too short for a network round trip — the WSMan service holds a bounded Receive for at least 500 ms before answering "nothing yet" — is waited out locally without touching the wire: the stream then does not advance. So poll with waits of about a second or more, on a request whose timeout is at least as long; a shorter request timeout leaves waitFor() and the readers as the ways to advance.

      Parameters:
      maxWait - when the server should answer at the latest (at least one millisecond)
      Returns:
      true when the command has completed — the exit code is then available from exitCode()false when it is still running
      Throws:
      WinRMTimeoutException - when the server stays silent for a whole inactivity timeout
      WinRMClientException - for any other failure
    • waitFor

      public boolean waitFor(Duration deadline)
      Wait at most the given duration for the command to complete — an overall deadline, on top of the per-response inactivity timeout. The remaining wait is a hard bound on the active protocol round trip: the server is asked to answer early enough for its reply to arrive within it, and a wait too short for any network round trip is waited out locally without touching the wire. Expiry does not affect the command: it keeps running, the process stays fully usable, and the caller decides whether to keep waiting or close().
      Parameters:
      deadline - how long to wait (at least one millisecond)
      Returns:
      true when the command completed within the given duration — the exit code is then available from exitCode()false when the wait expired first
      Throws:
      WinRMTimeoutException - when the server does not answer the bounded requests within the remaining wait — a peer that stopped answering cannot hold the wait past its deadline
      WinRMClientException - for any other failure
    • exitCode

      public int exitCode()
      Get the exit code of the completed command.
      Returns:
      the exit code
      Throws:
      IllegalStateException - when the command has not completed yet — wait for completion with waitFor(), or read the output streams to their end first — or when the process was closed before the command completed
    • close

      public void close()
      Terminate the command (when it is still running) and release the client's connection. Idempotent; a no-op when the command already completed. Output buffered before the close remains readable, then the readers report end of stream; waitFor() and exitCode() throw IllegalStateException when the close preceded completion — a terminated command has no exit code.
      Specified by:
      close in interface AutoCloseable