Mustang Script Engine
JSR 233 has designed a set of scripting language APIs for Java. This API provides interfaces for calling various script language engines in Java programs. Any scripting engine that implements this interface can be called in a Java program. The Mustang release includes a Javascript script engine based on Mozilla rhino.
Mozilla rhino
Rhino is a pure Java open source JavaScript implementation. His name comes from the cover of o'reilly's book about Javascript:
The rhino project can be traced back to 1997. At that time, Netscape planned to develop a pure Java-implemented navigator. Therefore, a Java-implemented JavaScript-javagator was required. It is also the predecessor of rhino. At first, rhino compiled Javascript into a Java binary code for execution, so that it would have the best performance. Later, due to the garbage collection problem in the method of compilation and execution, and the overhead of the compilation and loading process is too large to meet the needs of some projects, rhino provides a way to explain the execution. With the open source code of rhino, more and more users are using their own products.
At the same time, more and more developers have participated in rhino development and made great contributions. Today, rhino1.6r2 will be included in Java se6. more java developers will benefit from this.
Rhino provides the following functions:
Full support for JavaScript 1.5
Use JavaScript directly in Java
A JavaScript shell is used to run JavaScript scripts.
A JavaScript compiler used to compile Javascript into a Java binary file
Supported scripting languages
In dev.java.net, you can find the Implementation Project of the official script engine. This project is based on BSD license, indicating that these script engines are very free to use. Currently, this project supports more than 20 scripting languages, including groovy, JavaScript, Python, Ruby, and PHP. The support list will be expanded.
The factory mode is used for script engine search in Mustang. First, you need to instantiate a factory-scriptenginemanager.
// Create a script engine Manager
Scriptenginemanager factory = new scriptenginemanager ();
Scriptenginemanager looks for available script Engines Based on the META-INF of the JAR file in the classpath of thread context classloader. It provides three methods to retrieve the script engine:
// Create engine by name
Scriptengine engine = factory. getenginebyname ("JavaScript ");
// Create engine by name
Scriptengine engine = factory. getenginebyextension ("JS ");
// Create engine by name
Scriptengine engine = factory. getenginebymimetype ("application/JavaScript ");
The following code prints all the script engines supported by the current JDK.
Scriptenginemanager factory = new scriptenginemanager ();
For (scriptenginefactory available: factory. getenginefactories ()){
System. Out. println (available. getenginename ());
}
The following section uses JavaScript as an example.
Explain the script in Java
With the Script Engine instance, you can easily execute the script language. By convention, we start with a simple hello World:
Public class runjavascript {
Public static void main (string [] ARGs ){
Scriptenginemanager factory = new scriptenginemanager ();
Scriptengine engine = factory. getenginebyname ("JavaScript ");
Engine. eval ("Print ('Hello World ')");
}
}
This Java code will execute JavaScript and print out Hello world. What if a syntax error occurs in JavaScript?
Engine. eval ("If (true) {println ('hello ')");
If "}" is not intentionally added, Java will throw a javax. Script. scriptexception and print the error message accurately: exception in thread "Main" javax. Script. scriptexception:
Sun.org. Mozilla. Javascript. Internal. evaluatorexception:
MISSING} in compound statement (<Unknown Source> #1) in <Unknown Source>
At line number 1
At...
What should we do if we want to explain more complex scripting languages or want to change the script at runtime? The script engine supports an overloaded eval method, which can read the required script from a reader:
Scriptenginemanager factory = new scriptenginemanager ();
Scriptengine engine = factory. getenginebyname ("JavaScript ");
Engine. eval (new reader ("helloworld. js "));
In this way, the Java code will dynamically search for helloworld. js and execute it at runtime. You can change this script file to change the Java code behavior at any time. To do a simple experiment, the Java code is as follows: public class runjavascript {
Public static void main (string [] ARGs) throws filenotfoundexception,
Scriptexception, interruptedexception {
Scriptenginemanager factory = new scriptenginemanager ();
Scriptengine engine = factory. getenginebyname ("JavaScript ");
While (true ){
Engine. eval (New filereader ("helloworld. js "));
Thread. Sleep (1000 );
}
}
}
Helloworld. js content simply prints a hello World: Print ('Hello World ');
Running runjavascript will print a hello World every second. At this time, modify helloworld. js content to print ('Hello Tony ');
The printed content will be changed to hello Tony, so the Java program will dynamically read the script file and explain the execution. For this simple hello World script, Io operations will lose about 20% of the performance (on my Think Pad) than direct execution of the script ), but the flexibility he brings-the ability to dynamically change code at runtime is very exciting on some occasions.
Communication between the scripting language and Java
The PUT Method of scriptengine is used to map a Java object to a variable in the script language. Now there is a Java class with only one method. The function is to print a string Hello World:
Package Tony;
Public class helloworld {
String S = "Hello World ";
Public void sayhello (){
System. Out. println (s );
}
}
So how to use this class in the script language? The put method can be:
Import javax. Script. scriptengine;
Import javax. Script. scriptenginemanager;
Import javax. Script. scriptexception;
Public class testput {
Public static void main (string [] ARGs) throws scriptexception {
Scriptenginemanager factory = new scriptenginemanager ();
Scriptengine engine = factory. getenginebyname ("JavaScript ");
Helloworld Hello = new helloworld ();
Engine. Put ("script_hello", hello );
Engine. eval ("script_hello.sayhello ()");
}
}
First, we instantiate a helloworld, and then map this instance to the script_hello variable in the script language using the put method. Then we can call the method of this instance in the eval () function in the same way as in the Java program. Similarly, if we have a script function that performs certain calculations and returns values, we can also conveniently call this script in Java code:
Package Tony;
Import javax. Script. invocable;
Import javax. Script. scriptengine;
Import javax. Script. scriptenginemanager;
Import javax. Script. scriptexception;
Public class testinv {
Public static void main (string [] ARGs) throws scriptexception,
Nosuchmethodexception {
Scriptenginemanager factory = new scriptenginemanager ();
Scriptengine engine = factory. getenginebyname ("JavaScript ");
String script = "function say (first, second) {print (first +'' + second );}";
Engine. eval (SCRIPT );
Invocable inv = (invocable) engine;
Inv. invokefunction ("say", "hello", "Tony ");
}
}
In this example, we first define a script function "say", which accepts two string parameters to splice them and return them. The first time we met one of the two optional interfaces of scriptengine -- invocable, invocable indicates that the current engine can be called as a function. Here we forcibly convert the engine to the invocable type, and use the invokefunction method to pass the parameters to the script engine. Invokefunction defines variable parameters. Multiple parameters can be passed at a time and the return value of the script language is used as the return value. The following example uses JavaScript to implement a simple Max function. It accepts two parameters and returns a large one. In order to facilitate the correctness of the asserted results, the JUnit
For more information about JUnit, see www.junit.org.
Package Tony;
Import javax. Script. invocable;
Import javax. Script. scriptengine;
Import javax. Script. scriptenginemanager;
Import javax. Script. scriptexception;
Import JUnit. Framework. testcase;
Public class testscripting extends testcase {
Public void testinv () throws scriptexception, nosuchmethodexception {
Scriptenginemanager factory = new scriptenginemanager ();
Scriptengine engine = factory. getenginebyname ("JavaScript ");
String script = "function max (first, second )"
+ "{Return (first> second )? First: Second ;}";
Engine. eval (SCRIPT );
Invocable inv = (invocable) engine;
Object OBJ = Inv. invokefunction ("Max", "1", "0 ");
Assertequals ("1", obj. tostring ());
}
}
The invocable interface also has a method for obtaining a Java interface instance from an engine. Its definition is as follows:
<T> T getinterface (class <t> clasz)
It accepts a Java interface type as a parameter and returns an instance of this interface. That is to say, you can use the scripting language to write all the implementations of a Java interface. The following is an example. First, define a Java interface, which has two simple functions: calculate the maximum value and the minimum value respectively:
Package Tony;
Public interface maxmin {
Public int max (int A, int B );
Public int min (int A, int B );
}
This testcase implements the maxmin interface using JavaScript, then returns an instance using the getinterface method and verifies the result.
Public void testinvinterface () throws scriptexception,
Nosuchmethodexception {
Scriptenginemanager factory = new scriptenginemanager ();
Scriptengine engine = factory. getenginebyname ("JavaScript ");
String script = "function max (first, second )"
+ "{Return (first> second )? First: Second ;}";
Script + = "function min (first, second) {return (first <second )? First: Second ;}";
Engine. eval (SCRIPT );
Invocable inv = (invocable) engine;
Maxmin = Inv. getinterface (maxmin. Class );
Assertequals (1, maxmin. Max (1, 0 ));
Assertequals (0, maxmin. Min (1, 0 ));
}
Script compilation and execution
So far, all our scripts have been interpreted and executed. In comparison, compilation and execution can achieve better performance. The following describes compilable, another optional interface of scriptengine. The script engine that implements this interface supports script compilation and execution. The following example shows a script to determine whether a given string is an email address or an IP Address:
Public void testcomplie () throws scriptexception {
Scriptenginemanager manager = new scriptenginemanager ();
Scriptengine engine = manager. getenginebyname ("JavaScript ");
String script = "Var email =/^ [a-zA-Z0-9 _-] + @ [a-zA-Z0-9 _-]"
+ "+ (\. [A-zA-Z0-9 _-] +) + $ /;";
Script + = "Var IP =/^ (\ D {1, 2} | 1 \ D \ d | 2 [0-4] \ d | 25 [0-5 ])"
+ "(\\. (\ D {1, 2} | 1 \ D \ d | 2 [0-4] \ d | 25 [0-5]) {3} $ /; ";
Script + = "If (email. Test (STR) {println ('It is an email ')}"
+ "Else if (IP. Test (STR) {println ('It is an IP address ')}"
+ "Else {println ('I don \'t know ')}";
Engine. Put ("str", "email@address.tony ");
Compilable = (compilable) engine;
Compiledscript compiled = compilable. Compile (SCRIPT );
Compiled. eval ();
}
The script compilation process is as follows: first, the engine is converted to the compilable interface, and then the compilable interface's compile method is called to obtain a compiledscript instance, which represents a compiled script, in this way, the eval method of compiledscript is used to call the compiled script. On my Think Pad, the compiled call of this Code is about 3-4 times faster than calling engine. Eval directly. As the complexity of the script increases, the performance increases significantly.
Script context and binding
What really connects the scripting language with Java is not scriptengine, but scriptcontext, which serves as a bridge between Java and scriptengine.
A scriptengine has a corresponding scriptcontext, which maintains a map. Each element in this map is a ing between the scripting language object and the Java object. At the same time, this map is also called bindings in our API. A bindings is a map -- Map <string, Object> that specifies that the key must be of the string type. Therefore, a scriptcontext also has a corresponding bindings, which can be obtained and changed through the getbindings and setbindings methods.
A bindings includes all the script variables in its scriptcontext. How can we get the value of the script variable? Of course, get from bindings is a method, and scriptcontext also provides the getattribute method, which is obviously very effective when you only want to get a specific script variable value. Setattribute and removeattribute can add, modify, or delete a specific variable.
All variables stored in scriptcontext also have their own scopes. They can be engine_scope or global_scope. The former indicates the unique variables of scriptengine, and the latter is the variables shared by all scriptengine. For example, we run the engine. after the put (Key, value) method, a variable of engine_scope is added. to define a global_scope variable, you can use setattribute (Key, value, scriptcontext. global_scope)
.
In addition, scriptcontext provides the redirection of standard input and output, which can be used to specify the input and output of the script language.
Back to Top
Use Java advanced features in Javascript
This part is different from the preceding content and will introduce the unique features of the JavaScript engine-Rhino.
Use Java objects
The previous section has introduced how to use an instantiated Java object in Javascript. How to instantiate a Java object in JavaScript? In Java, all classes are stored hierarchically according to the package name. Without this structure in Javascript, rhino uses a clever method to reference all Java objects. Rhino defines a global variable, packages, and all its elements are global variables. This global variable maintains the Java class hierarchy. For example, packages. java. Io. file references
The file object in the Java Io package. In this way, we can easily use Java objects in Javascript. Both new and packages can be omitted:
// The same as: var frame = new packages. java. Io. File ("FILENAME ");
VaR frame = java. Io. File ("FILENAME ");
We can also reference this object like in Java code:
Importclass (Java. Io. File );
Var file = file ("FILENAME ");
If you want to reference all classes in the entire package, you can use importpackage:
Importpackage (Java. Io );
If you only need to reference certain packages in a specific code segment, you can use javaimporter with the with Keyword of JavaScript, such:
VaR myimport = javaimporter (Java. Io. File );
With (myimport ){
VaR myfile = file ("FILENAME ");
}
User-Defined packages can also be referenced, but packages references cannot be omitted at this time:
Importpackage (packages. Tony );
VaR Hello = helloworld ();
Hello. sayhello ();
Note that only public members and methods are visible in Javascript. For example, the reference to hello. s will get undefined. The following describes some common features:
Use a Java Array
It needs to be constructed using reflection:
VaR A = java. Lang. Reflect. array. newinstance (Java. Lang. String, 5 );
In most cases, JavaScript Arrays can be used. When a JavaScript array is passed as a parameter to a Java method, rhino performs automatic conversion and converts it to a Java array.
Implement a Java Interface
In addition to the getinterface method of the invocable interface mentioned above, we can also use the following method in the script:
// Define a javascript object which has corresponding method
OBJ = {max: function (a, B) {return (A> B )? A: B ;}};
// Pass this object to an interface
Maximpl = com. Tony. maxmin (OBJ );
// Invocation
Print (maximpl. Max (1, 2 ));
If the interface has only one method to implement, you can pass a function as a parameter in javascript:
Function func (){
Println ("Hello World ");
}
T = java. Lang. Thread (func );
T. Start ();
Support for JavaBean
Rhino will automatically match the get and is methods of JavaBean, for example, calling hello. string. If the variable "string" does not exist, rhino will automatically match the isstring method of the instance and then match the getstring method. If neither method exists, undefined will be returned.
Back to Top
Command line tool jrunscript
The Mustang release also contains a script-language command line tool that can explain all the script languages supported by the current JDK. It is also a good tool for learning scripting languages. You can.
Article Source: DIY tribe (http://www.diybl.com/course/1_web/javascript/Javascriptxl/2008921/144278.html)