Class TerminalProcess
- All Implemented Interfaces:
AutoCloseable,TerminalSink
juneau_terminal.py --pty and exposes its output as a
FileTerminalSource and its steps as a FileTerminalViewSource.
The command line is
python3 <script> --console none --pty --size <cols>x<rows> --full-log <log> --events <events> <tool> -- <cmd...>,
with the log ("terminal.log"), its .size sidecar and the events file ("terminal.events.jsonl") in the
given run directory. The script is found through the system property "juneau.terminal.script", then the
environment variable "JUNEAU_TERMINAL_SCRIPT".
The run directory is single-use. It is also the tool's working directory, and the runner's own stdout and stderr
go to runner.out there, in append mode and uncapped; that file is diagnostic only.
Both sources share one done flag, set once the process has exited. If the runner exits without writing a
done event, a warning note and done {status:"fail"} are appended first. Only the last
65536 bytes of the events file are scanned for that done event, because the protocol puts it
last.
The exit hook runs on one daemon thread per process, named juneau-terminal-exit-<pid>. Its executor is
shut down once the hook has run, and close() waits for that.
Started with input on, the runner also gets --input stdin (right after the events pair) and the process is a
TerminalSink: write(byte[]) queues the bytes in a 65536-byte pending buffer and never
blocks, and one daemon thread per process, named juneau-terminal-input-<pid>, copies them to the runner's
stdin, flushing after each chunk. A closed or broken pipe turns acceptsInput() false and discards the
buffer. With input off the command line and the streams are exactly as above, no writer thread starts, and
acceptsInput() is always false.
Started with a TerminalPrompts, the runner gets --prompts stdio and its stdout and stdin become the
prompt channel: one daemon thread per process, named terminal-prompts-<dir name>, reads the stdout lines (at
most 1048576 bytes each; a longer one is dropped) and hands them to
TerminalPrompts.onLine(String), then calls TerminalPrompts.onEnd() once. The runner's stderr goes to
runner.out. answer(String) writes one line back. Neither a prompt line nor an answer is ever
logged. Input and prompts are mutually exclusive.
POSIX only: there is no Windows pseudo-terminal support.
Example:
TerminalProcess
-
Field Summary
FieldsModifier and TypeFieldDescriptionstatic final StringThe events file's name in the run directory.static final StringThe byte log's file name in the run directory.static final StringThe environment variable namingjuneau_terminal.py, read when the property is not set.static final StringThe system property namingjuneau_terminal.py. -
Method Summary
Modifier and TypeMethodDescriptionbooleanWhether an answer can be sent right now.booleanWhether input is accepted right now.booleanWrites one answer line to the script's prompt channel; never blocks behind another answer and never logs the line.voidclose()Kills the runner and its tool if they are still running, then waits up to 5 seconds for the exit hook.events()The step events source over the events file.The events file.booleanisDone()Whether the process has exited and the events file ends with adoneevent.log()The byte log.nudges(String terminalId, TerminalNudgePublisher publisher) Polls the log's size every 100 ms and reports growth, then a final done, to a publisher.static TerminalProcessStarts a tool under a pseudo-terminal.static TerminalProcessStarts a tool under a pseudo-terminal, optionally with input.static TerminalProcessStarts a tool under a pseudo-terminal with a prompt channel.terminal()The raw byte source over the log.intWaits for the process to exit and for the done event to be in place.booleanwrite(byte[] bytes) Queues the bytes for the runner's stdin; never blocks.
-
Field Details
-
SCRIPT_PROPERTY
The system property namingjuneau_terminal.py.- See Also:
-
SCRIPT_ENV
The environment variable namingjuneau_terminal.py, read when the property is not set.- See Also:
-
LOG_NAME
The byte log's file name in the run directory.- See Also:
-
EVENTS_NAME
The events file's name in the run directory.- See Also:
-
-
Method Details
-
start
public static TerminalProcess start(String tool, List<String> cmd, int cols, int rows, Path dir) throws IOException Starts a tool under a pseudo-terminal.- Parameters:
tool- The juneau_terminal tool parser:maven,pytest,playwrightorgeneric.cmd- The command and its arguments; at least one element. It is a token list: each element reaches the tool as one argument, throughProcessBuilder. It is never joined into a string or given to a shell, so shell metacharacters in an element are literal.cols- The terminal width, 1-9999.rows- The terminal height, 1-9999.dir- The run directory; created if missing. Each run needs its own: the log and events files must be missing or empty. It is also the tool's working directory, and the runner's own stdout and stderr go torunner.outin it, in append mode and uncapped; that file is diagnostic only.- Returns:
- The running process.
- Throws:
IOException- If the directory could not be prepared or the process could not start.IllegalArgumentException- If an argument is out of range.IllegalStateException- If the script is not configured,python3is not on thePATH, or the run directory already holds a run.UnsupportedOperationException- On Windows.
-
start
public static TerminalProcess start(String tool, List<String> cmd, int cols, int rows, Path dir, boolean input) throws IOException Starts a tool under a pseudo-terminal, optionally with input.Same as
start(String, List, int, int, Path), except that withinputon the runner gets--input stdinand the returned process acceptswrite(byte[]).- Parameters:
tool- The juneau_terminal tool parser.cmd- The command and its arguments; at least one element.cols- The terminal width, 1-9999.rows- The terminal height, 1-9999.dir- The run directory; seestart(String, List, int, int, Path).input- Whether the job takes input throughwrite(byte[]).- Returns:
- The running process.
- Throws:
IOException- If the directory could not be prepared or the process could not start.IllegalArgumentException- If an argument is out of range.IllegalStateException- If the script is not configured,python3is not on thePATH, or the run directory already holds a run.UnsupportedOperationException- On Windows.
-
start
public static TerminalProcess start(String tool, List<String> cmd, int cols, int rows, Path dir, TerminalPrompts prompts) throws IOException Starts a tool under a pseudo-terminal with a prompt channel.Same as
start(String, List, int, int, Path), except that the runner gets--prompts stdio, the script's prompt lines go topromptsandanswer(String)sends the replies.- Parameters:
tool- The juneau_terminal tool parser.cmd- The command and its arguments; at least one element.cols- The terminal width, 1-9999.rows- The terminal height, 1-9999.dir- The run directory; seestart(String, List, int, int, Path).prompts- Receives the prompt lines; notnull .- Returns:
- The running process.
- Throws:
IOException- If the directory could not be prepared or the process could not start.IllegalArgumentException- If an argument is out of range.IllegalStateException- If the script is not configured,python3is not on thePATH, or the run directory already holds a run.UnsupportedOperationException- On Windows.
-
terminal
The raw byte source over the log.- Returns:
- The source.
-
events
The step events source over the events file.- Returns:
- The source.
-
log
The byte log.- Returns:
- The log path.
-
eventsFile
The events file.- Returns:
- The events path.
-
isDone
Whether the process has exited and the events file ends with adoneevent.- Returns:
true once finished.
-
waitFor
Waits for the process to exit and for the done event to be in place.- Parameters:
timeout- The longest wait.unit- The unit oftimeout.- Returns:
- The runner's exit code.
- Throws:
InterruptedException- If interrupted.TimeoutException- If the process is still running.
-
acceptsInput
Whether input is accepted right now.- Specified by:
acceptsInputin interfaceTerminalSink- Returns:
true only when input is on, the process is alive, no write has failed and the process has not been closed.
-
write
Queues the bytes for the runner's stdin; never blocks.- Specified by:
writein interfaceTerminalSink- Parameters:
bytes- The bytes to send; copied.- Returns:
false when input is not accepted, or when the bytes do not fit in the 65536-byte pending buffer. An empty array returnsacceptsInput().
-
acceptsAnswers
Whether an answer can be sent right now.- Returns:
true only when a prompt channel is on, the process is alive and the channel has not ended or failed.
-
answer
Writes one answer line to the script's prompt channel; never blocks behind another answer and never logs the line.- Parameters:
jsonLine- The answer as one JSON line, without a newline.- Returns:
false when there is no channel, it has ended, the line holds a newline, another answer is being written, or the write failed.
-
nudges
Polls the log's size every 100 ms and reports growth, then a final done, to a publisher.The runner writes the log in another process, so there is no in-process append hook to use. The publisher runs on a daemon thread named
juneau-terminal-nudge-<pid>; aRuntimeExceptionit throws is logged atFINEand otherwise ignored. The thread ends after the done report, or whenclose()is called.- Parameters:
terminalId- The terminal id passed to the publisher; must be a valid run id.publisher- The publisher.- Returns:
- This object.
- Throws:
NullPointerException- If an argument isnull .IllegalArgumentException- If the id is not a valid run id.IllegalStateException- If a publisher is already set, or this process has been closed.
-
close
Kills the runner and its tool if they are still running, then waits up to 5 seconds for the exit hook.The hook writes the done event and shuts its executor down, so once this returns normally both have happened. The executor is not shut down here: a hook submitted after that would be refused, and the done flag would never be set.
With input on, it first stops input: pending bytes are dropped, stdin is closed and the writer thread is told to stop. If the writer thread is blocked writing to a full pipe, it closes stdin itself once the kill breaks the pipe. Afterwards this waits up to 1000 ms for the writer thread. The nudge thread, if any, is stopped first of all and joined for up to 1000 ms. With input off, it only kills.
- Specified by:
closein interfaceAutoCloseable
-