Skip to main content

Scripting with Balance in Design and Performance

September 20, 2007

{cs.r.title}






We all know there are a variety of languages we can choose from,
and that those languages could benefit your development process
through features like dynamic typing or closures. There are a lot
of resources on specific features of certain languages and how they
make programming easier, cooler, and so on. But since you will
probably use scripting to implement only portions of your
Java applications, the important questions that are often neglected
are when and how to use scripting in your
applications.

In this article, I'll try to explain some advanced concepts of
the Scripting API and show how they could help you successfully use
scripting in your Java applications.

The basic prerequisite for any scripting language to be used in
JVM is to have an interpreter (aka an "engine") accessible from
Java. There are two common ways to achieve this: implement the engine
in Java or make a Java wrapper around a native language
interpreter. The features of scripting engines vary a great deal
(we will come back to this later), but there are a
few that represent a core functionality of every interpreter. Every
engine has to be able to provide a context for scripts'
evaluation and obviously has to be able evaluate scripts.

In the simplest scenario, you will instantiate a scripting
engine of some desired language in your Java application, bind some
variables to provide the context to the engine, and at some point
execute a script. Afterwards, you may also obtain some variable
values from the engine context, as the values could be changed (or
set) by the executed script.

There's an obvious need for a framework that abstracts various
scripting engines and thus creates general scripting support for
Java applications. The Scripting API (javax.script)
included in Java SE 6 serves just this purpose. It enables easy
engine registration, instantiating engines through factory methods
and sharing the context between them.

In this article, I want to focus on something else. Many of the
script engines available today provide more features to developers
than just simple script evaluation. One such feature is the ability
to call standalone functions and object methods defined in the
script. In some languages you can even implement whole Java
interfaces in scripts and use them in Java as regular objects. As
we will see, these features can have a great impact on how you use
scripting in your applications--but first, we must see how the
Scripting API supports these features.

javax.script.Invocable

One of the main design goals of the Scripting API was to be as
generic as possible, so that a wide range of scripting engines
could comply with it. For that reason, the
javax.script.ScriptEngine interface defines only the
most basic operations of scripting engines: variable binding and
script evaluation. All other advanced features that scripting
engines could implement are encapsulated in separate
interfaces, making it easy for developers to determine the
characteristics of a certain engine and use it appropriately. This
architecture also allows for the use of very simple engines through
this API.

Functions

In the following example, I will demonstrate the
Invocable interface. First, let's create a simple
script (all examples in this article will be implemented in
JavaScript, so that the "http://www.mozilla.org/rhino/">Rhino engine included in JDK 6
can be used to execute them):

[prettify]function sayHello(name) {
        println("Hello " + name);
}
[/prettify]

This simple JavaScript example script, located in a
function.js file (and available in the "#resources">Resources section at the end of this article), defines one function named
sayHello that prints text to standard output. Now
let's see how to invoke this kind of function using the Scripting
API.

[prettify]package net.scriptinginjava.invocable;

import java.io.FileReader;

import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;

public class InvocableTest {

    public static void main(String[] args) throws Exception {
      ScriptEngineManager factory = new ScriptEngineManager();
      ScriptEngine engine = factory.getEngineByName("js");
      if (engine instanceof Invocable) {
        engine
          .eval(new FileReader(
            "src/net/scriptinginjava/invocable/function.js"));
        ((Invocable) engine).invokeFunction("sayHello", "World");
      }
    }

}
[/prettify]

This Java application instantiates the
ScriptEngineManager class, uses the instance to obtain
a desired script engine, and evaluates a script with it.

The interesting part is the use of the Invocable
interface. As you can see, if the engine implements the
Invocable interface, Java developers can use its
InvokeFunction method to call functions in previously
evaluated scripts. This method accepts a function name as a first
parameter and variable number of Object arguments
(varargs) that will be passed as function arguments. In this
example, the sayHello function is called and the
World argument is passed to it. As a result, the "Hello
World" text is printed to standard output.

Methods

For object-oriented scripting languages, the same interface
could be used to invoke methods on objects created in evaluated
scripts. To demonstrate this feature, let's first create a suitable
script (located in a method.js file):

[prettify]function Hello() {}

Hello.prototype.sayHello = function(value) {
        with (this) println("Hello " + value);
}

var hello = new Hello();
hello.sayHello("World1");
[/prettify]

This script creates the Hello class with a
sayHello prototype function. Afterwards, it
instantiates a hello variable and calls its method. As
a result this scripts prints "Hello World1" on standard
output.

Now let's take a look at the following Java code.

[prettify]package net.scriptinginjava.invocable;

import java.io.FileReader;

import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;

public class MethodTest {

