Advanced JShell Usage
Overview
You can use jshell to evaluate code using Java's standard APIs, but you can also prototype programs
that require external dependencies, define a sequence of snippets and jshell
commands in a file and pass it to the tool.
This tutorial covers how to work with JavaFX in jshell
, using predefined scripts or creating your own, and finally how to produce a
jar
file inside a jshell
session.
Load External Libraries to JShell
In a jshell
session you can prototype code that needs external libraries, and you can set access to those through the class path (--class-path
).
To illustrate how you can achieve that, let's build in a jshell
session a visual tool that evaluates the type of Java expressions using JavaFX and the JShell API.
You will need a JavaFX SDK to achieve the next steps in this tutorial, so make sure to check out the download section of
the JavaFX series and find out how you can obtain one.
JavaFX is a standalone component and builds on top of a Java Development Kit and you can use its components in a jshell
session by adding each to the classpath:
# macOS and Linux compatible command
jshell --class-path /path/to/javafx-sdk/lib/javafx.controls.jar:/path/to/javafx-sdk/lib/javafx.graphics.jar
#windows compatible command
jshell --class-path "C:\path\to\javafx-sdk\lib\javafx.controls.jar;\path\to\javafx-sdk\lib\javafx.graphics.jar"
or in an existing jshell
session you can run:
jshell> /env -class-path /path/to/javafx-sdk/lib/javafx.controls.jar:/path/to/javafx-sdk/lib/javafx.graphics.jar
Yet, starting with Java 9 you can import JavaFX as modules through --module-path
option:
# macOS and Linux compatible command
jshell --module-path /path/to/javafx-sdk/lib --add-modules javafx.controls,javafx.graphics
#windows compatible command
jshell --module-path "C:\path\to\javafx-sdk\lib" --add-modules javafx.controls,javafx.graphics
Or, in an existing jshell
session just invoke:
jshell> /env --module-path /path/to/javafx-sdk/lib --add-modules javafx.controls,javafx.graphics
Let's create the visual tool with JavaFX using the jshell
editor:
jshell> /edit
and copy-paste the following code:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.*;
import javafx.stage.Stage;
import java.util.List;
import jdk.jshell.*;
public class VisualJShell extends Application {
@Override
public void start(Stage stage) {
JShell shell = JShell.builder().build();
TextField input = new TextField();
ListView<String> results = new ListView<>();
input.setOnKeyPressed(evt -> {
if (evt.getCode().equals(KeyCode.ENTER)) {
List<SnippetEvent> events = shell.eval(input.getText());
events.stream().map(VisualJShell::convert).forEach(s -> results.getItems().add(s));
}
});
BorderPane pane = new BorderPane();
pane.setTop(new VBox(input));
pane.setCenter(results);
Scene scene = new Scene(pane);
stage.setTitle("VisualJShell");
stage.setScene(scene);
stage.show();
}
private static String convert(SnippetEvent e) {
return switch (e.snippet()) {
case ExpressionSnippet expression -> String.join(" ", expression.typeName(), expression.name(), e.value());
case ErroneousSnippet error -> String.join(" ", error.probableKind().name());
case StatementSnippet statement -> String.join(" ", statement.kind().name(), e.value());
case VarSnippet var -> String.join(" ", var.typeName(), var.name(), e.value());
case PersistentSnippet persistent -> String.join(" ", persistent.name(), e.value());
case Snippet generic -> String.join(" ", generic.kind().name(), generic.source());
};
}
public static void main(String[] args) {
launch();
}
}
The above code allows you to input an expression (eg Boolean.TRUE
) and see its type upon pressing ENTER on your keyboard.
In the visual editor click Accept
and then Exit
. In your command line you will observe the following message:
jshell> /edit
ƒ created class VisualJShell
All that is left to do in order to try the tool is to instantiate it:
jshell> VisualJShell vjshell = new VisualJShell();
and call its main
method:
jshell> vjshell.main(null);
You should see an application similar to:
Next, let's see how you can save your work in a jshell
session and reuse it in the future.
Create and Load Custom JShell Scripts
In your jshell
session, you can choose to save the current active snippets:
jshell> /save setup-visual-jshell.jsh
The content of your setup-visual-jshell.jsh
script would look similar too:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.*;
import javafx.stage.Stage;
import java.util.List;
import jdk.jshell.*;
public class VisualJShell extends Application {
@Override
public void start(Stage stage) {
JShell shell = JShell.builder().build();
TextField input = new TextField();
ListView<String> results = new ListView<>();
input.setOnKeyPressed(evt -> {
if (evt.getCode().equals(KeyCode.ENTER)) {
List<SnippetEvent> events = shell.eval(input.getText());
events.stream().map(VisualJShell::convert).forEach(s -> results.getItems().add(s));
}
});
BorderPane pane = new BorderPane();
pane.setTop(new VBox(input));
pane.setCenter(results);
Scene scene = new Scene(pane);
stage.setTitle("VisualJShell");
stage.setScene(scene);
stage.show();
}
private static String convert(SnippetEvent e) {
return switch (e.snippet()) {
case ExpressionSnippet expression -> String.join(" ", expression.typeName(), expression.name(), e.value());
case ErroneousSnippet error -> String.join(" ", error.probableKind().name());
case StatementSnippet statement -> String.join(" ", statement.kind().name(), e.value());
case VarSnippet var -> String.join(" ", var.typeName(), var.name(), e.value());
case PersistentSnippet persistent -> String.join(" ", persistent.name(), e.value());
case Snippet generic -> String.join(" ", generic.kind().name(), generic.source());
};
}
public static void main(String[] args) {
launch();
}
}
VisualJShell vjshell = new VisualJShell();
vjshell.main(null);
Now you may exit your jshell
session with:
jshell> /exit
jshell
has a series of predefined scripts, as detailed in its commands.
You can set these default scripts at the startup of your jshell
session:
jshell --startup DEFAULT --startup PRINTING
or invoke them while in an active jshell
session:
jshell> /open DEFAULT
jshell> /open PRINTING
Yet, you can choose to save the entire history of the snippets and commands, both valid and invalid by running the following command:
jshell> /save -history myhistory.jsh
As there is a saved setup-visual-jshell.jsh
script, you can load it when starting a new jshell
session by running:
# macOS and Linux compatible command
jshell setup-visual-jshell.jsh --module-path /path/to/javafx-sdk/lib --add-modules javafx.controls,javafx.graphics
#windows compatible command
jshell setup-visual-jshell.jsh --module-path "C:\path\to\javafx-sdk\lib" --add-modules javafx.controls,javafx.graphics
As a result of running the previous command(s), you will be able to launch once again the visual tool and modify it, if needed.
⚠️ Be careful when executing remote scripts in
jshell
: you should only load a remote script from a site you trust!
Finally, let's create a jar file in a jshell
session and launch it whenever you need it.
How to Use JDK Tools Inside JShell
A series of JDK tools are available in a jshell
session by invoking the TOOLING
script:
jshell> /open TOOLING
To verify what tools are available, you can invoke the tools()
method:
jshell> tools()
and your output would be similar to:
jar
javac
javadoc
javap
jdeps
jlink
jmod
jpackage
In jshell
, you can run an arbitrary tool by passing its name and command-line arguments to the run(String, String...)
method:
jshell> run("javac", "-version")
21.0.2
You can also run a tool by using its dedicated wrapper method (eg javac(String...)
) and passing command-line arguments:
jshell> javac("-version")
21.0.2
To further make an executable jar file from previous code, let's create the following directory hierarchy locally:
src/
└── example/
Next, let's reset the current history and state:
jshell> /reset
and then ask jshell
to enter edit mode:
jshell> /edit
And once again copy-paste our VisualJShell
code:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.*;
import javafx.stage.Stage;
import java.util.List;
import jdk.jshell.*;
public class VisualJShell extends Application {
@Override
public void start(Stage stage) {
JShell shell = JShell.builder().build();
TextField input = new TextField();
ListView<String> results = new ListView<>();
input.setOnKeyPressed(evt -> {
if (evt.getCode().equals(KeyCode.ENTER)) {
List<SnippetEvent> events = shell.eval(input.getText());
events.stream().map(VisualJShell::convert).forEach(s -> results.getItems().add(s));
}
});
BorderPane pane = new BorderPane();
pane.setTop(new VBox(input));
pane.setCenter(results);
Scene scene = new Scene(pane);
stage.setTitle("VisualJShell");
stage.setScene(scene);
stage.show();
}
private static String convert(SnippetEvent e) {
return switch (e.snippet()) {
case ExpressionSnippet expression -> String.join(" ", expression.typeName(), expression.name(), e.value());
case ErroneousSnippet error -> String.join(" ", error.probableKind().name());
case StatementSnippet statement -> String.join(" ", statement.kind().name(), e.value());
case VarSnippet var -> String.join(" ", var.typeName(), var.name(), e.value());
case PersistentSnippet persistent -> String.join(" ", persistent.name(), e.value());
case Snippet generic -> String.join(" ", generic.kind().name(), generic.source());
};
}
public static void main(String[] args) {
launch();
}
}
and then save your work into the src/example/VisualJShell.java
file by running:
jshell> /save src/example/VisualJShell.java
According to JEP 220: Modular Run-Time Images a snippet cannot declare a package or module. Yet you will further use JDK tools that need that package declaration, so you should add it in a text editor:
package example;
This exercise aims to create a jar file for a modular application, so you'll need a module-info.java
file in your src/
folder with the following content:
module visualjshell {
requires javafx.graphics;
requires javafx.controls;
requires jdk.jshell;
exports example;
}
As the jshell
session was reset, you should open again the TOOLING
script in order to use available JDK tools:
jshell> /open TOOLING
Let's compile our VisualJShell.java
and module-info.java
:
jshell> javac("--module-path", "/path/to/javafx-sdk/lib", "--add-modules", "javafx.controls,javafx.graphics", "-d", "out/visualjshell", "src/example/VisualJShell.java", "src/module-info.java")
You can now create the jar
file that you will further use to run the application:
jshell> jar("--create", "--file", "out/visualjshell.jar", "--main-class", "example.VisualJShell", "-C", "out/visualjshell", ".")
To test the result of your efforts, go to another terminal window and run the following command:
java --module-path /path/to/javafx-sdk/lib --add-modules javafx.controls,javafx.graphics -jar out/visualjshell.jar
Congratulations! Now you have an executable jar that you can run whenever you need it.
More Learning
Last update: August 13, 2024