Skip Top Navigation Bar
Previous in the Series
Current Tutorial
Advanced JShell Usage
Next in the Series

Previous in the Series: JShell - The Java Shell Tool

Next in the Series: Jar - the Archive Tool

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:

Visual JShell

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


Previous in the Series
Current Tutorial
Advanced JShell Usage
Next in the Series

Previous in the Series: JShell - The Java Shell Tool

Next in the Series: Jar - the Archive Tool