0
# Groovy JSR-223 Script Engine
1
2
A 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.
3
4
## Package Information
5
6
- **Package Name**: org.apache.groovy:groovy-jsr223
7
- **Package Type**: Maven
8
- **Language**: Java
9
- **Installation**:
10
- **Maven**:
11
```xml
12
<dependency>
13
<groupId>org.apache.groovy</groupId>
14
<artifactId>groovy-jsr223</artifactId>
15
<version>5.0.0</version>
16
</dependency>
17
```
18
- **Gradle**:
19
```groovy
20
implementation 'org.apache.groovy:groovy-jsr223:5.0.0'
21
```
22
23
## Core Imports
24
25
```java
26
import javax.script.ScriptEngineManager;
27
import javax.script.ScriptEngine;
28
import javax.script.ScriptException;
29
import javax.script.Compilable;
30
import javax.script.CompiledScript;
31
import javax.script.Invocable;
32
import org.codehaus.groovy.jsr223.GroovyScriptEngineFactory;
33
import org.codehaus.groovy.jsr223.GroovyScriptEngineImpl;
34
import org.codehaus.groovy.control.CompilationFailedException;
35
```
36
37
For extension methods:
38
39
```java
40
import org.codehaus.groovy.jsr223.ScriptExtensions;
41
import org.codehaus.groovy.jsr223.ScriptStaticExtensions;
42
import groovy.lang.Binding;
43
```
44
45
## Basic Usage
46
47
```java
48
import javax.script.*;
49
50
// Get Groovy script engine through ScriptEngineManager
51
ScriptEngineManager manager = new ScriptEngineManager();
52
ScriptEngine engine = manager.getEngineByName("groovy");
53
54
// Execute simple script
55
Object result = engine.eval("2 + 3");
56
System.out.println(result); // Outputs: 5
57
58
// Execute script with variables
59
engine.put("x", 10);
60
engine.put("y", 20);
61
Object sum = engine.eval("x + y");
62
System.out.println(sum); // Outputs: 30
63
64
// Execute script with more complex logic
65
String script = """
66
def greet(name) {
67
return "Hello, ${name}!"
68
}
69
greet('World')
70
""";
71
Object greeting = engine.eval(script);
72
System.out.println(greeting); // Outputs: Hello, World!
73
74
// Compile and reuse scripts
75
Compilable compilable = (Compilable) engine;
76
CompiledScript compiled = compilable.compile("Math.sqrt(x)");
77
78
engine.put("x", 16);
79
Object result1 = compiled.eval(); // 4.0
80
81
engine.put("x", 25);
82
Object result2 = compiled.eval(); // 5.0
83
```
84
85
## Architecture
86
87
The Groovy JSR-223 implementation consists of several key components:
88
89
- **GroovyScriptEngineFactory**: JSR-223 factory class that creates script engine instances and provides metadata about the Groovy scripting engine
90
- **GroovyScriptEngineImpl**: Main script engine implementation providing script evaluation, compilation, and invocation capabilities
91
- **GroovyCompiledScript**: Represents pre-compiled Groovy scripts for efficient repeated execution
92
- **Extension Classes**: Provide enhanced integration between Groovy Binding objects and JSR-223 script contexts
93
- **Service Registration**: Automatic discovery through META-INF/services mechanism
94
95
This design provides full JSR-223 compliance while leveraging Groovy's dynamic capabilities and maintaining compatibility with existing Java scripting infrastructure.
96
97
## Capabilities
98
99
### Script Engine Factory
100
101
Factory class for creating Groovy script engines and providing engine metadata.
102
103
```java { .api }
104
public class GroovyScriptEngineFactory implements ScriptEngineFactory {
105
public String getEngineName();
106
public String getEngineVersion();
107
public String getLanguageName();
108
public String getLanguageVersion();
109
public List<String> getExtensions();
110
public List<String> getMimeTypes();
111
public List<String> getNames();
112
public Object getParameter(String key);
113
public ScriptEngine getScriptEngine();
114
public String getMethodCallSyntax(String obj, String method, String... args);
115
public String getOutputStatement(String toDisplay);
116
public String getProgram(String... statements);
117
}
118
```
119
120
**Usage Example:**
121
122
```java
123
GroovyScriptEngineFactory factory = new GroovyScriptEngineFactory();
124
125
// Get engine metadata
126
System.out.println(factory.getEngineName()); // "Groovy Scripting Engine"
127
System.out.println(factory.getLanguageName()); // "Groovy"
128
System.out.println(factory.getExtensions()); // ["groovy"]
129
System.out.println(factory.getMimeTypes()); // ["application/x-groovy"]
130
131
// Generate syntax examples
132
String methodCall = factory.getMethodCallSyntax("obj", "doSomething", "arg1", "arg2");
133
System.out.println(methodCall); // "obj.doSomething(arg1,arg2)"
134
135
String output = factory.getOutputStatement("Hello World");
136
System.out.println(output); // "println(\"Hello World\")"
137
138
// Create script engine
139
ScriptEngine engine = factory.getScriptEngine();
140
```
141
142
### Script Engine Implementation
143
144
Main JSR-223 script engine providing evaluation, compilation, and invocation capabilities.
145
146
```java { .api }
147
public class GroovyScriptEngineImpl extends AbstractScriptEngine
148
implements Compilable, Invocable {
149
150
// Constructors
151
public GroovyScriptEngineImpl();
152
public GroovyScriptEngineImpl(GroovyClassLoader classLoader);
153
154
// ScriptEngine methods
155
public Object eval(Reader reader, ScriptContext ctx) throws ScriptException;
156
public Object eval(String script, ScriptContext ctx) throws ScriptException;
157
public Bindings createBindings();
158
public ScriptEngineFactory getFactory();
159
160
// Compilable methods
161
public CompiledScript compile(String scriptSource) throws ScriptException;
162
public CompiledScript compile(Reader reader) throws ScriptException;
163
164
// Invocable methods
165
public Object invokeFunction(String name, Object... args)
166
throws ScriptException, NoSuchMethodException;
167
public Object invokeMethod(Object thiz, String name, Object... args)
168
throws ScriptException, NoSuchMethodException;
169
public <T> T getInterface(Class<T> clazz);
170
public <T> T getInterface(Object thiz, Class<T> clazz);
171
172
// Script class management methods
173
public Class<?> getScriptClass(String script) throws CompilationFailedException;
174
public Class<?> getScriptClass(String script, ScriptContext context)
175
throws CompilationFailedException;
176
177
// Utility methods
178
public void setClassLoader(GroovyClassLoader classLoader);
179
public GroovyClassLoader getClassLoader();
180
}
181
```
182
183
**Usage Example:**
184
185
```java
186
// Direct instantiation
187
GroovyScriptEngineImpl engine = new GroovyScriptEngineImpl();
188
189
// Function invocation
190
engine.eval("def multiply(a, b) { return a * b }");
191
Invocable invocable = (Invocable) engine;
192
Object result = invocable.invokeFunction("multiply", 5, 3); // 15
193
194
// Method invocation on objects
195
engine.eval("""
196
class Calculator {
197
def add(a, b) { return a + b }
198
def subtract(a, b) { return a - b }
199
}
200
calc = new Calculator()
201
""");
202
Object calculator = engine.get("calc");
203
Object sum = invocable.invokeMethod(calculator, "add", 10, 5); // 15
204
205
// Interface proxy
206
engine.eval("""
207
def hello(name) { return "Hello, ${name}!" }
208
def goodbye(name) { return "Goodbye, ${name}!" }
209
""");
210
211
interface Greeter {
212
String hello(String name);
213
String goodbye(String name);
214
}
215
216
Greeter greeter = invocable.getInterface(Greeter.class);
217
System.out.println(greeter.hello("Alice")); // "Hello, Alice!"
218
219
// Access to script class compilation
220
GroovyScriptEngineImpl groovyEngine = (GroovyScriptEngineImpl) engine;
221
Class<?> scriptClass = groovyEngine.getScriptClass("def hello() { 'Hello World' }");
222
System.out.println(scriptClass.getName()); // Generated class name
223
224
// Script class compilation with context
225
ScriptContext context = engine.getContext();
226
context.setAttribute(ScriptEngine.FILENAME, "MyScript.groovy", ScriptContext.ENGINE_SCOPE);
227
Class<?> namedClass = groovyEngine.getScriptClass("def greet() { 'Greetings!' }", context);
228
```
229
230
### Compiled Scripts
231
232
Pre-compiled Groovy scripts for efficient repeated execution.
233
234
```java { .api }
235
public class GroovyCompiledScript extends CompiledScript {
236
public GroovyCompiledScript(GroovyScriptEngineImpl engine, Class<?> clazz);
237
public Object eval(ScriptContext context) throws ScriptException;
238
public ScriptEngine getEngine();
239
}
240
```
241
242
**Usage Example:**
243
244
```java
245
ScriptEngine engine = new ScriptEngineManager().getEngineByName("groovy");
246
Compilable compilable = (Compilable) engine;
247
248
// Compile script once
249
CompiledScript compiled = compilable.compile("""
250
def processData(data) {
251
return data.collect { it * 2 }.findAll { it > 10 }
252
}
253
processData(input)
254
""");
255
256
// Execute multiple times with different inputs
257
engine.put("input", Arrays.asList(1, 2, 5, 8, 10));
258
Object result1 = compiled.eval(); // [16, 20]
259
260
engine.put("input", Arrays.asList(3, 6, 9, 12));
261
Object result2 = compiled.eval(); // [12, 18, 24]
262
```
263
264
### Script Extensions
265
266
Extension methods providing enhanced integration between ScriptEngine and Groovy Binding.
267
268
```java { .api }
269
public class ScriptExtensions {
270
public static Object eval(ScriptEngine self, String script, Binding binding)
271
throws ScriptException;
272
public static Object eval(ScriptEngine self, Reader reader, Binding binding)
273
throws ScriptException;
274
}
275
```
276
277
**Usage Example:**
278
279
```java
280
import static org.codehaus.groovy.jsr223.ScriptExtensions.eval;
281
282
ScriptEngine engine = new ScriptEngineManager().getEngineByName("groovy");
283
Binding binding = new Binding();
284
285
// Set variables in Groovy binding
286
binding.setVariable("name", "Alice");
287
binding.setVariable("age", 25);
288
289
// Execute script with binding integration
290
Object result = eval(engine, "\"${name} is ${age} years old\"", binding);
291
System.out.println(result); // "Alice is 25 years old"
292
293
// Variables modified in script are reflected back in binding
294
eval(engine, "name = name.toUpperCase(); newVar = 'created'", binding);
295
System.out.println(binding.getVariable("name")); // "ALICE"
296
System.out.println(binding.getVariable("newVar")); // "created"
297
```
298
299
### Static Extensions
300
301
Static extension methods for ScriptEngineManager providing dynamic engine access.
302
303
```java { .api }
304
public class ScriptStaticExtensions {
305
public static ScriptEngine $static_propertyMissing(
306
ScriptEngineManager self, String languageShortName);
307
}
308
```
309
310
**Usage Example:**
311
312
```java
313
// This enables dynamic property access syntax in Groovy code:
314
// manager.groovy instead of manager.getEngineByName("groovy")
315
316
// Standard JSR-223 approach
317
ScriptEngineManager manager = new ScriptEngineManager();
318
ScriptEngine groovyEngine = manager.getEngineByName("groovy");
319
ScriptEngine jsEngine = manager.getEngineByName("javascript");
320
321
// With static extensions (in Groovy code):
322
// ScriptEngine groovyEngine = manager.groovy
323
// ScriptEngine jsEngine = manager.javascript
324
```
325
326
## Error Handling
327
328
The implementation provides comprehensive error handling for various scenarios:
329
330
```java
331
ScriptEngine engine = new ScriptEngineManager().getEngineByName("groovy");
332
333
try {
334
// Script compilation errors
335
engine.eval("def invalid syntax here");
336
} catch (ScriptException e) {
337
System.err.println("Script compilation failed: " + e.getMessage());
338
}
339
340
try {
341
// Function invocation errors
342
Invocable invocable = (Invocable) engine;
343
invocable.invokeFunction("nonExistentFunction");
344
} catch (NoSuchMethodException e) {
345
System.err.println("Function not found: " + e.getMessage());
346
} catch (ScriptException e) {
347
System.err.println("Script execution failed: " + e.getMessage());
348
}
349
350
try {
351
// Invalid parameters
352
GroovyScriptEngineFactory factory = new GroovyScriptEngineFactory();
353
factory.getParameter("INVALID_KEY");
354
} catch (IllegalArgumentException e) {
355
System.err.println("Invalid parameter key: " + e.getMessage());
356
}
357
```
358
359
**Common Exceptions:**
360
361
- `ScriptException`: Script compilation or execution errors
362
- `NoSuchMethodException`: Missing function/method invocations
363
- `IllegalArgumentException`: Invalid parameters or arguments
364
- `NullPointerException`: Null method names in invocations
365
366
## Configuration Options
367
368
### Reference Management
369
370
Control memory management for global closures:
371
372
```java
373
ScriptEngine engine = new ScriptEngineManager().getEngineByName("groovy");
374
375
// Configure closure reference strength
376
engine.getContext().setAttribute(
377
"#jsr223.groovy.engine.keep.globals",
378
"weak", // Options: "hard", "soft", "weak", "phantom"
379
ScriptContext.ENGINE_SCOPE
380
);
381
```
382
383
### Custom ClassLoader
384
385
Use custom ClassLoader for script compilation:
386
387
```java
388
GroovyClassLoader customLoader = new GroovyClassLoader();
389
GroovyScriptEngineImpl engine = new GroovyScriptEngineImpl(customLoader);
390
391
// Or modify existing engine
392
engine.setClassLoader(customLoader);
393
```
394
395
## Thread Safety
396
397
The Groovy JSR-223 implementation provides:
398
399
- **MULTITHREADED** support as indicated by the THREADING parameter
400
- Thread-safe script class caching using concurrent maps
401
- Safe concurrent execution of compiled scripts
402
- Proper synchronization of ScriptContext access
403
404
```java
405
// Multiple threads can safely share a compiled script
406
CompiledScript compiled = ((Compilable) engine).compile("Math.random()");
407
408
// Each thread should use its own ScriptContext for variable isolation
409
ExecutorService executor = Executors.newFixedThreadPool(10);
410
for (int i = 0; i < 100; i++) {
411
executor.submit(() -> {
412
try {
413
ScriptContext context = new SimpleScriptContext();
414
Object result = compiled.eval(context);
415
System.out.println("Random: " + result);
416
} catch (ScriptException e) {
417
e.printStackTrace();
418
}
419
});
420
}
421
```
422
423
## Types
424
425
```java { .api }
426
// Core JSR-223 interfaces implemented
427
interface ScriptEngineFactory {
428
String getEngineName();
429
String getEngineVersion();
430
String getLanguageName();
431
String getLanguageVersion();
432
List<String> getExtensions();
433
List<String> getMimeTypes();
434
List<String> getNames();
435
Object getParameter(String key);
436
ScriptEngine getScriptEngine();
437
String getMethodCallSyntax(String obj, String method, String... args);
438
String getOutputStatement(String toDisplay);
439
String getProgram(String... statements);
440
}
441
442
interface ScriptEngine {
443
Object eval(String script) throws ScriptException;
444
Object eval(Reader reader) throws ScriptException;
445
Object eval(String script, ScriptContext context) throws ScriptException;
446
Object eval(Reader reader, ScriptContext context) throws ScriptException;
447
void put(String key, Object value);
448
Object get(String key);
449
Bindings getBindings(int scope);
450
void setBindings(Bindings bindings, int scope);
451
Bindings createBindings();
452
ScriptContext getContext();
453
void setContext(ScriptContext context);
454
ScriptEngineFactory getFactory();
455
}
456
457
interface Compilable {
458
CompiledScript compile(String script) throws ScriptException;
459
CompiledScript compile(Reader script) throws ScriptException;
460
}
461
462
interface Invocable {
463
Object invokeMethod(Object thiz, String name, Object... args)
464
throws ScriptException, NoSuchMethodException;
465
Object invokeFunction(String name, Object... args)
466
throws ScriptException, NoSuchMethodException;
467
<T> T getInterface(Class<T> clazz);
468
<T> T getInterface(Object thiz, Class<T> clazz);
469
}
470
471
abstract class CompiledScript {
472
public abstract Object eval(ScriptContext context) throws ScriptException;
473
public Object eval() throws ScriptException;
474
public abstract ScriptEngine getEngine();
475
}
476
477
// Groovy-specific types
478
class Binding {
479
public Binding();
480
public Binding(Map variables);
481
public Object getVariable(String name);
482
public void setVariable(String name, Object value);
483
public Map getVariables();
484
}
485
486
class GroovyClassLoader extends ClassLoader {
487
public GroovyClassLoader();
488
public GroovyClassLoader(ClassLoader parent);
489
public Class<?> parseClass(String text, String fileName);
490
}
491
492
// Exception types
493
class CompilationFailedException extends RuntimeException {
494
public CompilationFailedException(String message);
495
public CompilationFailedException(String message, Throwable cause);
496
}
497
```