Skip to content

Commit

Permalink
Add support for embedded applications
Browse files Browse the repository at this point in the history
There are unusual situations in which a container application may be
using JLine3 and be invoking an application that also uses JLine3 in the
same process. When this is the case, there can be conflicts between the
container's JLine usage and the application. To be more specific, this
is a problem for sbt and the scala REPL. This is a difficult problem to
solve because sbt attempts to support all versions of scala but there is
no entry point to the scala REPL that allows the calling application to
pass in a custom terminal. As a result, it isn't possible for sbt using
JLine3 to viably support all versions of the scala REPL.

We can work workaround this by override TerminalBuilder.build to always
return a statically provided Terminal that can be set via a new api:
TerminalBuilder.setGlobalTerminal. This api is to be avoided in normal
JLine usage so it is marked as deprecated and the javadocs hopefully do
a good enough job of explaining the intended use case to discourage
users from calling it unless they have an sbt like use case.
  • Loading branch information
eatkins authored and gnodet committed Jul 23, 2020
1 parent 7e807af commit fdc2fb5
Showing 1 changed file with 39 additions and 1 deletion.
40 changes: 39 additions & 1 deletion terminal/src/main/java/org/jline/terminal/TerminalBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ public static TerminalBuilder builder() {
}

private static final AtomicReference<Terminal> SYSTEM_TERMINAL = new AtomicReference<>();
private static final AtomicReference<Terminal> TERMINAL_OVERRIDE = new AtomicReference<>();

private String name;
private InputStream in;
Expand Down Expand Up @@ -260,7 +261,11 @@ public TerminalBuilder paused(boolean paused) {
}

public Terminal build() throws IOException {
Terminal terminal = doBuild();
Terminal override = TERMINAL_OVERRIDE.get();
Terminal terminal = override != null ? override : doBuild();
if (override != null) {
Log.debug(() -> "Overriding terminal with global value set by TerminalBuilder.setTerminalOverride");
}
Log.debug(() -> "Using terminal " + terminal.getClass().getSimpleName());
if (terminal instanceof AbstractPosixTerminal) {
Log.debug(() -> "Using pty " + ((AbstractPosixTerminal) terminal).getPty().getClass().getSimpleName());
Expand Down Expand Up @@ -486,4 +491,37 @@ private static Boolean getBoolean(String name, Boolean def) {
private <S> S load(Class<S> clazz) {
return ServiceLoader.load(clazz, clazz.getClassLoader()).iterator().next();
}

/**
* Allows an application to override the result of {@link #build()}. The
* intended use case is to allow a container or server application to control
* an embedded application that uses a LineReader that uses Terminal
* constructed with TerminalBuilder.build but provides no public api for setting
* the <code>LineReader</code> of the {@link Terminal}. For example, the sbt
* build tool uses a <code>LineReader</code> to implement an interactive shell.
* One of its supported commands is <code>console</code> which invokes
* the scala REPL. The scala REPL also uses a <code>LineReader</code> and it
* is necessary to override the {@link Terminal} used by the the REPL to
* share the same {@link Terminal} instance used by sbt.
*
* <p>
* When this method is called with a non-null {@link Terminal}, all subsequent
* calls to {@link #build()} will return the provided {@link Terminal} regardless
* of how the {@link TerminalBuilder} was constructed. The default behavior
* of {@link TerminalBuilder} can be restored by calling setTerminalOverride
* with a null {@link Terminal}
* </p>
*
* <p>
* Usage of setTerminalOverride should be restricted to cases where it
* isn't possible to update the api of the nested application to accept
* a {@link Terminal instance}.
* </p>
*
* @param terminal the {@link Terminal} to globally override
*/
@Deprecated
public static final void setTerminalOverride(final Terminal terminal) {
TERMINAL_OVERRIDE.set(terminal);
}
}

0 comments on commit fdc2fb5

Please sign in to comment.