Java SE 6's New Scripting and Compiling Goodies
Among the most intriguing features of the first Java SE 6 release candidate are the capabilities the new scripting and compiling APIs deliver.
by Eric Bruno
ince I wrote a DevX article on the first beta release of Java Standard Edition (SE) version 6 in February of this year, two new early-access versions have been released. This article discusses the first Java SE 6 release candidate, a feature-complete, almost fully tested implementation of the newest version of desktop Java. While the article in February focused on many GUI features (and declared Java SE 6 a desktop winner), this one focuses on some other new features and improvements, namely scripting and the compiler API.Java Supplants Scripting
In the early days of the enterprise Java standard (what was called J2EE and is now called Java EE), many Web site designers employed server-side scripting to implement functionality. Available tools and software made it easy to develop and deploy this type of business logic, but they had two huge drawbacks: poor performance and little debugging assistance. Server-side scripts, such as those implemented as JavaScript, are not compiled; they're interpreted as they execute. This leads to poor performance and little to no scalability.
The emergence of the Java Servlet specification and, subsequently, JavaServer Pages (JSP) put an end to all of that. Because these server-side technologies were based on pure Java, tools emerged to help you debug the code. Additionally, since the Java code could be compiled (thanks to the HotSpot compiler), performance and scalability were excellent. This marked the end of mainstream server-side scripting.
The Return of Scripting Languages
With the advent of Asynchronous JavaScript and XML (AJAX)-based rich Web applications, scripting languages have made a comeback. This time, the script is meant to run at the client, where scalability generally isn't an issue (each client runs its own browser and hence its own scripts). The result is a very dynamic Web page that includes rich application features with acceptable performance.
Further reasons to use scripting languages in a Web application include dynamic type conversion (automatic conversions from values to strings), access to the operating system environment (as with shell scripts), and the use of specialized Web frameworks for scripting languages. For these reasons, dynamic scripting languages have reemerged on the server as well.
However, this reintroduction of scripting languages has left programmers wanting the following:
- Access to the rich resources of the Java platform, as well as custom JAR files, from scripting languages
- Access to scripting languages from the Java platform itself
Java SE 6 satisfies both of these requests with JSR 223 (Scripting for the Java Platform). When you join the features of both the Java language and available scripting languages, you can pick and choose the strengths of both environments to use at the same time. For instance, Java developers can access Perl scripts to perform string operations that are best done with Perl. Additionally, AJAX developers can invoke Java objects directly from script embedded within a Web page to perform complex operations. For example, since database access is far less robust (if not impractical) from JavaScript as compared with Java, you can perform this and other complex operations in Java code and simply invoke the Java code from your page's script.
Java SE 6 ships with the Mozilla Rhino scripting engine, but you're free to substitute any available scripting engine that complies with JSR 223. (For a list of JSR 223-compliant script engines, click here.) This includes implementations of Python and Ruby. The new javax.script APIs provide access to the scripting environments from Java. For instance, the following code iterates through the list of available scripting engines and outputs the language types and the associated engines:
import java.util.*;
import javax.script.*;
public class Main
{
public static void main(String[] args)
{
try {
ScriptEngineManager mgr = new ScriptEngineManager();
List<ScriptEngineFactory> factories = mgr.getEngineFactories();
System.out.println("Available script engines:");
for ( int i = 0; i < factories.size(); i++ )
{
ScriptEngineFactory factory = factories.get(i);
String engine = factory.getEngineName();
String language = factory.getLanguageName();
System.out.println("-------------------------------------------");
System.out.println("Language: " + language );
System.out.println("Engine: " + engine);
System.out.println("-------------------------------------------");
}
}
catch ( Exception e ) {
e.printStackTrace();
}
}
}
JSR 223-compliant scripting engines must implement the javax.script.ScriptEngine interface and be packaged in JAR files with a META-INF/services/javax.script.ScriptEngineFactory text resource. Once you deploy a compliant scripting engine JAR file into your Java environment or with your application, you can access this engine from your Java code. To do this, simply include the engine's JAR file within your application's classpath.
Let's examine the Rhino JavaScript engine's Java scripting features with some simple examples.
The Java Scripting Engine in Action
The Java SE 6 installation comes with sample applications to demonstrate many of the release's new features. One of these samples, ScriptPad, is a 99 percent JavaScript application that loads and executes other scripts, all through the Java scripting API. It uses Java to load the Rhino JavaScript engine and execute the five script files that are built into ScriptPad. Included in the sample/scripting/scriptpad folder within your Java SE 6 installation root folder, ScriptPad comes complete with a NetBeans 5.5 project file, which you can use to easily build and execute it. (Figure 1 shows the main window of a ScriptPad application.)
You can also use the application (through the Java Scripting API) to run other scripts that you've written. For instance, the script shown within the ScriptPad window in Figure 1 accesses Java's static System object, invokes its method (getProperties), uses the resulting Properties object to get the name of the host operating system, and outputs the result in an alert window.
When you execute this script on a Windows XP machine, you get the output shown in Figure 2.
This is only a simple example of what you can do when you combine scripting and Java. However, it does illustrate how seamless the integration really is. You can execute any script entered in ScriptPad with the Tools..Run menu option.
The following Java code loads the Rhino JavaScript engine, loads the script contained within the file browse.js, and executes the function named browse (This script was adapted from a sample that is distributed with Java SE 6):
import java.util.*;
import java.io.*;
import javax.script.*;
public class Main
{
public Main()
{
try {
ScriptEngineManager m = new ScriptEngineManager();
ScriptEngine engine = m.getEngineByName("javascript");
if ( engine != null )
{
InputStream is = this.getClass().getResourceAsStream("browse.js");
Reader reader = new InputStreamReader(is);
engine.eval(reader);
Invocable invocableEngine = (Invocable)engine;
invocableEngine.invokeFunction("browse");
}
}
catch ( Exception e ) {
e.printStackTrace();
}
}
public static void main(String[] args)
{
Main m = new Main();
}
}
The browse.js script itself (see Listing 1) uses the new desktop features of Java SE 6 to open the default browser on the host OS to the page http://www.ericbruno.com.
This paradoxical example where Java invokes a script that in turn invokes Java to load an HTML page demonstrates just how dynamic the scripting API can be.
JSR 199: The Compiler API
The addition of the compiler API (JSR 199) into Java SE 6 presents new possibilities within the language. For instance, my previous article mentioned a small project that used this API. The code I wrote generated and compiled classes used to access an application's database tables. Since then, another interesting implementation of this API has caught my attention. It's called Project Gosling.
Named after the main character in the book The Ugly Duckling, Gosling rewrites Ant to use Java code in place of XML files for performing Ant-related tasks, such as project builds. Gosling, itself written in Java, leverages the Compiler API to be able to compile and then execute Java-based Ant scripts. Listing 2 contains the Ant-script Java file to build Gosling.
The compiler API consists of new javax packages, such as javax.lang.model, javax.annotation, and javax.annotation.processing. The ability to use Gosling and Java build scripts greatly enhances the Ant build environment. For Java developers, using the Java programming language to perform build tasks should be more natural than using XML. Simply checking dependencies, performing looping operations, and making decisions are much more obvious and understandable in Java.
Beyond database-access code and Java build files, you can use the compiler API to implement dynamic language-processing tools, such as those that compile JSP files, and other dynamic languages such as PHP or JRuby. Sun declares that this API is not meant for the everyday compiler, but I submit that all developers should think of ways to extend the power of Java through the use of Java itself and the new compiler API.
The All-Purpose Web Development Language
In February, I declared Java SE 6 a desktop winner. I now would add that it will empower everyday developers to build better tools and frameworks that will catapult Java further as the language for serious enterprise Web development. The addition of the scripting API and the compiler API—combined with becoming a GPL v2 licensed open-source project—means that Java will remain the right tool for any Web-related job. The choice to integrate with otherwise alternative languages such as Ruby ensures that Java will be only augmented by them, as opposed to replaced by them.