Scripting

Collapse
X
 
  • Time
  • Show
Clear All
new posts
  • JosAH
    Recognized Expert MVP
    • Mar 2007
    • 11453

    Scripting

    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:

    Code:
    Mozilla Rhino(1.6 release 2): ECMAScript
    js
    rhino
    JavaScript
    javascript
    ECMAScript
    ecmascript
    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:

    Code:
    a+b= 42.0
    type: java.lang.Double
    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:

    Code:
    add(1, 41)= 42.0
    add(a, b)= ab
    add(a, b)= 42.0
    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
  • manuelgk
    New Member
    • Sep 2007
    • 54

    #2
    Hi! Well this article result really useful, I'm a total beginner in Java and I'm trying to import a .js file into a java code. I was wondering if you could tell me how can I do that. I found this code but I don't know if it is correct to put that:
    Code:
    ScriptEngineManager engineMgr = new ScriptEngineManager();
      		  ScriptEngine engine = engineMgr.getEngineByName("ECMAScript");
    //joscript is my .js file
    		  InputStream is = this.getClass().getResourceAsStream("/joscript.js");
                      try {
        			Reader reader = new InputStreamReader(is);
        			engine.eval(reader);
      		  }
                      catch (ScriptException ex) {
        			ex.printStackTrace();
      	          }
    I don't know if I need to import a class too. I put this:
    Code:
    import java.ScriptEngineManager.*;
    is it correct?
    Thanks and good job for the article.

    Comment

    • hsn
      New Member
      • Sep 2007
      • 237

      #3
      great article Jos, i had no idea about the ScripEngine stuff.
      keep it up

      Kind regards
      hsn

      Comment

      Working...