Greetings,
Introduction
Java is not Javascript and most of the times when questions end up in the wrong
forum, they're moved to the other forum as soon as possible. Accidentally this
article talks about Javascript a bit but it belongs in the Java section.
Since version 1.6 of Java a script engine comes packaged with the core classes.
A script engine is an abstract little framework that offers support for other
languages, scripting languages to be exact, a.k.a. interpreters.
Java version 1.6. comes bundled with one scripting language: Javascript. The
next paragraphs decribe what you can do with this little framework and how you
can do it.
ScriptEngineMan ager
A ScriptEngineMan ager manages (sic) ScriptEngines. A ScriptEngine is Java's
interface to a scripting language. The manager doesn't know how to install,
or instantiate, an engine itself: it uses so called ScriptEngineFac tories for
that purpose. So if you want to obtain a ScriptEngine you ask the manager for
it; the manager checks if one of its factories can instantiate the appropriate
engine; if so, it asks the factory to do so and the manager returns the new
ScriptEngine to the caller.
Note that in this simple case we, the programmers, didn't need to deal with the
factories ourself. Here's a simple example
[code=java]
ScriptEngineMan ager manager= new ScriptEngineMan ager();
ScriptEngine engine= manager.getEngi neByName("JavaS cript");
try {
engine.eval("pr int('Hello, world!')");
} catch (ScriptExceptio n ex) {
ex.printStackTr ace();
}
[/code]
The first line instantiates the new manager. Line two indirectly instantiates
a ScriptEngine for the Javascript language. The next lines put that ScriptEngine
to work a bit; it's the obligatory "hello world!" program written in Javascript
and executed (or "interprete d") from the Java environment.
ScriptEngineFac tory
The factories are implementations of this interface. Factories can produce the
ScriptEngines (see previous paragraph). Let's see what those factories can tell
us; here's an example:
[code=java]
ScriptEngineMan ager manager= new ScriptEngineMan ager();
List<ScriptEngi neFactory> factories= manager.getEngi neFactories();
for (ScriptEngineFa ctory factory: factories) {
String name = factory.getEngi neName();
String version = factory.getEngi neVersion();
String language = factory.getLang uageName();
System.out.prin tln(name+"("+ve rsion+"): "+language) ;
for(String n: factory.getName s())
System.out.prin tln(n);
}
[/code]
This example instantiates a ScriptEngineMan ager again and asks it for a List
of available ScriptEngineFac tories. For every factory it's information is
retrieved and printed. Note that a language doesn't just have a name, the
language is also known by zero or more aliases; the inner loop shows the alias
names too, if available.
Read the API documentation to see what more a ScriptEngineFac tory can do for you.
When I run that little snippet on my laptop this is the output:
There's only one factory installed for the 'ECMAScript' language; that's the
old name of Javascript; the ScriptEngine implementation is Mozilla Rhino. The
language is also available under the alias names, "js", "rhino", "Javascript ",
and a few other variations.
How does the ScriptEngineMan ager know which ScriptEngineFac tories are available?
It uses quite a new naming convention for that: the jars that contain factories
(and the engines) must be stored in a special directory and the manager checks
that directory for the jars and dynamically figures out which factories are in
those jars. A detailed discussion of this mechanism is beyond the scope of this
article. Maybe in the near future I'll build a factory and an engine for the
little expression language presented in a previous article series.
ScriptEngine
A ScriptEngine is Java's gateway to a particular script interpreter. But where
does that script come from? There are two ways to feed a script to the engine:
pass it a Reader or pass it a String. When reading from the Reader the script
is supposed to be read. The Reader can be any Reader: wrapped around a socket
InputStream, or maybe just a FileReader. The script can come from anywhere.
The alternative is just to pass the entire script text as a String.
A script maintains 'bindings'. A binding is nothing more than a Map<String, Object>,
i.e. it associated Strings with objects. The engine uses those bindings for its
scripts. Here's a small example:
[code=java]
ScriptEngineMan ager manager = new ScriptEngineMan ager();
ScriptEngine engine = manager.getEngi neByName("JavaS cript");
try {
String expression = "a+b";
engine.put("a", 41);
engine.put("b", 1);
Object result = engine.eval(exp ression);
System.out.prin tln(expression+ "= "+result);
System.out.prin tln("type: "+result.getCla ss().getCanonic alName());
} catch(ScriptExc eption se) {
se.printStackTr ace();
}
[/code]
Both 'engine.put()' method calls put a new binding in the engine's bindings:
a=41 and b=1. When I run this code snippet on my laptop I see this:
That ScriptEngine was smart enough to convert a Javascript '42' to a Java Double
object. It was also smart enough to convert Java's ints '41' and '1' to the
correct objects for Javascript so that it can evaluate 'a+b'. That's cute.
As a matter of fact, a ScriptEngine can convert all sorts of Java objects to
the script's representation thereof and back again to Java's representation.
The Javascript engine can even directly use Java objects; I bluntly 'borrowed'
the following example from a piece of Sun's text on the same topic:
[code=java]
ScriptEngineMan ager manager = new ScriptEngineMan ager();
ScriptEngine engine = manager.getEngi neByName("JavaS cript");
try {
engine.eval(
"importPackage( javax.swing);" +
"var pane = " +
"JOptionPane.sh owMessageDialog (null, 'Hello, world!');");
} catch (ScriptExceptio n ex) {
ex.printStackTr ace();
}
[/code]
Invocable
Script engines don't just read, parse and interpret source text; they compile
the script text to their internal form. Such engines implement another interface,
the Invocable interface.
Here's an example showing how this interface can be used:
[code=java]
ScriptEngineMan ager manager = new ScriptEngineMan ager();
ScriptEngine engine = manager.getEngi neByName("JavaS cript");
try {
String expression = "function add(x, y) { return x+y; }";
engine.eval(exp ression);
engine.put("a", 41);
engine.put("b", 1);
Invocable invocable= (Invocable)engi ne;
System.out.prin tln("add(1, 41)= "+invocable.inv okeFunction("ad d", 1, 41));
System.out.prin tln("add(a, b)= "+invocable.inv okeFunction("ad d", "a", "b"));
System.out.prin tln("add(a, b)= "+engine.eval(" add(a, b)"));
} catch(ScriptExc eption se) {
se.printStackTr ace();
} catch (NoSuchMethodEx ception nsme) {
nsme.printStack Trace();
}
[/code]
First the small script "function add(x, y) { return x+y; }' is compiled and two
bindings are added. Next the ScriptEngine is cast to an Invocable. If the cast
fails (it doesn't in this example), an exception is thrown indicating that the
particular script cannot compile the script and keep it for later use.
When I run this little code snippet, this is the output:
Note that the second invocation tried to add the String values "a" and "b", not
the values present in the bindings. The result is the String "ab" showing that
Javascript concatenates strings when the '+' operator is applied to them.
The third call properly uses the bound values for a and b again.
Concluding remarks
There's much more that can be done with this quite new little framework. The
framework is the result of all the work done by JSR 223 and at this very moment
they're working on implementations for the Ruby language, the BeanShell interpreter
and other languages as well. This article showed the basic usage and the structure
of the ScriptEngine framework. Have fun with it. I hope to see you all again
next week and
kind regards,
Jos
Introduction
Java is not Javascript and most of the times when questions end up in the wrong
forum, they're moved to the other forum as soon as possible. Accidentally this
article talks about Javascript a bit but it belongs in the Java section.
Since version 1.6 of Java a script engine comes packaged with the core classes.
A script engine is an abstract little framework that offers support for other
languages, scripting languages to be exact, a.k.a. interpreters.
Java version 1.6. comes bundled with one scripting language: Javascript. The
next paragraphs decribe what you can do with this little framework and how you
can do it.
ScriptEngineMan ager
A ScriptEngineMan ager manages (sic) ScriptEngines. A ScriptEngine is Java's
interface to a scripting language. The manager doesn't know how to install,
or instantiate, an engine itself: it uses so called ScriptEngineFac tories for
that purpose. So if you want to obtain a ScriptEngine you ask the manager for
it; the manager checks if one of its factories can instantiate the appropriate
engine; if so, it asks the factory to do so and the manager returns the new
ScriptEngine to the caller.
Note that in this simple case we, the programmers, didn't need to deal with the
factories ourself. Here's a simple example
[code=java]
ScriptEngineMan ager manager= new ScriptEngineMan ager();
ScriptEngine engine= manager.getEngi neByName("JavaS cript");
try {
engine.eval("pr int('Hello, world!')");
} catch (ScriptExceptio n ex) {
ex.printStackTr ace();
}
[/code]
The first line instantiates the new manager. Line two indirectly instantiates
a ScriptEngine for the Javascript language. The next lines put that ScriptEngine
to work a bit; it's the obligatory "hello world!" program written in Javascript
and executed (or "interprete d") from the Java environment.
ScriptEngineFac tory
The factories are implementations of this interface. Factories can produce the
ScriptEngines (see previous paragraph). Let's see what those factories can tell
us; here's an example:
[code=java]
ScriptEngineMan ager manager= new ScriptEngineMan ager();
List<ScriptEngi neFactory> factories= manager.getEngi neFactories();
for (ScriptEngineFa ctory factory: factories) {
String name = factory.getEngi neName();
String version = factory.getEngi neVersion();
String language = factory.getLang uageName();
System.out.prin tln(name+"("+ve rsion+"): "+language) ;
for(String n: factory.getName s())
System.out.prin tln(n);
}
[/code]
This example instantiates a ScriptEngineMan ager again and asks it for a List
of available ScriptEngineFac tories. For every factory it's information is
retrieved and printed. Note that a language doesn't just have a name, the
language is also known by zero or more aliases; the inner loop shows the alias
names too, if available.
Read the API documentation to see what more a ScriptEngineFac tory can do for you.
When I run that little snippet on my laptop this is the output:
Code:
Mozilla Rhino(1.6 release 2): ECMAScript js rhino JavaScript javascript ECMAScript ecmascript
old name of Javascript; the ScriptEngine implementation is Mozilla Rhino. The
language is also available under the alias names, "js", "rhino", "Javascript ",
and a few other variations.
How does the ScriptEngineMan ager know which ScriptEngineFac tories are available?
It uses quite a new naming convention for that: the jars that contain factories
(and the engines) must be stored in a special directory and the manager checks
that directory for the jars and dynamically figures out which factories are in
those jars. A detailed discussion of this mechanism is beyond the scope of this
article. Maybe in the near future I'll build a factory and an engine for the
little expression language presented in a previous article series.
ScriptEngine
A ScriptEngine is Java's gateway to a particular script interpreter. But where
does that script come from? There are two ways to feed a script to the engine:
pass it a Reader or pass it a String. When reading from the Reader the script
is supposed to be read. The Reader can be any Reader: wrapped around a socket
InputStream, or maybe just a FileReader. The script can come from anywhere.
The alternative is just to pass the entire script text as a String.
A script maintains 'bindings'. A binding is nothing more than a Map<String, Object>,
i.e. it associated Strings with objects. The engine uses those bindings for its
scripts. Here's a small example:
[code=java]
ScriptEngineMan ager manager = new ScriptEngineMan ager();
ScriptEngine engine = manager.getEngi neByName("JavaS cript");
try {
String expression = "a+b";
engine.put("a", 41);
engine.put("b", 1);
Object result = engine.eval(exp ression);
System.out.prin tln(expression+ "= "+result);
System.out.prin tln("type: "+result.getCla ss().getCanonic alName());
} catch(ScriptExc eption se) {
se.printStackTr ace();
}
[/code]
Both 'engine.put()' method calls put a new binding in the engine's bindings:
a=41 and b=1. When I run this code snippet on my laptop I see this:
Code:
a+b= 42.0 type: java.lang.Double
object. It was also smart enough to convert Java's ints '41' and '1' to the
correct objects for Javascript so that it can evaluate 'a+b'. That's cute.
As a matter of fact, a ScriptEngine can convert all sorts of Java objects to
the script's representation thereof and back again to Java's representation.
The Javascript engine can even directly use Java objects; I bluntly 'borrowed'
the following example from a piece of Sun's text on the same topic:
[code=java]
ScriptEngineMan ager manager = new ScriptEngineMan ager();
ScriptEngine engine = manager.getEngi neByName("JavaS cript");
try {
engine.eval(
"importPackage( javax.swing);" +
"var pane = " +
"JOptionPane.sh owMessageDialog (null, 'Hello, world!');");
} catch (ScriptExceptio n ex) {
ex.printStackTr ace();
}
[/code]
Invocable
Script engines don't just read, parse and interpret source text; they compile
the script text to their internal form. Such engines implement another interface,
the Invocable interface.
Here's an example showing how this interface can be used:
[code=java]
ScriptEngineMan ager manager = new ScriptEngineMan ager();
ScriptEngine engine = manager.getEngi neByName("JavaS cript");
try {
String expression = "function add(x, y) { return x+y; }";
engine.eval(exp ression);
engine.put("a", 41);
engine.put("b", 1);
Invocable invocable= (Invocable)engi ne;
System.out.prin tln("add(1, 41)= "+invocable.inv okeFunction("ad d", 1, 41));
System.out.prin tln("add(a, b)= "+invocable.inv okeFunction("ad d", "a", "b"));
System.out.prin tln("add(a, b)= "+engine.eval(" add(a, b)"));
} catch(ScriptExc eption se) {
se.printStackTr ace();
} catch (NoSuchMethodEx ception nsme) {
nsme.printStack Trace();
}
[/code]
First the small script "function add(x, y) { return x+y; }' is compiled and two
bindings are added. Next the ScriptEngine is cast to an Invocable. If the cast
fails (it doesn't in this example), an exception is thrown indicating that the
particular script cannot compile the script and keep it for later use.
When I run this little code snippet, this is the output:
Code:
add(1, 41)= 42.0 add(a, b)= ab add(a, b)= 42.0
the values present in the bindings. The result is the String "ab" showing that
Javascript concatenates strings when the '+' operator is applied to them.
The third call properly uses the bound values for a and b again.
Concluding remarks
There's much more that can be done with this quite new little framework. The
framework is the result of all the work done by JSR 223 and at this very moment
they're working on implementations for the Ruby language, the BeanShell interpreter
and other languages as well. This article showed the basic usage and the structure
of the ScriptEngine framework. Have fun with it. I hope to see you all again
next week and
kind regards,
Jos
Comment