    public static void main(String[] args) throws Exception {
      ScriptEngineManager factory = new ScriptEngineManager();
      ScriptEngine engine = factory.getEngineByName("js");
      if (engine instanceof Invocable) {
        engine
          .eval(new FileReader(
            "src/net/scriptinginjava/invocable/method.js"));
        ((Invocable) engine).invokeMethod(engine
          .get("hello"), "sayHello", "World2");
      }
    }

}
[/prettify]

Again, the example looks much like previous Java program that
evaluated the script and executed a function defined in it. The
only difference is that this example uses the
invokeMethod method to call an object method. Remember
the script we are using in this example: it instantiates a
hello object. As such, it can be obtained from
engine's context with the engine.get("hello")
statement. That is exactly the first parameter that is passed to
the invokeMethod method; an object defined in an
evaluated script whose method we want to call. Other arguments are
the same as for the invokeFunction method: the name of
the method and variable number of objects to be passed as arguments
to the method. In this example we called the sayHello
method and passed World2 as the only argument. Since the
sayHello method has been called twice (once from the
script and once from the Java application), the application will
print the following to standard output:

[prettify]Hello World1
Hello World2
[/prettify]

Interfaces

All examples shown above were nice and handy, but the real power
of the Invocable interface is the fact it can be used
to implement Java interfaces with scripts (of course, in languages
whose engines support this feature). I'll get back to the
discussion on why this feature is important and how you can benefit
from it in your Java projects, but first let's demonstrate it with
a simple example.

Let's start by defining a simple Java interface:

[prettify]package net.scriptinginjava.invocable;

public interface Hello {
        
        public void sayHello(String name);
        
        public void time();

}
[/prettify]

Now, remember our first JavaScript example used to demonstrate
function calls with the Scripting API. It defines one function
named sayHello, which accepts one argument. Let's see
how we can use this simple script to implement the interface
defined above.

[prettify]package net.scriptinginjava.invocable;

import java.io.FileReader;

import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;

public class InterfaceTest {

    public static void main(String[] args) throws Exception {
      ScriptEngineManager factory = new ScriptEngineManager();
      ScriptEngine engine = factory.getEngineByName("js");
      if (engine instanceof Invocable) {
        engine
          .eval(new FileReader(
            "src/net/scriptinginjava/invocable/function.js"));
        Hello hello = ((Invocable)engine).getInterface(Hello.class);
        hello.sayHello("World");
        //hello.time();
      }
    }

}
[/prettify]

If you execute this Java application, you'll get "Hello
World" text on your output, which mean that we managed to
implement a interface with a three-line script.

Now let's elaborate this example a bit further. The first
interesting thing to notice it that the Rhino engine (used in this
example) allows you to implement interfaces by providing scripts
that define a collection of functions implementing interface
methods. Further, in order to successfully implement an interface,
you don't have to provide functions for all methods, just those
that you are planning to use. In this example, we provided an
implementation for the sayHello method and used it
successfully from our Java application. You can experiment and
uncomment the call to the time method, in which case
you will get an exception stating that the time method
is not implemented. Of course, if we now rewrite our script in the
following manner:

[prettify]function sayHello(name) {
    println("Hello " + name);
}

function time() {
    println(new Date());
}
[/prettify]

and re-execute our Java application, we will get something like
this

[prettify]Hello World
Wed Aug 29 2007 14:53:16 GMT+0200 (CEST)
[/prettify]

Mechanisms for implementing Java interfaces vary from language
to language and you should consult their documentation (or look at
the Resources section for more reading on the
topic) before trying to implement it with your scripting engine of
choice. But one thing is common to all engines that support this
feature: they enable easy and quick implementation of Java
interfaces with suitable scripts.

Practical Usage

After this basic introduction to the Scripting API's
Invocable interface, it's time to see what kind of
value it brings to the overall development process. Probably every
Java developer is aware of the importance of interfaces and clean
API designs. A design with interfaces approach to software
development emphasizes separation of interfaces and their
implementations and is one of the fundamental design techniques
used in Java (and object-oriented programming in general).

In order to define a part of your application as a software
component
or service and expose it for use either
locally in your application or remotely, you need to define a
well-structured interface. The primarily task of this
interface is to define all operations that a component
should implement. The particular implementations of the interface
is a topic of its own and it is usually hidden from the end user of
the component. Of course, many implementations of the same interface
are possible, usually differing with regards to the context in
which the component is used. This certainly leads to a much cleaner
application design and (as we will see in a moment) is a base for
many widely used design patterns.

I'm sure that you have been aware of the aforementioned principles for
years now, but the previous discussion is important in order to ask ourselves
a crucial question: how does implementing Java interfaces with
scripts and the javax.script.Invocable interface
enhance our development process? I don't want to debate here about
dynamic typing, closures, runtime modifications, and all the other
benefits that dynamic languages generally provide (you can find
more on these topics in the Resources
section). Here, I would like to talk about appropriate use of
scripting in Java applications. As you've probably guessed, I think the
Invocable interface has a big role in that.

It easy to instantiate the script engine and evaluate scripts
from certain points of your Java application. While this practice
definitely has its place in development process, it is easy to
break good object-oriented design with unstructured (or loosely
structured) scripts. Your application most likely follow good
object-oriented practices and follow many design patterns (IoC, for
example), so you need a solution that will not break all the hard
work that is put in to have a good overall application design. That is
why the design with interfaces principle combined with
interfaces implemented in scripts could lead to both good
object-oriented designed software and rapid development with
scripting languages.

Of course, another important question involved in all
discussions related to scripting is performance. If we implement
some or most of our interfaces with scripts written in JavaScript
or Groovy, how will this
affect the performance of the overall solution? Luckily, there are
mechanisms and patterns that can help us out with this problem.

The ideal balance would be to have the flexibility of dynamic
languages in the development phase, and pure Java performance in
production. Fortunately, most of the scripting languages provide
mechanisms that could be used to achieve this goal. The main one
is the ability to compile your scripts to Java classes. After this
process, you can use them to instantiate regular Java objects. Most
of the "modern" script engines provide this functionality, but some
of them (such as Groovy) go even a step further and define Ant
tasks or Maven plugins that could be used in your build
process.

Now that we have both a script in evaluable source and its
compiled version in our project, it is much easier to achieve our
original goal. There are many ways you can instantiate a specific
implementation of an interface, depending on whether certain
conditions are met in the application. The one that I will
demonstrate here is the well-known Factory Method
pattern
.

In brief, it suggests that you should not instantiate your
objects directly but to enclose this operation in a suitable
factory method and thus add more flexibility to the
process. Let's say that we have a build environment in which our
script that implements the Hello interface is compiled to
the HelloImpl class. I will not dig into how this could
be done in different environments, since it is beyond the scope of
this article. Now look at the following Java example:

[prettify]package net.scriptinginjava.invocable;

import java.io.FileReader;

import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;

public class HelloFactory {

   private boolean debug = true;

   private ScriptEngine engine;

   public HelloFactory(boolean debug) {
      this.debug = debug;
      ScriptEngineManager factory = new ScriptEngineManager();
      engine = factory.getEngineByName("js");
   }
   
   public Hello getHello() throws Exception {
      if (debug) {
         System.out.println("scripted");
         engine
             .eval(new FileReader(
                 "src/net/scriptinginjava/invocable/interface.js"));
         return ((Invocable) engine)
             .getInterface(Hello.class);
      } else {
         System.out.println("compiled");
         return new HelloImpl();
      }
   }
   
   public static void main(String[] args) throws Exception {
      HelloFactory client = new HelloFactory(true);
      Hello hello = client.getHello();
      hello.sayHello("World");
   }

}
[/prettify]

This Java class represents a factory that is responsible for
instantiating implementations of the Hello interface.
There are a few important things about this example. First, you can
notice that this class has a debug property that is
used to determine whether it should evaluate a script or
instantiate a compiled object. This property is set through the
class' constructor and used in the getHello
method.

Now if you run this example, you will get the following
result:

[prettify]scripted
Hello World
[/prettify]

meaning that we instantiated an object by evaluating a script.
You can try to change the value of the debug parameter
to false and see what happens. It will now print the
following:

[prettify]compiled
Hello World
[/prettify]

With this technique we have achieved our goal of having a
dynamic environment to develop in and a pure-Java performance
solution in production. Of course, a debug parameter
is just a simple example and you will probably use a technique that
is appropriate to your development environment (IoC framework,
properties file configuration, etc.), but the main principle remains
the same. You implement your interfaces with scripts and enjoy
the flexibility of dynamic languages (such as the ability to modify your
application at runtime). When finished, you compile your scripts and
reconfigure the application to use the compiled implementation of
the interfaces (a pure Java class).

The design pattern that I just described above is a base for
many scripting-based designed patterns that could be used in Java
applications. There are many "traditional" design patterns whose
elements are suitable for implementation in scripts (but you can
also find patterns specific to the scripting environment). I
discuss some of these patterns in "http://www.scriptinginjava.net/chapter8/">chapter 8 of my
book, Scripting in Java.

Conclusion

When thinking about scripting languages in Java applications,
two key questions come up: how it will affect software architecture
and how will it affect performance. By keeping the design with
interface
principle as our focus and applying the simple patterns
shown in this article, we can successfully tackle both of these
questions. The rest is left to you to choose what language best
suits your programming needs, but also when and how much you want to use scripting
in your projects.

Resources


width="1" height="1" border="0" alt=" " />
Dejan Bosanac is a software developer, technology consultant and author.
Related Topics >> Programming   |