Java™ スクリプト API は、Java コードからスクリプトエンジンを使用するための、スクリプト言語に依存しないフレームワークです。Java スクリプト API を使用すると、カスタマイズ可能/拡張可能なアプリケーションを Java 言語で記述し、カスタマイズスクリプト言語の選択をエンドユーザーに任せることができます。Java アプリケーション開発者が開発中に拡張言語を選択する必要はありません。アプリケーションを JSR-223 API で記述した場合、ユーザーは任意の JSR-223 準拠のスクリプト言語を使用できます。
topJava スクリプト機能は、javax.script
パッケージ内にあります。これは比較的小さい単純な API です。スクリプト API の開始位置は、ScriptEngineManager
クラスです。ScriptEngineManager オブジェクトは、jar ファイルサービス発見メカニズムを使用してスクリプトエンジンを発見できます。特定のスクリプト言語で記述されたスクリプトを解釈する ScriptEngine オブジェクトをインスタンス化することもできます。スクリプト API を使用するもっとも単純な方法は、次のとおりです。
ScriptEngineManager
オブジェクトを作成します。ScriptEngine
オブジェクトを取得します。ScriptEngine
の eval
メソッドを使用して、スクリプトを評価します。ここで、いくつかのサンプルコードを見てみます。必須ではありませんが、これらの例を見ることは JavaScript の理解に役立つ場合があります。
topScriptEngineManager
インスタンスから、getEngineByName
メソッドを使用して JavaScript エンジンインスタンスを要求します。スクリプトエンジンで、指定された文字列を JavaScript コードとして実行するために eval
メソッドが呼び出されます。簡潔にするために、この例およびこれ以降の例では、例外処理は示しません。javax.script
API からスローされるチェック例外および実行時例外が存在します。言うまでもなく、例外を適切に処理する必要があります。
import javax.script.*;
public class EvalScript {
public static void main(String[] args) throws Exception {
// create a script engine manager
ScriptEngineManager factory = new ScriptEngineManager();
// create a JavaScript engine
ScriptEngine engine = factory.getEngineByName("JavaScript");
// evaluate JavaScript code from String
engine.eval("print('Hello, World')");
}
}
top
この例では、入力ソースとして java.io.Reader
を受け入れる eval
メソッドを呼び出します。指定されたリーダーによって読み取られるスクリプトが実行されます。この方法では、関連する入力ストリームオブジェクトをリーダーとしてラップすることによって、ファイル、URL、およびリソースからスクリプトを実行できます。
import javax.script.*;
public class EvalFile {
public static void main(String[] args) throws Exception {
// create a script engine manager
ScriptEngineManager factory = new ScriptEngineManager();
// create JavaScript engine
ScriptEngine engine = factory.getEngineByName("JavaScript");
// evaluate JavaScript code from given file - specified by first argument
engine.eval(new java.io.FileReader(args[0]));
}
}
次のテキストを含む「test.js」という名前のファイルがあるとします。
println("This is hello from test.js");
前述の Java は次のように実行できます。
java EvalFile test.js
top
Java アプリケーションでスクリプトエンジンおよびスクリプトを埋め込む場合、アプリケーションオブジェクトをグローバル変数としてスクリプトに公開することがあります。この例は、アプリケーションオブジェクトをグローバル変数としてスクリプトに公開する方法を示しています。アプリケーション内に java.io.File
を作成し、同じものを「file」という名前でグローバル変数として公開します。スクリプトは変数にアクセスできます。たとえば、スクリプト上で public メソッドを呼び出すことができます。Java オブジェクト、メソッド、およびフィールドにアクセスする構文は、スクリプト言語によって異なります。JavaScript は、ほとんどの「自然な」Java に類似した構文をサポートしています。
public class ScriptVars {
public static void main(String[] args) throws Exception {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("JavaScript");
File f = new File("test.txt");
// expose File object as variable to script
engine.put("file", f);
// evaluate a script string. The script accesses "file"
// variable and calls method on it
engine.eval("print(file.getAbsolutePath())");
}
}
top
特定のスクリプト関数を繰り返し呼び出す場合があります。たとえば、アプリケーションメニュー機能がスクリプトによって実装されている場合があります。メニューのアクションイベントハンドラで、特定のスクリプト関数を呼び出します。次の例は、Java コードからの特定のスクリプト関数の呼び出しを示しています。
import javax.script.*; public class InvokeScriptFunction { public static void main(String[] args) throws Exception { ScriptEngineManager manager = new ScriptEngineManager(); ScriptEngine engine = manager.getEngineByName("JavaScript"); // JavaScript code in a String String script = "function hello(name) { print('Hello, ' + name); }"; // evaluate script engine.eval(script); // javax.script.Invocable is an optional interface. // Check whether your script engine implements or not! // Note that the JavaScript engine implements Invocable interface. Invocable inv = (Invocable) engine; // invoke the global function named "hello" inv.invokeFunction("hello", "Scripting!!" ); } }
スクリプト言語が (JavaScript のように) オブジェクトベースまたはオブジェクト指向の場合、スクリプトオブジェクトでスクリプトメソッドを呼び出すことができます。
import javax.script.*; public class InvokeScriptMethod { public static void main(String[] args) throws Exception { ScriptEngineManager manager = new ScriptEngineManager(); ScriptEngine engine = manager.getEngineByName("JavaScript"); // JavaScript code in a String. This code defines a script object 'obj' // with one method called 'hello'. String script = "var obj = new Object(); obj.hello = function(name) { print('Hello, ' + name); }"; // evaluate script engine.eval(script); // javax.script.Invocable is an optional interface. // Check whether your script engine implements or not! // Note that the JavaScript engine implements Invocable interface. Invocable inv = (Invocable) engine; // get script object on which we want to call the method Object obj = engine.get("obj"); // invoke the method named "hello" on the script object "obj" inv.invokeMethod(obj, "hello", "Script Method !!" ); } }top
Java から特定のスクリプト関数を呼び出す代わりに、スクリプト関数またはメソッドを使用して Java インタフェースを実装すると便利な場合があります。また、インタフェースを使用することによって、多くの場所で javax.script
API の使用を回避できます。インタフェース実装側オブジェクトを取得し、さまざまな Java API に渡すことができます。次の例は、スクリプトでの java.lang.Runnable
インタフェースの実装を示しています。
import javax.script.*;
public class RunnableImpl {
public static void main(String[] args) throws Exception {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("JavaScript");
// JavaScript code in a String
String script = "function run() { println('run called'); }";
// evaluate script
engine.eval(script);
Invocable inv = (Invocable) engine;
// get Runnable interface object from engine. This interface methods
// are implemented by script functions with the matching name.
Runnable r = inv.getInterface(Runnable.class);
// start a new thread that runs the script implemented
// runnable interface
Thread th = new Thread(r);
th.start();
}
}
スクリプト言語がオブジェクトベースまたはオブジェクト指向の場合、スクリプトオブジェクト上のスクリプトメソッドを使用して Java インタフェースを実装できます。これにより、インタフェースメソッドのスクリプトグローバル関数を呼び出す必要がなくなります。スクリプトオブジェクトは、インタフェース実装側に関連付けられている「状態」を格納できます。
import javax.script.*;
public class RunnableImplObject {
public static void main(String[] args) throws Exception {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("JavaScript");
// JavaScript code in a String
String script = "var obj = new Object(); obj.run = function() { println('run method called'); }";
// evaluate script
engine.eval(script);
// get script object on which we want to implement the interface with
Object obj = engine.get("obj");
Invocable inv = (Invocable) engine;
// get Runnable interface object from engine. This interface methods
// are implemented by script methods of object 'obj'
Runnable r = inv.getInterface(obj, Runnable.class);
// start a new thread that runs the script implemented
// runnable interface
Thread th = new Thread(r);
th.start();
}
}
top
スクリプト変数の例では、アプリケーションオブジェクトをスクリプトグローバル変数として公開する方法について説明しました。スクリプトの複数のグローバル「スコープ」を公開できます。単一のスコープは、javax.script.Bindings
のインスタンスです。このインタフェースは、java.util.Map<String, Object>
から派生します。スコープは、名前が空でも null でもない文字列である名前-値のペアのセットです。複数のスコープは、javax.script.ScriptContext
インタフェースによってサポートされています。スクリプトコンテキストは、スコープごとに関連付けられているバインディングを持つ 1 つ以上のスコープをサポートしています。デフォルトでは、すべてのスクリプトエンジンにデフォルトのスクリプトコンテキストがあります。デフォルトのスクリプトコンテキストには、「ENGINE_SCOPE」という少なくとも 1 つのスコープがあります。スクリプトコンテキストによってサポートされているさまざまなスコープが、getScopes
メソッドによって使用できます。
import javax.script.*;
public class MultiScopes {
public static void main(String[] args) throws Exception {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("JavaScript");
engine.put("x", "hello");
// print global variable "x"
engine.eval("println(x);");
// the above line prints "hello"
// Now, pass a different script context
ScriptContext newContext = new SimpleScriptContext();
Bindings engineScope = newContext.getBindings(ScriptContext.ENGINE_SCOPE);
// add new variable "x" to the new engineScope
engineScope.put("x", "world");
// execute the same script - but this time pass a different script context
engine.eval("println(x);", newContext);
// the above line prints "world"
}
}
top
Oracle による JDK 7 の実装は、Mozilla Rhino に基づく JavaScript スクリプトエンジンと相互にバンドルされています。これは、javax.script (JSR-223) API とともに使用されます。
このディレクトリとサブディレクトリには、Oracle によって修正された Rhino のソースが含まれています。Mozilla Rhino のソースは、Mozilla Public License Version 1.1 によってライセンスされています。このライセンスのコピーは、http://www.mozilla.org/MPL
で取得できます。com.sun.script
パッケージおよびサブパッケージ内のソースは、MPL によってカバーされません。
var v = new java.lang.Runnable() { run: function() { print('hello'); } } v.run();ほとんどの場合、JavaAdapter は Java anonymizer クラスのような構文を使用して単一のインタフェースを実装するために使用されます。Java クラスの拡張または複数のインタフェースの実装のために JavaAdapter を使用することは、非常にまれです。
Java クラス、オブジェクト、およびメソッドへのアクセスは、ほとんどの場合簡単です。特に、JavaScript からフィールドおよびメソッドへのアクセスは、Java からの場合と同じです。ここでは、JavaScript から Java へのアクセスの重要な側面について説明します。詳細は、http://www.mozilla.org/rhino/scriptjava.html を参照してください。次の例は、Java にアクセスする JavaScript のコードの一部です。このセクションでは、JavaScript の知識が必要です。JavaScript ではなくほかの JSR-223 スクリプト言語を使用する場合、このセクションを飛ばして先に進んでください。
topJava パッケージおよびクラスをインポートするために、組み込み関数 importPackage
および importClass
を使用できます。
// Import Java packages and classes
// like import package.*; in Java
importPackage(java.awt);
// like import java.awt.Frame in Java
importClass(java.awt.Frame);
// Create Java Objects by "new ClassName"
var frame = new java.awt.Frame("hello");
// Call Java public methods from script
frame.setVisible(true);
// Access "JavaBean" properties like "fields"
print(frame.title);
Packages グローバル変数を使用すると、Java パッケージにアクセスできます。例: Packages.java.util.Vector
、Packages.javax.swing.JFrame
。「java」は「Packages.java」のショートカットです。javax、org、edu、com、net 接頭辞に等価のショートカットがあるため、実際上すべての JDK プラットフォームクラスに「Packages」接頭辞なしでアクセスできます。
java.lang は、Java とは異なりデフォルトではインポートされません。これは、JavaScript の組み込みの Object、Boolean、Math などと競合するためです。
importPackage
および importClass
関数は、JavaScript のグローバル変数スコープを「煩雑」にします。これを回避するために、JavaImporter を使用できます。
// create JavaImporter with specific packages and classes to import
var SwingGui = new JavaImporter(javax.swing,
javax.swing.event,
javax.swing.border,
java.awt.event);
with (SwingGui) {
// within this 'with' statement, we can access Swing and AWT
// classes by unqualified (simple) names.
var mybutton = new JButton("test");
var myframe = new JFrame("test");
}
top
Java オブジェクトの作成は Java の場合と同じですが、JavaScript で Java 配列を作成するには、Java リフレクションを明示的に使用する必要があります。ただし、いったん作成されると、要素のアクセスまたは長さのアクセスは Java の場合と同じです。また、Java メソッドが Java 配列を想定する場合にスクリプト配列を使用できます (自動変換)。このため、ほとんどの場合、Java 配列を明示的に作成する必要はありません。
// create Java String array of 5 elements
var a = java.lang.reflect.Array.newInstance(java.lang.String, 5);
// Accessing elements and length access is by usual Java syntax
a[0] = "scripting is great!";
print(a.length);
top
Java 匿名クラスのような構文を使用すると、JavaScript に Java インタフェースを実装できます。
var r = new java.lang.Runnable() {
run: function() {
print("running...\n");
}
};
// "r" can be passed to Java methods that expect java.lang.Runnable
var th = new java.lang.Thread(r);
th.start();
単一のメソッドでのインタフェースが想定されている場合、スクリプト関数を直接渡すことができます (自動変換)。
function func() {
print("I am func!");
}
// pass script function for java.lang.Runnable argument
var th = new java.lang.Thread(func);
th.start();
top
Java メソッドが引数の型によってオーバーロードされる場合があります。Java では、オーバーロードの解決はコンパイル時に発生します (javac により実行)。スクリプトから Java メソッドを呼び出すと、スクリプトインタプリタ/コンパイラは適切なメソッドを選択する必要があります。JavaScript エンジンでは、特別なことを行う必要はありません。正しい Java メソッドオーバーロードバリアントが、引数の型に基づいて選択されます。ただし、特定のオーバーロードバリアントを明示的に選択する (または、選択する必要がある) 場合があります。
var out = java.lang.System.out;
// select a particular println function
out["println(java.lang.Object)"]("hello");
JavaScript の Java メソッドオーバーロードの解決の詳細は、Java メソッドのオーバーロードおよび LiveConnect 3 を参照してください
topJSR-223 準拠のスクリプトエンジンの実装について、詳細には説明しません。最低限、javax.script.ScriptEngine
および javax.script.ScriptEngineFactory
インタフェースを実装する必要があります。abstract クラス javax.script.AbstractScriptEngine
は、ScriptEngine
インタフェースのいくつかのメソッドに有用なデフォルトを提供します。