google-site-verification: google61fe8ba583a51912.html
Java REPL (jshell) Scripting in Java: Interactive Development and Automation

Introduction

Java REPL (Read-Eval-Print Loop) through jshell, introduced in Java 9, revolutionized how developers interact with Java. It provides an interactive environment for rapid prototyping, testing, and learning Java without the compile-run cycle. This article explores jshell capabilities, integration with applications, and advanced scripting techniques.

What is jshell?

jshell is Java's official REPL tool that allows you to:

  • Execute Java code snippets instantly
  • Test ideas without creating full classes
  • Import and use existing libraries
  • Define methods, classes, and variables on the fly
  • Get immediate feedback with error messages

Getting Started with jshell

Basic Usage

# Start jshell jshell # Start with verbose feedback jshell -v # Start with pre-loaded imports jshell --startup DEFAULT --startup PRINTING

Common jshell Commands

// In jshell environment jshell> /help jshell> /list jshell> /vars jshell> /methods jshell> /types jshell> /imports jshell> /history jshell> /save filename.java jshell> /open filename.java jshell> /reset jshell> /exit

Programming jshell from Java Applications

Basic jshell Integration

import jdk.jshell.*; import jdk.jshell.spi.ExecutionControl; import java.util.List; import java.util.Locale; public class BasicJShellRunner { public static void main(String[] args) { try (JShell jshell = JShell.create()) { // Execute simple expressions List<SnippetEvent> events = jshell.eval("int x = 10;"); for (SnippetEvent event : events) { System.out.println("Snippet: " + event.snippet().source()); System.out.println("Value: " + event.value()); System.out.println("Status: " + event.status()); } // Execute another expression using previous variable events = jshell.eval("x * 2 + 5"); if (!events.isEmpty() && events.get(0).value() != null) { System.out.println("Result: " + events.get(0).value()); } } } }

Advanced jshell Manager

import jdk.jshell.*; import jdk.jshell.Snippet.Status; import java.util.*; import java.util.stream.Collectors; public class JShellManager implements AutoCloseable { private final JShell jshell; private final List<SnippetEvent> history; public JShellManager() { this.jshell = JShell.create(); this.history = new ArrayList<>(); // Set up default imports setupDefaultImports(); } private void setupDefaultImports() { eval("import java.util.*;"); eval("import java.util.stream.*;"); eval("import java.time.*;"); eval("import java.math.*;"); eval("import java.io.*;"); } public ExecutionResult execute(String code) { List<SnippetEvent> events = jshell.eval(code); history.addAll(events); ExecutionResult result = new ExecutionResult(); result.setEvents(events); result.setSuccessful(events.stream().allMatch(this::isSuccessful)); return result; } private boolean isSuccessful(SnippetEvent event) { return event.status() == Status.VALID && event.exception() == null && !event.status().isDefined(); } public List<VariableSnippet> getVariables() { return jshell.variables().collect(Collectors.toList()); } public List<MethodSnippet> getMethods() { return jshell.methods().collect(Collectors.toList()); } public List<TypeDeclSnippet> getTypes() { return jshell.types().collect(Collectors.toList()); } public void reset() { jshell.stop(); jshell.snippets().forEach(jshell::drop); history.clear(); setupDefaultImports(); } public String getHistory() { return history.stream() .map(event -> event.snippet().source()) .collect(Collectors.joining("\n")); } @Override public void close() { jshell.close(); } public static class ExecutionResult { private List<SnippetEvent> events; private boolean successful; private String output; // Getters and setters public List<SnippetEvent> getEvents() { return events; } public void setEvents(List<SnippetEvent> events) { this.events = events; } public boolean isSuccessful() { return successful; } public void setSuccessful(boolean successful) { this.successful = successful; } public String getOutput() { return output; } public void setOutput(String output) { this.output = output; } } }

Interactive Java Console Application

import jdk.jshell.*; import java.util.Scanner; import java.util.List; public class InteractiveJavaConsole { private final JShellManager jshellManager; private final Scanner scanner; public InteractiveJavaConsole() { this.jshellManager = new JShellManager(); this.scanner = new Scanner(System.in); } public void start() { System.out.println("Java REPL Console - Type 'exit' to quit, 'help' for commands"); while (true) { System.out.print("jshell> "); String input = scanner.nextLine().trim(); if ("exit".equalsIgnoreCase(input)) { break; } else if ("help".equalsIgnoreCase(input)) { showHelp(); } else if ("reset".equalsIgnoreCase(input)) { jshellManager.reset(); System.out.println("Session reset."); } else if ("vars".equalsIgnoreCase(input)) { showVariables(); } else if ("methods".equalsIgnoreCase(input)) { showMethods(); } else if ("history".equalsIgnoreCase(input)) { showHistory(); } else if (!input.isEmpty()) { executeCode(input); } } jshellManager.close(); System.out.println("Goodbye!"); } private void showHelp() { System.out.println("Available commands:"); System.out.println(" exit - Exit the REPL"); System.out.println(" help - Show this help"); System.out.println(" reset - Reset the session"); System.out.println(" vars - Show defined variables"); System.out.println(" methods - Show defined methods"); System.out.println(" history - Show command history"); System.out.println(" [code] - Execute Java code"); } private void showVariables() { List<VariableSnippet> variables = jshellManager.getVariables(); if (variables.isEmpty()) { System.out.println("No variables defined."); } else { variables.forEach(var -> { System.out.println(var.typeName() + " " + var.name()); }); } } private void showMethods() { List<MethodSnippet> methods = jshellManager.getMethods(); if (methods.isEmpty()) { System.out.println("No methods defined."); } else { methods.forEach(method -> { System.out.println(method.source()); }); } } private void showHistory() { System.out.println(jshellManager.getHistory()); } private void executeCode(String code) { try { JShellManager.ExecutionResult result = jshellManager.execute(code); for (SnippetEvent event : result.getEvents()) { if (event.value() != null) { System.out.println("==> " + event.value()); } if (event.exception() != null) { System.out.println("Error: " + event.exception().getMessage()); } } } catch (Exception e) { System.out.println("Execution error: " + e.getMessage()); } } public static void main(String[] args) { new InteractiveJavaConsole().start(); } }

Script Execution Engine

import jdk.jshell.*; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.List; public class JavaScriptEngine { private final JShell jshell; private final List<ExecutionListener> listeners; public JavaScriptEngine() { this.jshell = JShell.builder() .executionEngine("local") .build(); this.listeners = new ArrayList<>(); setupEventHandlers(); } private void setupEventHandlers() { jshell.onSnippetEvent(event -> { notifyListeners("SnippetEvent: " + event.snippet().source() + " - Status: " + event.status()); }); } public ScriptExecutionResult executeScript(String script) { ScriptExecutionResult result = new ScriptExecutionResult(); List<String> outputs = new ArrayList<>(); List<String> errors = new ArrayList<>(); String[] lines = script.split("\n"); for (String line : lines) { if (line.trim().isEmpty()) continue; try { List<SnippetEvent> events = jshell.eval(line); for (SnippetEvent event : events) { if (event.value() != null) { outputs.add(event.value()); } if (event.exception() != null) { errors.add("Line: " + line + " - Error: " + event.exception().getMessage()); } } } catch (Exception e) { errors.add("Line: " + line + " - Error: " + e.getMessage()); } } result.setOutputs(outputs); result.setErrors(errors); result.setSuccess(errors.isEmpty()); return result; } public ScriptExecutionResult executeScriptFile(Path filePath) throws Exception { String script = Files.readString(filePath); return executeScript(script); } public void addListener(ExecutionListener listener) { listeners.add(listener); } private void notifyListeners(String message) { listeners.forEach(listener -> listener.onEvent(message)); } public void close() { jshell.close(); } public static class ScriptExecutionResult { private List<String> outputs; private List<String> errors; private boolean success; // Getters and setters public List<String> getOutputs() { return outputs; } public void setOutputs(List<String> outputs) { this.outputs = outputs; } public List<String> getErrors() { return errors; } public void setErrors(List<String> errors) { this.errors = errors; } public boolean isSuccess() { return success; } public void setSuccess(boolean success) { this.success = success; } } public interface ExecutionListener { void onEvent(String message); } }

Practical Use Cases

1. Data Analysis Scripting

public class DataAnalysisRunner { public static void main(String[] args) throws Exception { JavaScriptEngine engine = new JavaScriptEngine(); String analysisScript = """ // Data analysis example import java.util.*; import java.util.stream.*; List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); double average = numbers.stream() .mapToInt(Integer::intValue) .average() .orElse(0.0); int sum = numbers.stream() .mapToInt(Integer::intValue) .sum(); List<Integer> evenNumbers = numbers.stream() .filter(n -> n % 2 == 0) .collect(Collectors.toList()); System.out.println("Numbers: " + numbers); System.out.println("Average: " + average); System.out.println("Sum: " + sum); System.out.println("Even numbers: " + evenNumbers); """; ScriptExecutionResult result = engine.executeScript(analysisScript); System.out.println("Execution successful: " + result.isSuccess()); result.getOutputs().forEach(System.out::println); engine.close(); } }

2. Mathematical Calculations

public class MathCalculationRunner { public static void runCalculations() { try (JShell jshell = JShell.create()) { // Import math utilities jshell.eval("import java.math.*;"); jshell.eval("import static java.lang.Math.*;"); // Define mathematical functions jshell.eval(""" double calculateCircleArea(double radius) { return PI * radius * radius; } """); jshell.eval(""" double calculateCompoundInterest(double principal, double rate, int years) { return principal * pow(1 + rate/100, years); } """); // Execute calculations jshell.eval("double area = calculateCircleArea(5.0);"); jshell.eval("double futureValue = calculateCompoundInterest(1000, 5, 10);"); // Print results jshell.variables().forEach(var -> { if (var.name().equals("area") || var.name().equals("futureValue")) { jshell.eval("System.out.println(\"" + var.name() + " = \" + " + var.name() + ");"); } }); } } }

3. API Testing and Prototyping

public class APITestingScript { public static void testAPI() throws Exception { JavaScriptEngine engine = new JavaScriptEngine(); String apiTestScript = """ import java.net.http.*; import java.net.*; import com.google.gson.*; // Simple HTTP client setup HttpClient client = HttpClient.newHttpClient(); // Test GET request HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://api.coingecko.com/api/v3/ping")) .GET() .build(); HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString()); System.out.println("Status Code: " + response.statusCode()); System.out.println("Response: " + response.body()); // Parse JSON response Gson gson = new Gson(); JsonObject jsonResponse = gson.fromJson(response.body(), JsonObject.class); System.out.println("Parsed JSON: " + jsonResponse); """; // Note: This requires Gson in classpath ScriptExecutionResult result = engine.executeScript(apiTestScript); if (!result.isSuccess()) { System.out.println("Errors occurred:"); result.getErrors().forEach(System.err::println); } engine.close(); } }

Advanced jshell Features

Custom Execution Control

import jdk.jshell.execution.*; import jdk.jshell.spi.ExecutionControl; import jdk.jshell.spi.ExecutionEnv; public class CustomJShellEnvironment { public static JShell createCustomJShell() { return JShell.builder() .executionEngine("local", new Object[] { new DirectExecutionControl() { @Override public void load(ExecutionEnv env) { super.load(env); // Custom initialization System.out.println("Custom JShell environment loaded"); } @Override public String invoke(String classname, String methodName) throws ExecutionControl.UserException { // Custom method invocation handling System.out.println("Invoking: " + classname + "." + methodName); return super.invoke(classname, methodName); } } }) .build(); } }

Snippet Management

import jdk.jshell.*; import java.util.*; public class SnippetManager { private final JShell jshell; private final Map<String, Snippet> snippets; public SnippetManager(JShell jshell) { this.jshell = jshell; this.snippets = new HashMap<>(); } public String executeAndStore(String code, String name) { List<SnippetEvent> events = jshell.eval(code); for (SnippetEvent event : events) { if (event.status() == Status.VALID) { snippets.put(name, event.snippet()); return event.value(); } } return null; } public Snippet getSnippet(String name) { return snippets.get(name); } public void updateSnippet(String name, String newCode) { Snippet oldSnippet = snippets.get(name); if (oldSnippet != null) { jshell.drop(oldSnippet); } executeAndStore(newCode, name); } public List<String> getSnippetNames() { return new ArrayList<>(snippets.keySet()); } }

Integration with Build Tools

Maven jshell Plugin

<!-- Add to pom.xml --> <build> <plugins> <plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>exec-maven-plugin</artifactId> <version>3.1.0</version> <executions> <execution> <goals> <goal>java</goal> </goals> </execution> </executions> <configuration> <mainClass>com.example.JShellRunner</mainClass> </configuration> </plugin> </plugins> </build>

Gradle jshell Task

// build.gradle task jshell(type: JavaExec) { group = 'Execution' description = 'Run jshell with project classpath' classpath = sourceSets.main.runtimeClasspath mainClass = 'jdk.jshell.JShell' standardInput = System.in } task runScript(type: JavaExec) { group = 'Execution' description = 'Run custom jshell script' classpath = sourceSets.main.runtimeClasspath mainClass = 'com.example.JShellScriptRunner' args 'script.jsh' }

Best Practices

1. Error Handling

public class SafeJShellExecutor { public static Optional<String> safeEval(JShell jshell, String code) { try { List<SnippetEvent> events = jshell.eval(code); for (SnippetEvent event : events) { if (event.exception() != null) { System.err.println("Error: " + event.exception().getMessage()); return Optional.empty(); } if (event.value() != null) { return Optional.of(event.value()); } } } catch (Exception e) { System.err.println("Execution failed: " + e.getMessage()); } return Optional.empty(); } }

2. Resource Management

public class ResourceAwareJShell implements AutoCloseable { private final JShell jshell; private final List<AutoCloseable> resources; public ResourceAwareJShell() { this.jshell = JShell.create(); this.resources = new ArrayList<>(); } public void registerResource(AutoCloseable resource) { resources.add(resource); } @Override public void close() { // Close resources in reverse order Collections.reverse(resources); for (AutoCloseable resource : resources) { try { resource.close(); } catch (Exception e) { System.err.println("Failed to close resource: " + e.getMessage()); } } jshell.close(); } }

3. Security Considerations

public class SecureJShellEnvironment { public static JShell createSecureJShell() { return JShell.builder() .remoteVMOptions( "-Djava.security.manager", "-Djava.security.policy==/path/to/security.policy" ) .build(); } }

Conclusion

Java REPL (jshell) provides powerful capabilities for:

  1. Rapid Prototyping - Test ideas without full project setup
  2. Interactive Learning - Learn Java features interactively
  3. Scripting - Write Java scripts for automation
  4. API Testing - Quickly test libraries and APIs
  5. Data Analysis - Perform quick calculations and data manipulation

By integrating jshell into your applications, you can create interactive tools, educational software, and rapid prototyping environments that leverage the full power of Java in an interactive manner.

The key advantages include immediate feedback, reduced boilerplate, and the ability to experiment with Java features in isolation, making it an invaluable tool for both beginners and experienced Java developers.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper