In depth understanding of Java scripting API programming

Time:2020-2-23

For whom is the Java scripting API prepared?

Some useful features of scripting languages are:

  • Convenient: most scripting languages are dynamically typed. You can usually create new variables without declaring variable types, and you can reuse variables to store different types of objects. In addition, scripting languages often perform many types of transformations automatically, such as converting the number 10 to “10” if necessary.
  • Develop a rapid prototype: you can avoid editing the compilation run cycle and only use “Edit run”!
  • Application extension / Customization: you can “materialize” some applications, such as some configuration scripts, business logic / rules and mathematical expressions in financial applications.
  • Add a command line mode for the application for debugging, runtime configuration / deployment time. Most applications now have a web-based GUI configuration tool. But system administrators / Deployers often like command-line tools. A “standard” scripting language can be used for this purpose, rather than inventing a special scripting language.

The java script API is a framework independent scripting language that uses a scripting engine from Java code. Through the Java scripting API, you can use the Java language to write custom / extensible applications and leave the custom scripting language selection to the end user. Java application developers do not need to choose an extension language during the development process. If you use the jsr-223 API to write applications, your users can use any jsr-223 compatible scripting language.

Script package

The JavaScript function is in the javax. Script package. This is a relatively small, simple API. The starting point of the script is the scriptenginemanager class. A scriptenginemanager object can discover the script engine through the service discovery mechanism of the jar file. It can also instantiate a script engine to interpret scripts written in a specific scripting language. The simplest way to use the scripting interface is as follows:

1. Create a scriptenginemanager object

2. Get scriptengine object from scriptenginemanager

3. Use the eval method of scriptengine to execute the script

Now, it’s time to look at some sample code. Knowing some JavaScript helps to read these examples, but it’s not mandatory.

Example

“Hello,World”

From the scriptenginemanager instance, we get a JavaScript engine instance through the getenginebyname method. Execute the given JavaScript code through the eval method of the script engine. For simplicity, we do not handle exceptions in this and subsequent examples. The javax.script API has check and run-time exceptions, which you must handle properly.


import javax.script.*;
public class EvalScript {
public static void main(String[] args) throws Exception {
// create a script engine manager
ScriptEngineManager factory = new ScriptEngineManager();
// create a JavaScript engine
ScriptEngine engine = factory.getEngineByName("JavaScript");
// evaluate JavaScript code from String
engine.eval("print('Hello, World')");
}
}

Execute a script file

In this example, we call the eval method to receive Java. Io. Reader as the input source. The script read in is executed. In this way, scripts can be executed as files, and URLs and resources can be read by related input stream objects.


import javax.script.*;
public class EvalFile {
public static void main(String[] args) throws Exception {
// create a script engine manager
ScriptEngineManager factory = new ScriptEngineManager();
// create JavaScript engine
ScriptEngine engine = factory.getEngineByName("JavaScript");
// evaluate JavaScript code from given file - specified by first argument
engine.eval(new java.io.FileReader(args[0]));
}
}

Suppose we have a file called “test. JS”. The contents are as follows:


println("This is hello from test.js");

We can run the script as follows


java EvalFile test.js

Script variable

When your Java application embeds script engines and scripts, you may want to expose your application objects as global variables in the script. This example demonstrates how to expose your application object as a global variable in a script. We create a java.io.file object as a global variable in the application, with the name of file. The script can access variables, for example, it can call its public methods. Note that the syntax for accessing Java objects, domains, and methods depends on the scripting language. JavaScript supports the most “natural” Java like syntax.


public class ScriptVars { 
public static void main(String[] args) throws Exception {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("JavaScript");

File f = new File("test.txt");
// expose File object as variable to script
engine.put("file", f);

// evaluate a script string. The script accesses "file" 
// variable and calls method on it
engine.eval("print(file.getAbsolutePath())");
}
}

Call script functions and methods

Sometimes, you may need to call a specific script function multiple times, for example, your application menu function may be implemented by script. In the action event handler in the menu, you may need to call a specific script function. The following example demonstrates calling a specific script in Java code.


import javax.script.*;
public class InvokeScriptFunction {
public static void main(String[] args) throws Exception {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("JavaScript");
// JavaScript code in a String
String script = "function hello(name) { print('Hello, ' + name); }";
// evaluate script
engine.eval(script);
// javax.script.Invocable is an optional interface.
// Check whether your script engine implements or not!
// Note that the JavaScript engine implements Invocable interface.
Invocable inv = (Invocable) engine;
// invoke the global function named "hello"
inv.invokeFunction("hello", "Scripting!!" );
}
}

