0
# JSR-223 Scripting
1
2
Jython provides full support for the Java Scripting API (JSR-223), allowing seamless integration with existing Java applications that use the standard scripting framework.
3
4
## Script Engine Factory
5
6
The factory class for creating Jython script engines.
7
8
```java { .api }
9
public class PyScriptEngineFactory implements ScriptEngineFactory {
10
// Engine metadata
11
public String getEngineName(); // Returns "jython"
12
public String getEngineVersion(); // Returns Jython version
13
public String getLanguageName(); // Returns "python"
14
public String getLanguageVersion(); // Returns Python version "2.7"
15
public List<String> getExtensions(); // Returns ["py"]
16
public List<String> getMimeTypes(); // Returns Python MIME types
17
public List<String> getNames(); // Returns ["python", "jython"]
18
19
// Script engine creation
20
public ScriptEngine getScriptEngine();
21
22
// Utility methods
23
public String getMethodCallSyntax(String obj, String m, String... args);
24
public String getOutputStatement(String toDisplay);
25
public String getProgram(String... statements);
26
public Object getParameter(String key);
27
}
28
```
29
30
## Script Engine
31
32
The main JSR-223 script engine implementation for Jython.
33
34
```java { .api }
35
public class PyScriptEngine extends AbstractScriptEngine
36
implements Compilable, Invocable, AutoCloseable {
37
38
// Script execution
39
public Object eval(String script) throws ScriptException;
40
public Object eval(String script, ScriptContext context) throws ScriptException;
41
public Object eval(Reader reader) throws ScriptException;
42
public Object eval(Reader reader, ScriptContext context) throws ScriptException;
43
44
// Compilation support (Compilable interface)
45
public CompiledScript compile(String script) throws ScriptException;
46
public CompiledScript compile(Reader reader) throws ScriptException;
47
48
// Function invocation (Invocable interface)
49
public Object invokeFunction(String name, Object... args)
50
throws ScriptException, NoSuchMethodException;
51
public Object invokeMethod(Object thiz, String name, Object... args)
52
throws ScriptException, NoSuchMethodException;
53
public <T> T getInterface(Class<T> clasz);
54
public <T> T getInterface(Object thiz, Class<T> clasz);
55
56
// Engine management
57
public Bindings createBindings();
58
public ScriptEngineFactory getFactory();
59
public void close();
60
}
61
```
62
63
## Script Engine Scope
64
65
Provides variable scoping and bindings for the JSR-223 engine.
66
67
```java { .api }
68
public final class PyScriptEngineScope extends PyObject implements Bindings {
69
// Bindings interface implementation
70
public Object put(String key, Object value);
71
public Object get(Object key);
72
public Object remove(Object key);
73
public boolean containsKey(Object key);
74
public boolean containsValue(Object value);
75
public Set<String> keySet();
76
public Collection<Object> values();
77
public Set<Entry<String, Object>> entrySet();
78
public int size();
79
public boolean isEmpty();
80
public void clear();
81
public void putAll(Map<? extends String, ? extends Object> toMerge);
82
}
83
```
84
85
## Basic Usage Examples
86
87
### Simple Script Execution
88
89
```java
90
import javax.script.*;
91
92
public class JSR223Example {
93
public static void main(String[] args) throws ScriptException {
94
// Get script engine manager
95
ScriptEngineManager manager = new ScriptEngineManager();
96
97
// Get Jython engine by name
98
ScriptEngine engine = manager.getEngineByName("python");
99
100
// Execute simple script
101
engine.eval("print('Hello from Jython!')");
102
103
// Set variables
104
engine.put("name", "World");
105
engine.put("number", 42);
106
107
// Use variables in script
108
Object result = engine.eval("greeting = f'Hello, {name}! The answer is {number}'");
109
Object greeting = engine.get("greeting");
110
111
System.out.println("Result: " + greeting);
112
}
113
}
114
```
115
116
### Using Script Context
117
118
```java
119
ScriptEngineManager manager = new ScriptEngineManager();
120
ScriptEngine engine = manager.getEngineByName("python");
121
122
// Create custom context
123
ScriptContext context = new SimpleScriptContext();
124
Bindings bindings = engine.createBindings();
125
bindings.put("x", 10);
126
bindings.put("y", 20);
127
context.setBindings(bindings, ScriptContext.ENGINE_SCOPE);
128
129
// Execute with custom context
130
Object result = engine.eval("result = x + y", context);
131
Object sum = context.getAttribute("result");
132
133
System.out.println("Sum: " + sum); // Sum: 30
134
```
135
136
## Compilation Support
137
138
Pre-compile scripts for better performance when executing multiple times.
139
140
### Compiling Scripts
141
142
```java
143
ScriptEngineManager manager = new ScriptEngineManager();
144
ScriptEngine engine = manager.getEngineByName("python");
145
146
// Check if engine supports compilation
147
if (engine instanceof Compilable) {
148
Compilable compilable = (Compilable) engine;
149
150
// Compile script
151
CompiledScript compiled = compilable.compile("""
152
def calculate(x, y):
153
return x * y + (x - y)
154
155
result = calculate(a, b)
156
""");
157
158
// Execute compiled script multiple times
159
engine.put("a", 5);
160
engine.put("b", 3);
161
compiled.eval();
162
System.out.println("Result 1: " + engine.get("result")); // 17
163
164
engine.put("a", 10);
165
engine.put("b", 4);
166
compiled.eval();
167
System.out.println("Result 2: " + engine.get("result")); // 46
168
}
169
```
170
171
### CompiledScript Usage
172
173
```java
174
CompiledScript compiled = compilable.compile("x ** 2 + y ** 2");
175
176
// Execute with different contexts
177
ScriptContext ctx1 = new SimpleScriptContext();
178
ctx1.setAttribute("x", 3, ScriptContext.ENGINE_SCOPE);
179
ctx1.setAttribute("y", 4, ScriptContext.ENGINE_SCOPE);
180
Object result1 = compiled.eval(ctx1);
181
System.out.println("3² + 4² = " + result1); // 25
182
183
ScriptContext ctx2 = new SimpleScriptContext();
184
ctx2.setAttribute("x", 5, ScriptContext.ENGINE_SCOPE);
185
ctx2.setAttribute("y", 12, ScriptContext.ENGINE_SCOPE);
186
Object result2 = compiled.eval(ctx2);
187
System.out.println("5² + 12² = " + result2); // 169
188
```
189
190
## Function Invocation
191
192
Call Python functions directly from Java using the Invocable interface.
193
194
### Basic Function Invocation
195
196
```java
197
ScriptEngineManager manager = new ScriptEngineManager();
198
ScriptEngine engine = manager.getEngineByName("python");
199
200
// Define Python functions
201
engine.eval("""
202
def add(x, y):
203
return x + y
204
205
def greet(name, title=""):
206
if title:
207
return f"Hello, {title} {name}!"
208
else:
209
return f"Hello, {name}!"
210
211
class Calculator:
212
def __init__(self, factor=1):
213
self.factor = factor
214
215
def multiply(self, x):
216
return x * self.factor
217
218
calc = Calculator(10)
219
""");
220
221
// Invoke functions
222
if (engine instanceof Invocable) {
223
Invocable invocable = (Invocable) engine;
224
225
// Call simple function
226
Object sum = invocable.invokeFunction("add", 5, 3);
227
System.out.println("Sum: " + sum); // 8
228
229
// Call function with keyword-like behavior (positional only in JSR-223)
230
Object greeting1 = invocable.invokeFunction("greet", "Alice");
231
Object greeting2 = invocable.invokeFunction("greet", "Bob", "Dr.");
232
System.out.println(greeting1); // Hello, Alice!
233
System.out.println(greeting2); // Hello, Dr. Bob!
234
235
// Call method on object
236
Object calcInstance = engine.get("calc");
237
Object product = invocable.invokeMethod(calcInstance, "multiply", 7);
238
System.out.println("Product: " + product); // 70
239
}
240
```
241
242
### Interface Implementation
243
244
Create Java interfaces implemented by Python objects.
245
246
```java
247
// Define Java interface
248
interface MathOperations {
249
double calculate(double x, double y);
250
String getOperationName();
251
}
252
253
// Define Python implementation
254
engine.eval("""
255
class Adder:
256
def calculate(self, x, y):
257
return x + y
258
259
def getOperationName(self):
260
return "Addition"
261
262
adder = Adder()
263
""");
264
265
// Get interface implementation
266
MathOperations mathOps = invocable.getInterface(
267
engine.get("adder"),
268
MathOperations.class
269
);
270
271
// Use as Java interface
272
double result = mathOps.calculate(10.5, 5.3);
273
String opName = mathOps.getOperationName();
274
275
System.out.println(opName + ": " + result); // Addition: 15.8
276
```
277
278
## Advanced Usage Patterns
279
280
### Custom Bindings
281
282
```java
283
ScriptEngine engine = manager.getEngineByName("python");
284
285
// Create custom bindings with initial values
286
Bindings customBindings = new SimpleBindings();
287
customBindings.put("PI", Math.PI);
288
customBindings.put("E", Math.E);
289
customBindings.put("logger", LoggerFactory.getLogger("jython"));
290
291
// Set as engine scope
292
engine.setBindings(customBindings, ScriptContext.ENGINE_SCOPE);
293
294
engine.eval("""
295
import math
296
297
# Use Java objects in Python
298
logger.info("Calculating circle area")
299
300
radius = 5
301
area = PI * radius * radius
302
303
logger.info(f"Area of circle with radius {radius} is {area}")
304
""");
305
```
306
307
### Multi-Engine Coordination
308
309
```java
310
ScriptEngineManager manager = new ScriptEngineManager();
311
312
// Create multiple engines
313
ScriptEngine engine1 = manager.getEngineByName("python");
314
ScriptEngine engine2 = manager.getEngineByName("python");
315
316
// Shared data object
317
Map<String, Object> sharedData = new ConcurrentHashMap<>();
318
319
// Setup engines with shared data
320
engine1.put("shared", sharedData);
321
engine2.put("shared", sharedData);
322
323
// Engine 1 sets data
324
engine1.eval("shared['message'] = 'Hello from Engine 1'");
325
326
// Engine 2 reads data
327
engine2.eval("print('Engine 2 received:', shared.get('message'))");
328
```
329
330
### Error Handling
331
332
```java
333
ScriptEngine engine = manager.getEngineByName("python");
334
335
try {
336
engine.eval("undefined_variable + 1");
337
} catch (ScriptException e) {
338
System.out.println("Script error: " + e.getMessage());
339
System.out.println("Line number: " + e.getLineNumber());
340
System.out.println("Column number: " + e.getColumnNumber());
341
System.out.println("File name: " + e.getFileName());
342
343
// Get the underlying Python exception
344
Throwable cause = e.getCause();
345
if (cause instanceof PyException) {
346
PyException pyEx = (PyException) cause;
347
System.out.println("Python exception type: " + pyEx.type);
348
System.out.println("Python exception value: " + pyEx.value);
349
}
350
}
351
```
352
353
## Configuration and Properties
354
355
### Engine Parameters
356
357
```java
358
ScriptEngineFactory factory = new PyScriptEngineFactory();
359
360
// Get engine information
361
System.out.println("Engine Name: " + factory.getEngineName());
362
System.out.println("Engine Version: " + factory.getEngineVersion());
363
System.out.println("Language Name: " + factory.getLanguageName());
364
System.out.println("Language Version: " + factory.getLanguageVersion());
365
System.out.println("Extensions: " + factory.getExtensions());
366
System.out.println("MIME Types: " + factory.getMimeTypes());
367
System.out.println("Names: " + factory.getNames());
368
369
// Check specific parameters
370
Object threadingType = factory.getParameter(ScriptEngine.THREADING);
371
System.out.println("Threading: " + threadingType);
372
```
373
374
### Script Context Management
375
376
```java
377
ScriptEngine engine = manager.getEngineByName("python");
378
379
// Get default context
380
ScriptContext defaultContext = engine.getContext();
381
382
// Create isolated context for sensitive operations
383
ScriptContext isolatedContext = new SimpleScriptContext();
384
Bindings isolatedBindings = engine.createBindings();
385
isolatedBindings.put("secure_data", "sensitive information");
386
isolatedContext.setBindings(isolatedBindings, ScriptContext.ENGINE_SCOPE);
387
388
// Execute in isolated context
389
engine.eval("result = len(secure_data)", isolatedContext);
390
Object result = isolatedContext.getAttribute("result");
391
392
// Original context remains unaffected
393
Object originalData = defaultContext.getAttribute("secure_data");
394
System.out.println("Original context has secure_data: " + (originalData != null));
395
```
396
397
## Performance Optimization
398
399
### Script Compilation Best Practices
400
401
```java
402
// Cache compiled scripts for reuse
403
Map<String, CompiledScript> scriptCache = new ConcurrentHashMap<>();
404
405
public Object executeScript(String script, Map<String, Object> variables)
406
throws ScriptException {
407
408
// Get or compile script
409
CompiledScript compiled = scriptCache.computeIfAbsent(script, s -> {
410
try {
411
return ((Compilable) engine).compile(s);
412
} catch (ScriptException e) {
413
throw new RuntimeException(e);
414
}
415
});
416
417
// Create context with variables
418
ScriptContext context = new SimpleScriptContext();
419
Bindings bindings = engine.createBindings();
420
bindings.putAll(variables);
421
context.setBindings(bindings, ScriptContext.ENGINE_SCOPE);
422
423
// Execute compiled script
424
return compiled.eval(context);
425
}
426
```
427
428
### Resource Management
429
430
```java
431
// Use try-with-resources for automatic cleanup
432
try (AutoCloseable engine = (AutoCloseable) manager.getEngineByName("python")) {
433
engine.eval("print('Script executed')");
434
// Engine automatically closed
435
}
436
437
// Manual resource management
438
ScriptEngine engine = manager.getEngineByName("python");
439
try {
440
engine.eval("print('Script executed')");
441
} finally {
442
if (engine instanceof AutoCloseable) {
443
((AutoCloseable) engine).close();
444
}
445
}
446
```
447
448
## Integration with Java Frameworks
449
450
### Spring Framework Integration
451
452
```java
453
@Component
454
public class JythonScriptService {
455
private final ScriptEngine engine;
456
457
public JythonScriptService() {
458
ScriptEngineManager manager = new ScriptEngineManager();
459
this.engine = manager.getEngineByName("python");
460
}
461
462
@PostConstruct
463
public void initialize() throws ScriptException {
464
// Load common Python modules
465
engine.eval("""
466
import json
467
import datetime
468
import re
469
470
def format_data(data):
471
return json.dumps(data, indent=2)
472
""");
473
}
474
475
public String processData(Map<String, Object> data) throws ScriptException {
476
engine.put("input_data", data);
477
return (String) engine.eval("format_data(input_data)");
478
}
479
}
480
```
481
482
### Servlet Integration
483
484
```java
485
@WebServlet("/python-script")
486
public class PythonScriptServlet extends HttpServlet {
487
private ScriptEngine engine;
488
489
@Override
490
public void init() throws ServletException {
491
ScriptEngineManager manager = new ScriptEngineManager();
492
engine = manager.getEngineByName("python");
493
}
494
495
@Override
496
protected void doPost(HttpServletRequest request, HttpServletResponse response)
497
throws ServletException, IOException {
498
499
String script = request.getParameter("script");
500
501
try {
502
engine.put("request_params", request.getParameterMap());
503
Object result = engine.eval(script);
504
505
response.setContentType("application/json");
506
response.getWriter().write(result.toString());
507
} catch (ScriptException e) {
508
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
509
response.getWriter().write("Script error: " + e.getMessage());
510
}
511
}
512
}
513
```