Many Java developers now like to use scripting languages in the Java platform, but using dynamic languages compiled into Java bytecode is sometimes impractical. In some cases, writing a script part of a Java application directly or invoking a particular Java object in a script is a faster and more efficient method.
This is the reason why Javax.script is produced. The Java Scripting API, introduced from Java 6, fills the gap between a handy little scripting language and a robust Java ecosystem. By using the Java Scripting API, you can quickly integrate almost any scripting language in your Java code, which allows you to have more options to solve small problems.
1. Using Jrunscript to execute JavaScript
Each new Java platform release brings a new set of command-line toolsets that are located in the JDK's Bin directory. Java 6 is the same, where Jrunscript is a small complement to the Java Platform toolset.
Imagine a simple problem of writing command-line scripting for performance monitoring. This tool will borrow Jmap (described in the previous article in this series) to run a Java process every 5 seconds to understand the health of the process. In general, we use command-line shell scripts to do this, but the server applications here are deployed on some very different platforms, including Windows® and Linux®. The system administrator will find it painful to write shell scripts that can run on two platforms at the same time. It is common practice to write a Windows batch file and a Unix®shell script that ensures that the two files are updated synchronously.
However, anyone who has read the pragmatic Programmer knows that this is a serious violation of the DRY (Don T Repeat yourself) principle and that it creates many flaws and problems. What we really want is to write an OS-independent script that runs on all platforms.
Of course, the Java language is platform-agnostic, but this is not a case where the "system" language is required. What we need is a scripting language-for example, JavaScript.
Listing 1 shows the simple shell script we need:
Listing 1. Periodic.js
while (true) { echo ("Hello, world!") );}
Because of the frequent dealings with Web browsers, many Java developers already know JavaScript (or ECMAScript; JavaScript is a ECMAScript language developed by Netscape. The question is, how will the system administrator run the script?
The workaround, of course, is the Jrunscript utility with the JDK, as shown in Listing 2:
Listing 2. Jrunscript
C:\developerworks\5things-scripting\code\jssrc>jrunscript Periodic.jshello,world! Hello,world! Hello,world! Hello,world! Hello,world! Hello,world! Hello,world! ...
Note that you can also use the For loop to loop through the script for a specified number of times before exiting. Basically, Jrunscript allows you to perform all of the JavaScript operations. The only difference is that the runtime is not a browser, so there is no DOM in the run. Therefore, the top-most functions and objects are slightly different.
Because Java 6 takes the Rhino ECMAScript engine as part of the JDK, Jrunscript can execute any ECMAScript code passed to it, whether it's a file (as shown here) or in a more interactive REPL ("Read-evaluat E-print-loop ") shell environment. Run Jrunscript to access the REPL shell.
2. Accessing Java objects from a script
It's nice to be able to write javascript/ecmascript code, but we don't want to be forced to recompile all the code we use in the Java language-it's against our intentions. Fortunately, all code that uses the Java Scripting API engine has full access to the entire Java ecosystem, because all the code is essentially Java bytecode. So, back to our previous question, we can start the process using the traditional runtime.exec () call on the Java platform, as shown in Listing 3:
Listing 3. Runtime.exec () Start Jmap
var p = java.lang.Runtime.getRuntime (). EXEC ("Jmap", ["-histo", Arguments[0]]) p.waitfor ()
The array arguments is a standard built-in reference to the ECMAScript passed to this function parameter. In the top-most scripting environment, it is a parameter array (command-line arguments) that is passed to the script itself. So, in Listing 3, the script expects to receive a parameter that contains the VMID of the Java process to be mapped.
In addition, we can use the Jmap itself as a Java class, and then call its main () method directly, as shown in Listing 4. With this approach, we do not need the In/out/err stream of the "transfer" Process object.
Listing 4. Jmap.main ()
var args = ["-histo", Arguments[0]]packages.sun.tools.jmap.jmap.main (args)
The Packages syntax is a rhino ECMAScript identity that points to a Java package that has been created within rhino that is outside the core java.* package.
3. Invoking scripts from Java code
Invoking a Java object from a script is only half done: the Java Scripting Environment also provides the ability to invoke scripts from Java code. This only needs to instantiate a ScriptEngine object and then load and evaluate the script, as shown in Listing 5:
Listing 5. Script invocation of the Java platform
ImportJava.io.*;Importjavax.script.*; Public classapp{ Public Static voidMain (string[] args) {Try{ScriptEngine engine=NewScriptenginemanager (). Getenginebyname ("JavaScript"); for(String Arg:args) {FileReader fr=NewFileReader (ARG); Engine.eval (FR); } } Catch(IOException ioex) {ioex.printstacktrace (); } Catch(scriptexception Screx) {screx.printstacktrace (); } }}
The eval () method can also manipulate a String directly, so the script does not have to be a file on the filesystem-it can come from a database, user input, or even be generated in the application based on the environment and user actions.
4. Binding Java objects to script space
Just calling a script is not enough: scripts typically interact with objects created in the Java environment. At this point, the Java host environment must create some objects and bind them so that they can be easily found and used by the script. This process is the task of the ScriptContext object, as shown in Listing 6:
Listing 6. Binding an object to a script
ImportJava.io.*;Importjavax.script.*; Public classapp{ Public Static voidMain (string[] args) {Try{ScriptEngine engine=NewScriptenginemanager (). Getenginebyname ("JavaScript"); for(String Arg:args) {Bindings Bindings=Newsimplebindings (); Bindings.put ("Author",NewPerson ("Ted", "Neward", 39)); Bindings.put ("Title", "5 things you didn ' t Know"); FileReader FR=NewFileReader (ARG); Engine.eval (FR, bindings); } } Catch(IOException ioex) {ioex.printstacktrace (); } Catch(scriptexception Screx) {screx.printstacktrace (); } }}
Access to the object being bound is simple-the name of the bound object is introduced as a global namespace into the script, so it is simple to use the person in Rhino, as shown in Listing 7:
Listing 7. Who wrote the article?
println ("Hello from inside scripting!" ) println ("author.firstname =" + author.firstname)
As you can see, the properties of the JavaBeans style are simplified to direct access using names, as if they were fields.
5. Compiling frequently used scripts
The drawbacks of scripting languages persist in performance. The reason for this is that in most cases the scripting language is "instant", so it loses some time and CPU cycles for parsing and validating text when it executes. Many scripting languages running in the JVM will eventually convert the received code to Java bytecode, at least when the script is first parsed and validated, and the immediately compiled code disappears when the Java program shuts down. Keeping frequently used scripts in bytecode form can help improve performance.
We can use the Java Scripting API in a very natural and meaningful way. If the returned ScriptEngine implements the Compilable interface, the method compiled by this interface can be used to compile the script (passed as a String or a Reader) into a compiledscript instance, which can then be used in eval ( method to handle the compiled code repeatedly using a different binding, as shown in Listing 8:
Listing 8. Compiling the decoded code
ImportJava.io.*;Importjavax.script.*; Public classapp{ Public Static voidMain (string[] args) {Try{ScriptEngine engine=NewScriptenginemanager (). Getenginebyname ("JavaScript"); for(String Arg:args) {Bindings Bindings=Newsimplebindings (); Bindings.put ("Author",NewPerson ("Ted", "Neward", 39)); Bindings.put ("Title", "5 things you didn ' t Know"); FileReader FR=NewFileReader (ARG); if(Engineinstanceofcompilable) {System.out.println ("Compiling ..."); Compilable Compengine=(compilable) engine; Compiledscript CS=Compengine.compile (FR); Cs.eval (bindings); } ElseEngine.eval (FR, bindings); } } Catch(IOException ioex) {ioex.printstacktrace (); } Catch(scriptexception Screx) {screx.printstacktrace (); } }}
In most cases, the Compiledscript instance needs to be stored in a long-time store (for example, Servlet-context) in order to avoid repeatedly compiling the same script over and over again. However, if the script changes, you will need to create a new compiledscript to reflect the change, and once the compilation is complete, Compiledscript will no longer execute the original script file contents.
Conclusion
The Java Scripting API is a big step forward in extending the scope and functionality of Java programs, and it takes advantage of the coding efficiency of scripting languages to the Java environment. Jrunscript-It is clearly not a very difficult program to write-and Javax.script gives Java developers the advantage of scripting languages such as Ruby (JRuby) and ECMAScript (Rhino) without disrupting the ecology of the Java environment System and scalability.
5 things you don't know about the Javascripting API