If your scripting language is object-based (such as JavaScript) or object-oriented, you can call script methods on script objects.


import javax.script.*;
public class InvokeScriptMethod {
public static void main(String[] args) throws Exception {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("JavaScript");
// JavaScript code in a String. This code defines a script object 'obj'
// with one method called 'hello'. 
String script = "var obj = new Object(); obj.hello = function(name) { print('Hello, ' + name); }";
// evaluate script
engine.eval(script);
// javax.script.Invocable is an optional interface.
// Check whether your script engine implements or not!
// Note that the JavaScript engine implements Invocable interface.
Invocable inv = (Invocable) engine;
// get script object on which we want to call the method
Object obj = engine.get("obj");
// invoke the method named "hello" on the script object "obj"
inv.invokeMethod(obj, "hello", "Script Method !!" );
}
}

Implementing java interface through script

Sometimes it is easy to implement java interface by script function or method instead of calling in Java. At the same time, through the interface, we can avoid using the javax. Script API interface in many places. We can get an interface implementer object and pass it to different Java APIs. The following example demonstrates the implementation of the java.lang.runnable interface through scripts.


import javax.script.*;
public class RunnableImpl {
public static void main(String[] args) throws Exception {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("JavaScript");
// JavaScript code in a String
String script = "function run() { println('run called'); }";
// evaluate script
engine.eval(script);
Invocable inv = (Invocable) engine;
// get Runnable interface object from engine. This interface methods
// are implemented by script functions with the matching name.
Runnable r = inv.getInterface(Runnable.class);
// start a new thread that runs the script implemented
// runnable interface
Thread th = new Thread(r);
th.start();
}
}

If your scripting language is object-based or object-oriented, you can implement the java interface through scripting methods of scripting objects. This avoids having to call interface methods of script global functions. Script objects can store interface implementation state.


import javax.script.*;
public class RunnableImplObject {
public static void main(String[] args) throws Exception {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("JavaScript");
// JavaScript code in a String
String script = "var obj = new Object(); obj.run = function() { println('run method called'); }";
// evaluate script
engine.eval(script);
// get script object on which we want to implement the interface with
Object obj = engine.get("obj");
Invocable inv = (Invocable) engine;
// get Runnable interface object from engine. This interface methods
// are implemented by script methods of object 'obj'
Runnable r = inv.getInterface(obj, Runnable.class);
// start a new thread that runs the script implemented
// runnable interface
Thread th = new Thread(r);
th.start();
}
}

Multi scope of script

In the script variables example, we see how to expose the application object as a global variable of the script. It can be exposed to multiple global scopes. The single scope is in the instance of javax.script.bindings. This excuse derives from Java. Util. Map < string, Object >. The collection of scope key value pairs, where the key is a non empty, non empty string. Multiple scopes are supported by the javax.script.scriptcontext interface. Supports binding of one or more script contexts to related domains. By default, each script engine has a default script context. The default script context has at least one domain called “engine? Scope”. Script context support for different domains can be obtained through the getscopes method.


import javax.script.*;
public class MultiScopes {
public static void main(String[] args) throws Exception {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("JavaScript");
engine.put("x", "hello");
// print global variable "x"
engine.eval("println(x);");
// the above line prints "hello"
// Now, pass a different script context
ScriptContext newContext = new SimpleScriptContext();
Bindings engineScope = newContext.getBindings(ScriptContext.ENGINE_SCOPE);
// add new variable "x" to the new engineScope 
engineScope.put("x", "world");
// execute the same script - but this time pass a different script context
engine.eval("println(x);", newContext);
// the above line prints "world"
}
}

JavaScript script engine

Sun’s JDK 6 includes a Mozilla rhino JavaScript script engine. The engine is based on version 1.6r2 of Mozilla rhino. Most rhino implementations are included. A few components were excluded for size and security reasons:

1. JavaScript bytecode compilation (also known as “optimizer”). This feature relies on a class build library. Removing this feature means that JavaScript is interpreted execution and does not affect script execution because the optimizer is transparent.

2. Rhino’s Java adapter has also been removed. Javaadapter is a JavaScript extensible Java class and JavaScript can implement java interface functions. This feature also requires a class generation library. We replace rhino’s javaadapter with sun’s. In sun’s implementation, only the JavaScript object can realize the Java single interface function. For example, the following code will execute correctly.


