JSR-223 (Scripting for the Java Platform) implementation for Apache Groovy
npx @tessl/cli install tessl/maven-org-apache-groovy--groovy-jsr223@5.0.0A JSR-223 (Java Specification Request 223 - Scripting for the Java Platform) compliant script engine implementation for Apache Groovy. This package enables Java applications to execute Groovy scripts through the standard javax.script API, providing seamless integration between Java and Groovy code.
<dependency>
<groupId>org.apache.groovy</groupId>
<artifactId>groovy-jsr223</artifactId>
<version>5.0.0</version>
</dependency>implementation 'org.apache.groovy:groovy-jsr223:5.0.0'import javax.script.ScriptEngineManager;
import javax.script.ScriptEngine;
import javax.script.ScriptException;
import javax.script.Compilable;
import javax.script.CompiledScript;
import javax.script.Invocable;
import org.codehaus.groovy.jsr223.GroovyScriptEngineFactory;
import org.codehaus.groovy.jsr223.GroovyScriptEngineImpl;
import org.codehaus.groovy.control.CompilationFailedException;For extension methods:
import org.codehaus.groovy.jsr223.ScriptExtensions;
import org.codehaus.groovy.jsr223.ScriptStaticExtensions;
import groovy.lang.Binding;import javax.script.*;
// Get Groovy script engine through ScriptEngineManager
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("groovy");
// Execute simple script
Object result = engine.eval("2 + 3");
System.out.println(result); // Outputs: 5
// Execute script with variables
engine.put("x", 10);
engine.put("y", 20);
Object sum = engine.eval("x + y");
System.out.println(sum); // Outputs: 30
// Execute script with more complex logic
String script = """
def greet(name) {
return "Hello, ${name}!"
}
greet('World')
""";
Object greeting = engine.eval(script);
System.out.println(greeting); // Outputs: Hello, World!
// Compile and reuse scripts
Compilable compilable = (Compilable) engine;
CompiledScript compiled = compilable.compile("Math.sqrt(x)");
engine.put("x", 16);
Object result1 = compiled.eval(); // 4.0
engine.put("x", 25);
Object result2 = compiled.eval(); // 5.0The Groovy JSR-223 implementation consists of several key components:
This design provides full JSR-223 compliance while leveraging Groovy's dynamic capabilities and maintaining compatibility with existing Java scripting infrastructure.
Factory class for creating Groovy script engines and providing engine metadata.
public class GroovyScriptEngineFactory implements ScriptEngineFactory {
public String getEngineName();
public String getEngineVersion();
public String getLanguageName();
public String getLanguageVersion();
public List<String> getExtensions();
public List<String> getMimeTypes();
public List<String> getNames();
public Object getParameter(String key);
public ScriptEngine getScriptEngine();
public String getMethodCallSyntax(String obj, String method, String... args);
public String getOutputStatement(String toDisplay);
public String getProgram(String... statements);
}Usage Example:
GroovyScriptEngineFactory factory = new GroovyScriptEngineFactory();
// Get engine metadata
System.out.println(factory.getEngineName()); // "Groovy Scripting Engine"
System.out.println(factory.getLanguageName()); // "Groovy"
System.out.println(factory.getExtensions()); // ["groovy"]
System.out.println(factory.getMimeTypes()); // ["application/x-groovy"]
// Generate syntax examples
String methodCall = factory.getMethodCallSyntax("obj", "doSomething", "arg1", "arg2");
System.out.println(methodCall); // "obj.doSomething(arg1,arg2)"
String output = factory.getOutputStatement("Hello World");
System.out.println(output); // "println(\"Hello World\")"
// Create script engine
ScriptEngine engine = factory.getScriptEngine();Main JSR-223 script engine providing evaluation, compilation, and invocation capabilities.
public class GroovyScriptEngineImpl extends AbstractScriptEngine
implements Compilable, Invocable {
// Constructors
public GroovyScriptEngineImpl();
public GroovyScriptEngineImpl(GroovyClassLoader classLoader);
// ScriptEngine methods
public Object eval(Reader reader, ScriptContext ctx) throws ScriptException;
public Object eval(String script, ScriptContext ctx) throws ScriptException;
public Bindings createBindings();
public ScriptEngineFactory getFactory();
// Compilable methods
public CompiledScript compile(String scriptSource) throws ScriptException;
public CompiledScript compile(Reader reader) throws ScriptException;
// Invocable methods
public Object invokeFunction(String name, Object... args)
throws ScriptException, NoSuchMethodException;
public Object invokeMethod(Object thiz, String name, Object... args)
throws ScriptException, NoSuchMethodException;
public <T> T getInterface(Class<T> clazz);
public <T> T getInterface(Object thiz, Class<T> clazz);
// Script class management methods
public Class<?> getScriptClass(String script) throws CompilationFailedException;
public Class<?> getScriptClass(String script, ScriptContext context)
throws CompilationFailedException;
// Utility methods
public void setClassLoader(GroovyClassLoader classLoader);
public GroovyClassLoader getClassLoader();
}Usage Example:
// Direct instantiation
GroovyScriptEngineImpl engine = new GroovyScriptEngineImpl();
// Function invocation
engine.eval("def multiply(a, b) { return a * b }");
Invocable invocable = (Invocable) engine;
Object result = invocable.invokeFunction("multiply", 5, 3); // 15
// Method invocation on objects
engine.eval("""
class Calculator {
def add(a, b) { return a + b }
def subtract(a, b) { return a - b }
}
calc = new Calculator()
""");
Object calculator = engine.get("calc");
Object sum = invocable.invokeMethod(calculator, "add", 10, 5); // 15
// Interface proxy
engine.eval("""
def hello(name) { return "Hello, ${name}!" }
def goodbye(name) { return "Goodbye, ${name}!" }
""");
interface Greeter {
String hello(String name);
String goodbye(String name);
}
Greeter greeter = invocable.getInterface(Greeter.class);
System.out.println(greeter.hello("Alice")); // "Hello, Alice!"
// Access to script class compilation
GroovyScriptEngineImpl groovyEngine = (GroovyScriptEngineImpl) engine;
Class<?> scriptClass = groovyEngine.getScriptClass("def hello() { 'Hello World' }");
System.out.println(scriptClass.getName()); // Generated class name
// Script class compilation with context
ScriptContext context = engine.getContext();
context.setAttribute(ScriptEngine.FILENAME, "MyScript.groovy", ScriptContext.ENGINE_SCOPE);
Class<?> namedClass = groovyEngine.getScriptClass("def greet() { 'Greetings!' }", context);Pre-compiled Groovy scripts for efficient repeated execution.
public class GroovyCompiledScript extends CompiledScript {
public GroovyCompiledScript(GroovyScriptEngineImpl engine, Class<?> clazz);
public Object eval(ScriptContext context) throws ScriptException;
public ScriptEngine getEngine();
}Usage Example:
ScriptEngine engine = new ScriptEngineManager().getEngineByName("groovy");
Compilable compilable = (Compilable) engine;
// Compile script once
CompiledScript compiled = compilable.compile("""
def processData(data) {
return data.collect { it * 2 }.findAll { it > 10 }
}
processData(input)
""");
// Execute multiple times with different inputs
engine.put("input", Arrays.asList(1, 2, 5, 8, 10));
Object result1 = compiled.eval(); // [16, 20]
engine.put("input", Arrays.asList(3, 6, 9, 12));
Object result2 = compiled.eval(); // [12, 18, 24]Extension methods providing enhanced integration between ScriptEngine and Groovy Binding.
public class ScriptExtensions {
public static Object eval(ScriptEngine self, String script, Binding binding)
throws ScriptException;
public static Object eval(ScriptEngine self, Reader reader, Binding binding)
throws ScriptException;
}Usage Example:
import static org.codehaus.groovy.jsr223.ScriptExtensions.eval;
ScriptEngine engine = new ScriptEngineManager().getEngineByName("groovy");
Binding binding = new Binding();
// Set variables in Groovy binding
binding.setVariable("name", "Alice");
binding.setVariable("age", 25);
// Execute script with binding integration
Object result = eval(engine, "\"${name} is ${age} years old\"", binding);
System.out.println(result); // "Alice is 25 years old"
// Variables modified in script are reflected back in binding
eval(engine, "name = name.toUpperCase(); newVar = 'created'", binding);
System.out.println(binding.getVariable("name")); // "ALICE"
System.out.println(binding.getVariable("newVar")); // "created"Static extension methods for ScriptEngineManager providing dynamic engine access.
public class ScriptStaticExtensions {
public static ScriptEngine $static_propertyMissing(
ScriptEngineManager self, String languageShortName);
}Usage Example:
// This enables dynamic property access syntax in Groovy code:
// manager.groovy instead of manager.getEngineByName("groovy")
// Standard JSR-223 approach
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine groovyEngine = manager.getEngineByName("groovy");
ScriptEngine jsEngine = manager.getEngineByName("javascript");
// With static extensions (in Groovy code):
// ScriptEngine groovyEngine = manager.groovy
// ScriptEngine jsEngine = manager.javascriptThe implementation provides comprehensive error handling for various scenarios:
ScriptEngine engine = new ScriptEngineManager().getEngineByName("groovy");
try {
// Script compilation errors
engine.eval("def invalid syntax here");
} catch (ScriptException e) {
System.err.println("Script compilation failed: " + e.getMessage());
}
try {
// Function invocation errors
Invocable invocable = (Invocable) engine;
invocable.invokeFunction("nonExistentFunction");
} catch (NoSuchMethodException e) {
System.err.println("Function not found: " + e.getMessage());
} catch (ScriptException e) {
System.err.println("Script execution failed: " + e.getMessage());
}
try {
// Invalid parameters
GroovyScriptEngineFactory factory = new GroovyScriptEngineFactory();
factory.getParameter("INVALID_KEY");
} catch (IllegalArgumentException e) {
System.err.println("Invalid parameter key: " + e.getMessage());
}Common Exceptions:
ScriptException: Script compilation or execution errorsNoSuchMethodException: Missing function/method invocationsIllegalArgumentException: Invalid parameters or argumentsNullPointerException: Null method names in invocationsControl memory management for global closures:
ScriptEngine engine = new ScriptEngineManager().getEngineByName("groovy");
// Configure closure reference strength
engine.getContext().setAttribute(
"#jsr223.groovy.engine.keep.globals",
"weak", // Options: "hard", "soft", "weak", "phantom"
ScriptContext.ENGINE_SCOPE
);Use custom ClassLoader for script compilation:
GroovyClassLoader customLoader = new GroovyClassLoader();
GroovyScriptEngineImpl engine = new GroovyScriptEngineImpl(customLoader);
// Or modify existing engine
engine.setClassLoader(customLoader);The Groovy JSR-223 implementation provides:
// Multiple threads can safely share a compiled script
CompiledScript compiled = ((Compilable) engine).compile("Math.random()");
// Each thread should use its own ScriptContext for variable isolation
ExecutorService executor = Executors.newFixedThreadPool(10);
for (int i = 0; i < 100; i++) {
executor.submit(() -> {
try {
ScriptContext context = new SimpleScriptContext();
Object result = compiled.eval(context);
System.out.println("Random: " + result);
} catch (ScriptException e) {
e.printStackTrace();
}
});
}// Core JSR-223 interfaces implemented
interface ScriptEngineFactory {
String getEngineName();
String getEngineVersion();
String getLanguageName();
String getLanguageVersion();
List<String> getExtensions();
List<String> getMimeTypes();
List<String> getNames();
Object getParameter(String key);
ScriptEngine getScriptEngine();
String getMethodCallSyntax(String obj, String method, String... args);
String getOutputStatement(String toDisplay);
String getProgram(String... statements);
}
interface ScriptEngine {
Object eval(String script) throws ScriptException;
Object eval(Reader reader) throws ScriptException;
Object eval(String script, ScriptContext context) throws ScriptException;
Object eval(Reader reader, ScriptContext context) throws ScriptException;
void put(String key, Object value);
Object get(String key);
Bindings getBindings(int scope);
void setBindings(Bindings bindings, int scope);
Bindings createBindings();
ScriptContext getContext();
void setContext(ScriptContext context);
ScriptEngineFactory getFactory();
}
interface Compilable {
CompiledScript compile(String script) throws ScriptException;
CompiledScript compile(Reader script) throws ScriptException;
}
interface Invocable {
Object invokeMethod(Object thiz, String name, Object... args)
throws ScriptException, NoSuchMethodException;
Object invokeFunction(String name, Object... args)
throws ScriptException, NoSuchMethodException;
<T> T getInterface(Class<T> clazz);
<T> T getInterface(Object thiz, Class<T> clazz);
}
abstract class CompiledScript {
public abstract Object eval(ScriptContext context) throws ScriptException;
public Object eval() throws ScriptException;
public abstract ScriptEngine getEngine();
}
// Groovy-specific types
class Binding {
public Binding();
public Binding(Map variables);
public Object getVariable(String name);
public void setVariable(String name, Object value);
public Map getVariables();
}
class GroovyClassLoader extends ClassLoader {
public GroovyClassLoader();
public GroovyClassLoader(ClassLoader parent);
public Class<?> parseClass(String text, String fileName);
}
// Exception types
class CompilationFailedException extends RuntimeException {
public CompilationFailedException(String message);
public CompilationFailedException(String message, Throwable cause);
}