この章では、JavaスクリプトAPI (JSR 223で定義)を使用してJavaアプリケーションにスクリプトを埋め込む方法について説明し、JavaスクリプトAPIの機能を例示するJavaクラスを含む例をいくつか示します。
JavaスクリプトAPIは、javax.scriptパッケージのクラスとインタフェースで構成されます。 これは、ScriptEngineManagerクラスを出発点とする比較的小規模で単純なパッケージです。 ScriptEngineManagerオブジェクトは、JARファイル・サービス検出メカニズムによってスクリプト・エンジンを検出し、特定のスクリプト言語で記述されたスクリプトを解釈するScriptEngineオブジェクトをインスタンス化できます。 javax.scriptパッケージの詳細は、Java SE仕様(http://docs.oracle.com/javase/8/docs/api/javax/script/package-summary.html)を参照してください。
Nashornエンジンは、Java SE Development Kit (JDK)にバンドルされているデフォルトのECMAScript (JavaScript)エンジンです。 Nashornエンジンは、オラクル社がOpenJDKプロジェクトの一環として完全にJavaで開発しました。 Nashornプロジェクトの詳細は、http://openjdk.java.net/projects/nashorn/を参照してください。
NashornはJavaスクリプトAPIで使用されるデフォルトのECMAScriptエンジンですが、JSR 223に準拠する任意のスクリプト・エンジンを使用することも、独自のエンジンを実装することもできます。 このドキュメントでは、JSR 223に準拠するスクリプト・エンジンの実装について説明しませんが、もっとも基本的なレベルでは、javax.script.ScriptEngineおよびjavax.script.ScriptEngineFactoryインタフェースを実装する必要があります。 抽象クラスjavax.script.AbstractScriptEngineは、ScriptEngineインタフェースのいくつかのメソッドに有用なデフォルトを提供します。
JavaスクリプトAPIを使用するには:
ScriptEngineManagerオブジェクトを作成します。
マネージャからScriptEngineオブジェクトを取得します。
スクリプト・エンジンのeval()メソッドを使用してスクリプトを評価します。
次の例は、JavaでJavaスクリプトAPIを使用する方法を示しています。 例を簡単にするため、例外は処理されていません。 ただし、JavaスクリプトAPIによってスローされるチェック例外と実行時例外が存在するため、それらを適切に処理する必要があります。 すべての例で、ScriptEngineManagerクラスのインスタンスとgetEngineByName()メソッドを使用して、Nashornエンジン(ScriptEngineクラスのインスタンス)を要求します。 指定されたエンジンが存在しない場合は、nullを返します。 Nashornエンジンの使用方法の詳細は、『Nashornユーザーズ・ガイド』を参照してください。
この例では、スクリプト・エンジン・インスタンスのeval()メソッドを呼び出し、StringオブジェクトのJavaScriptコードを実行します。
import javax.script.*;
public class EvalScript {
public static void main(String[] args) throws Exception {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("nashorn");
// evaluate JavaScript code
engine.eval("print('Hello, World')");
}
}
この例では、eval()メソッドがscript.jsという名前のファイルからJavaScriptコードを読み取るFileReaderオブジェクトを取ります。 様々な入力ストリーム・オブジェクトをリーダーとしてラップすることにより、ファイル、URLおよびその他のリソースからスクリプトを実行できます。
import javax.script.*;
public class EvalFile {
public static void main(String[] args) throws Exception {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("nashorn");
// evaluate JavaScript code
engine.eval(new java.io.FileReader("script.js"));
}
}
この例では、Fileオブジェクトを作成し、put()メソッドを使用してそれをfileという名前のグローバル変数としてエンジンに公開します。 次に、この変数にアクセスしてgetAbsolutePath()メソッドを呼び出すJavaScriptコードを指定して、eval()メソッドを呼び出します。
|
ノート: 変数として公開されたJavaオブジェクトのフィールドにアクセスしてメソッドを呼び出す構文は、スクリプト言語によって異なります。 この例では、Javaによく似たJavaScript構文を使用します。 |
import javax.script.*;
import java.io.*;
public class ScriptVars {
public static void main(String[] args) throws Exception {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("nashorn");
// create File object
File f = new File("test.txt");
// expose File object as a global variable to the engine
engine.put("file", f);
// evaluate JavaScript code and access the variable
engine.eval("print(file.getAbsolutePath())");
}
}
この例では、1つのパラメータを持つ関数を定義するJavaScriptコードを指定してeval()メソッドを呼び出します。 次に、Invocableオブジェクトを作成し、そのinvokeFunction()メソッドを使用して関数を呼び出します。
|
ノート: すべてのスクリプト・エンジンがInvocableインタフェースを実装しているわけではありません。 この例ではNashornエンジンを使用しており、このエンジンによって前に評価されたスクリプト内の関数を呼び出すことができます。 |
import javax.script.*;
public class InvokeScriptFunction {
public static void main(String[] args) throws Exception {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("nashorn");
// evaluate JavaScript code that defines a function with one parameter
engine.eval("function hello(name) { print('Hello, ' + name) }");
// create an Invocable object by casting the script engine object
Invocable inv = (Invocable) engine;
// invoke the function named "hello" with "Scripting!" as the argument
inv.invokeFunction("hello", "Scripting!");
}
}
この例では、1つのメソッドを持つオブジェクトを定義するJavaScriptコードを指定してeval()メソッドを呼び出します。 次に、スクリプト・エンジンのget()メソッドを使用して、このオブジェクトをスクリプトからJavaアプリケーションに公開します。 次に、Invocableオブジェクトを作成し、そのinvokeMethod()メソッドを使用してスクリプト・オブジェクトに定義されたメソッドを呼び出します。
|
ノート: すべてのスクリプト・エンジンがInvocableインタフェースを実装しているわけではありません。 この例ではNashornエンジンを使用しており、このエンジンによって前に評価されたスクリプト内のメソッドを呼び出すことができます。 |
import javax.script.*;
public class InvokeScriptMethod {
public static void main(String[] args) throws Exception {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("nashorn");
// evaluate JavaScript code that defines an object with one method
engine.eval("var obj = new Object()");
engine.eval("obj.hello = function(name) { print('Hello, ' + name) }");
// expose object defined in the script to the Java application
Object obj = engine.get("obj");
// create an Invocable object by casting the script engine object
Invocable inv = (Invocable) engine;
// invoke the method named "hello" on the object defined in the script
// with "Script Method!" as the argument
inv.invokeMethod(obj, "hello", "Script Method!");
}
}
この例では、関数を定義するJavaScriptコードを指定してeval()メソッドを呼び出します。 次に、Invocableオブジェクトを作成し、そのgetInterface()メソッドを使用してRunnableインタフェース・オブジェクトを作成します。 このインタフェースのメソッドは、一致する名前を持つスクリプト関数によって実装されます(この場合は、run()関数を使ってインタフェース・オブジェクトのrun()メソッドが実装されます)。 最後に、スクリプト関数を実行する新しいスレッドを開始します。
import javax.script.*;
public class ImplementRunnable {
public static void main(String[] args) throws Exception {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("nashorn");
// evaluate JavaScript code that defines a function with one parameter
engine.eval("function run() { print('run() function called') }");
// create an Invocable object by casting the script engine object
Invocable inv = (Invocable) engine;
// get Runnable interface object
Runnable r = inv.getInterface(Runnable.class);
// start a new thread that runs the script
Thread th = new Thread(r);
th.start();
th.join();
}
}
この例では、1つのメソッドを持つオブジェクトを定義するJavaScriptコードを指定してeval()メソッドを呼び出します。 次に、スクリプト・エンジンのget()メソッドを使用して、このオブジェクトをスクリプトからJavaアプリケーションに公開します。 次に、Invocableオブジェクトを作成し、そのgetInterface()メソッドを使用してRunnableインタフェース・オブジェクトを作成します。 このインタフェースのメソッドは、一致する名前を持つスクリプト・オブジェクトのメソッドによって実装されます(この場合は、objオブジェクトのrunメソッドを使ってインタフェース・オブジェクトのrun()メソッドが実装されます)。 最後に、スクリプト・オブジェクトのメソッドを実行する新しいスレッドを開始します。
import javax.script.*;
public class ImplementRunnableObject {
public static void main(String[] args) throws Exception {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("nashorn");
// evaluate JavaScript code that defines a function with one parameter
engine.eval("var obj = new Object()")
engine.eval("obj.run = function() { print('obj.run() method called') }");
// expose object defined in the script to the Java application
Object obj = engine.get("obj");
// create an Invocable object by casting the script engine object
Invocable inv = (Invocable) engine;
// get Runnable interface object
Runnable r = inv.getInterface(obj, Runnable.class);
// start a new thread that runs the script
Thread th = new Thread(r);
th.start();
th.join();
}
}
この例では、スクリプト・エンジンのput()メソッドを使用して変数xをStringオブジェクトhelloに設定します。 次に、eval()メソッドを使用して変数をデフォルトのスコープで出力します。 次に、異なるスクリプト・コンテキストを定義し、そのスコープを使用して同じ変数を異なる値(Stringオブジェクトworld)に設定します。 最後に、異なる値を表示する新しいスクリプト・コンテキストで変数を出力します。
単一のスコープは、javax.script.Bindingsインタフェースのインスタンスです。 このインタフェースは、java.util.Map<String, Object>インタフェースから派生します。 スコープは、名前が空でもnullでもないStringオブジェクトである名前と値のペアのセットです。 javax.script.ScriptContextインタフェースは、各スコープにBindingsが関連付けられている複数のスコープをサポートします。 デフォルトでは、すべてのスクリプト・エンジンにデフォルトのスクリプト・コンテキストがあります。 デフォルトのスクリプト・コンテキストには、staticフィールドENGINE_SCOPEで表されるスコープが少なくとも1つ存在します。 スクリプト・コンテキストでサポートされる様々なスコープをgetScopes()メソッドで取得できます。
import javax.script.*;
public class MultipleScopes {
public static void main(String[] args) throws Exception {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("nashorn");
// set global variable
engine.put("x","hello");
// evaluate JavaScript code that prints the variable (x = "hello")
engine.eval("print(x)");
// define a different script context
ScriptContext newContext = new SimpleScriptContext();
newContext.setBindings(engine.createBindings(), ScriptContext.ENGINE_SCOPE);
Bindings engineScope = newContext.getBindings(ScriptContext.ENGINE_SCOPE);
// set the variable to a different value in another scope
engineScope.put("x", "world");
// evaluate the same code but in a different script context (x = "world")
engine.eval("print(x)", newContext);