var v = new java.lang.Runnable() {
run: function() { print('hello'); }
}
v.run();

In most cases, the Java adapter uses anonymous class syntax to implement a single interface. It is not common to use javaadapter to extend Java classes or implement multiple interfaces.

3. E4X (ECMAScript for XML – ECMA standard 357) is removed. Using XML JavaScript code will cause a syntax error. Please note that E4X supports ECMAScript standard is optional – the implementation of omitting E4X is supported and compatible with ECMAScript.

4. Rhino’s command-line tools (rhino shell, debugger, etc.) are not included. But you can use jrunscript instead.

Communication between JavaScript and Java

In most cases, accessing Java classes, objects, and methods is simple. Accessing properties and methods from JavaScript is the same as in Java. Here, we highlight the important aspects of JavaScript Java access. For more details, please read http://www.mozilla.org/rhino/scriptjava.html. Here are some snippets of JavaScript accessing Java. This section requires some JavaScript knowledge. If you plan to use the non JavaScript scripting language in jsr-223, you can skip this section.

Introducing Java packages, classes

The built-in functions importpackage and importclass can be used to introduce Java packages and classes.


// Import Java packages and classes 
// like import package.*; in Java
importPackage(java.awt);
// like import java.awt.Frame in Java
importClass(java.awt.Frame);
// Create Java Objects by "new ClassName"
var frame = new java.awt.Frame("hello");
// Call Java public methods from script
frame.setVisible(true);
// Access "JavaBean" properties like "fields"
print(frame.title);

The global variable packages can also be used to access Java packages.

For example: packages.java.util.vector, packages.javax.swing.jframe. Please note that “Java” is a quick reference to “packages. Java”. There are also some equivalent quick reference prefixes: javax, org, edu, com, net, so almost all classes on the JDK platform can be accessed without using the “packages” prefix.

Please note that java.lang is not introduced by default (different from Java), because it conflicts with JavaScript’s built-in object, Boolean, math, etc. The importpackage and importclass functions “pollute” global variables in JavaScript. To avoid this, you can use the Java importer.


// create JavaImporter with specific packages and classes to import
var SwingGui = new JavaImporter(javax.swing,
javax.swing.event,
javax.swing.border,
java.awt.event);
with (SwingGui) {
// within this 'with' statement, we can access Swing and AWT
// classes by unqualified (simple) names.
var mybutton = new JButton("test");
var myframe = new JFrame("test");
}

C creating and using java arrays

In JavaScript, creating an object is the same as in Java, while creating a Java array requires explicit use of java reflection. But once created, accessing the elements or getting the size is the same as in Java. In addition, you can also use script arrays as Java arrays in Java methods (because they can be converted automatically). So in most cases we don’t need to explicitly create Java arrays.


// create Java String array of 5 elements
var a = java.lang.reflect.Array.newInstance(java.lang.String, 5);
// Accessing elements and length access is by usual Java syntax
a[0] = "scripting is great!";
print(a.length);

Implement java interface

In JavaScript, you can use Java anonymous class syntax to implement the interface in Java:


var r = new java.lang.Runnable() {
run: function() {
print("running...\n");
}
};
// "r" can be passed to Java methods that expect java.lang.Runnable
var th = new java.lang.Thread(r);
th.start();

When there is only one method in the interface that needs to be implemented, you can pass in the function of the script by yourself (because it can be converted automatically).


function func() {
print("I am func!");
}
// pass script function for java.lang.Runnable argument
var th = new java.lang.Thread(func);
th.start();

heavy load

Java methods are overloaded with parameter types. In Java, overloading occurs during the compilation phase (execution of javac). When the Java method is invoked in the script, the translator or compiler of the script needs to select the appropriate method. For the JavaScript engine, you don’t need to do anything special — the correct Java method overload variant is chosen based on the parameter type. But sometimes, you may want (or have) to explicitly select a specific overload variant.


var out = java.lang.System.out;
// select a particular println function 
out["println(java.lang.Object)"]("hello");

Custom script engine

We will not cover the implementation details of jsr-223 compatible script engine. At least, you need to implement the javax.script.scriptengine and javax.script.scriptenginefactory interfaces.

The abstract class javax.script.abstractscriptengine provides some methods defined in the scriptengine interface.

Before you start implementing the jsr-223 engine, you may need to download the http://scripting.dev.java.net project. This project maintains the jsr-223 implementation of some popular open source scripting languages.

The above is the whole content of this article. I hope it will help you in your study, and I hope you can support developepaer more.