Skip to content

Commit

Permalink
Warn when using a deprecated provider (#967)
Browse files Browse the repository at this point in the history
  • Loading branch information
gnodet authored Apr 17, 2024
1 parent ed52706 commit 3865e5f
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 11 deletions.
24 changes: 13 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,47 +63,49 @@ JLine supports the following platforms:
* Solaris
* Windows

## JNI vs Jansi vs JNA vs FFM vs Exec
## FFM vs JNI vs Jansi vs JNA vs Exec

To perform the required operations, JLine needs to interoperate with the OS layer. This is done through the JLine
`TerminalProvider` interface. The terminal builder will automatically select a provider amongst the ones
that are available.

On the Windows platform, relying on native calls is mandatory, so you need to have a real provider (`jline-terminal-xxx` jar) registered and its dependencies available (usually the Jansi or JNA library). Failing to do so will create a `dumb` terminal with no advanced capabilities.

By default, the following order will be used.

### FFM

The [FFM](https://docs.oracle.com/en/java/javase/21/core/foreign-function-and-memory-api.html#GUID-FBE990DA-C356-46E8-9109-C75567849BA8) provider is available since JLine 3.24.0 and when running on JDK >= 21.
Since FFM is still a preview in JDK 21, the `--enable-preview --enable-native-access=ALL-UNNAMED` switches must be
used when launching the JVM.
The [FFM](https://docs.oracle.com/en/java/javase/21/core/foreign-function-and-memory-api.html#GUID-FBE990DA-C356-46E8-9109-C75567849BA8) provider is available since JLine 3.24 and when running on JDK >= 21. It's very lightweight with no additional dependencies.
With JLine 3.26, the FFM provider requires JDK 22 with the `--enable-native-access=ALL-UNNAMED` JVM option.
Note that JLine 3.24 and 3.25 uses the preview version of FFM support shipped in JDK 21 which is incompatible with the final version in JDK 22.

### JNI

Since JLine 3.24.0, JLine provides its own JNI based provider and native libraries.
Since JLine 3.24.0, JLine provides its own JNI based provider and native libraries. This is the best default choice, with no additional dependency, but it requires loading a native library.

### JANSI

The [Jansi](https://github.com/fusesource/jansi) library is a library specialized in supporting ANSI sequences in
terminals. Historically, the JNI methods used by JLine were provided by Jansi. In order to minimize the maintenance
cost, Jansi will be merged into JLine 3.25.

This provider has been deprecated in 3.26 in favor of the JNI provider.

### JNA

The [JNA](https://github.com/java-native-access/jna) library aims to provide an alternative way to access native
methods without requiring writing a full JNI native library. If JNA is in JLine's class loader, the provider may
be used.
be used. JNA is not supported on Apple M2 architectures.

This provider has been deprecated in 3.26 in favor of the FFM provider.

### Exec

The exec provider is available on Posix systems and on Windows when running under [Cygwin](https://www.cygwin.com)
or [MSys2](https://www.msys2.org). This provider launches child processes whenever the terminal is accessed
(using `Terminal.getAttributes`, `Terminal.setAttributes`, `Terminal.getSize`, `Terminal.setSize`).
This provider also does not support external terminals
This also allows the use of pseudo-terminals when dealing with non system terminals (for example when creating a terminal for an incoming connection).

On the Windows platform, relying on native calls is mandatory, so you need to have either Jansi or JNA library in your classpath along with the `jline-terminal-jansi` or `jline-terminal-jna` jar. Failing to do so will create a `dumb` terminal with no advanced capabilities.

There is no difference between JLine's support for Jansi and JNA. Both will provide the exact same behaviors. So it's a matter of preference: Jansi is a smaller but more focused library while JNA is a bigger but more generic and versatile one.
This provider also does not support external terminals (for example when creating a terminal for an incoming connection) and does not support the Windows native environment.

# Maven Usage

Expand Down
19 changes: 19 additions & 0 deletions terminal/src/main/java/org/jline/terminal/TerminalBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,15 @@
import java.nio.charset.UnsupportedCharsetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
import java.util.stream.Stream;
Expand All @@ -32,6 +35,7 @@
import org.jline.terminal.impl.DumbTerminal;
import org.jline.terminal.impl.DumbTerminalProvider;
import org.jline.terminal.spi.SystemStream;
import org.jline.terminal.spi.TerminalExt;
import org.jline.terminal.spi.TerminalProvider;
import org.jline.utils.Log;
import org.jline.utils.OSUtils;
Expand Down Expand Up @@ -101,6 +105,12 @@ public final class TerminalBuilder {
public static final String PROP_REDIRECT_PIPE_CREATION_MODE_DEFAULT =
String.join(",", PROP_REDIRECT_PIPE_CREATION_MODE_REFLECTION, PROP_REDIRECT_PIPE_CREATION_MODE_NATIVE);

public static final Set<String> DEPRECATED_PROVIDERS =
Collections.unmodifiableSet(new HashSet<>(Arrays.asList(PROP_PROVIDER_JNA, PROP_PROVIDER_JANSI)));

public static final String PROP_DISABLE_DEPRECATED_PROVIDER_WARNING =
"org.jline.terminal.disableDeprecatedProviderWarning";

//
// Terminal output control
//
Expand Down Expand Up @@ -496,6 +506,15 @@ private Terminal doBuild() throws IOException {
if (terminal == null) {
throw exception;
}
if (terminal instanceof TerminalExt) {
TerminalExt te = (TerminalExt) terminal;
if (DEPRECATED_PROVIDERS.contains(te.getProvider().name())
&& !getBoolean(PROP_DISABLE_DEPRECATED_PROVIDER_WARNING, false)) {
Log.warn("The terminal provider " + te.getProvider().name()
+ " has been deprecated, check your configuration. This warning can be disabled by setting the system property "
+ PROP_DISABLE_DEPRECATED_PROVIDER_WARNING + " to true.");
}
}
return terminal;
}

Expand Down

0 comments on commit 3865e5f

Please sign in to comment.