Java スクリプトプログラマーズガイド

Java スクリプト API の対象者

スクリプト言語には、次のような有用な特性があります。

Java™ スクリプト API は、Java コードからスクリプトエンジンを使用するための、スクリプト言語に依存しないフレームワークです。Java スクリプト API を使用すると、カスタマイズ可能/拡張可能なアプリケーションを Java 言語で記述し、カスタマイズスクリプト言語の選択をエンドユーザーに任せることができます。Java アプリケーション開発者が開発中に拡張言語を選択する必要はありません。アプリケーションを JSR-223 API で記述した場合、ユーザーは任意の JSR-223 準拠のスクリプト言語を使用できます。

top

スクリプトパッケージ

Java スクリプト機能は、javax.script パッケージ内にあります。これは比較的小さい単純な API です。スクリプト API の開始位置は、ScriptEngineManager クラスです。ScriptEngineManager オブジェクトは、jar ファイルサービス発見メカニズムを使用してスクリプトエンジンを発見できます。特定のスクリプト言語で記述されたスクリプトを解釈する ScriptEngine オブジェクトをインスタンス化することもできます。スクリプト API を使用するもっとも単純な方法は、次のとおりです。

  1. ScriptEngineManager オブジェクトを作成します。
  2. マネージャーから ScriptEngine オブジェクトを取得します。
  3. ScriptEngineeval メソッドを使用して、スクリプトを評価します。

ここで、いくつかのサンプルコードを見てみます。必須ではありませんが、これらの例を見ることは JavaScript の理解に役立つ場合があります。

top

「Hello, World」

ScriptEngineManager インスタンスから、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 から特定のスクリプト関数を呼び出す代わりに、スクリプト関数またはメソッドを使用して 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

JavaScript スクリプトエンジン

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 によってカバーされません。

JavaScript 実装の機能の変更

Rhino 実装のほとんどは含まれていますが、主に占有スペースとセキュリティー上の理由で、いくつかの Rhino のコンポーネントは含まれていないか、変更されています。これらの機能には、次のものが含まれます。
  1. JavaScript からバイトコードへのコンパイル。これは、「オプティマイザ」とも呼ばれます。セキュリティーマネージャーが存在する場合、オプティマイザは無効になります。セキュリティーマネージャーが使用されていない場合は、システムプロパティー「rhino.opt.level」を [-1, 9] の範囲で定義できます。デフォルトでは、この値は -1 に設定されます (オプティマイザが無効になることを意味する)。
  2. Rhino の JavaAdapter はオーバーライドされました。JavaAdapter は、JavaScript による Java クラスの拡張および JavaScript による Java インタフェースの実装を可能にする機能です。Rhino の JavaAdapter は、Oracle 独自の JavaAdapter の実装に置き換えられました。Oracle の実装では、JavaScript オブジェクトによって単一の Java インタフェースのみを実装できます。次の例は、想定したとおりに機能します。
           var v = new java.lang.Runnable() {
                        run: function() { print('hello'); }
                   }
           v.run();
    
    ほとんどの場合、JavaAdapter は Java anonymizer クラスのような構文を使用して単一のインタフェースを実装するために使用されます。Java クラスの拡張または複数のインタフェースの実装のために JavaAdapter を使用することは、非常にまれです。
  3. Rhino コマンド行ツール (Rhino シェル、デバッガなど) は含まれていません。
top

JavaScript から Java への通信

Java クラス、オブジェクト、およびメソッドへのアクセスは、ほとんどの場合簡単です。特に、JavaScript からフィールドおよびメソッドへのアクセスは、Java からの場合と同じです。ここでは、JavaScript から Java へのアクセスの重要な側面について説明します。詳細は、http://www.mozilla.org/rhino/scriptjava.html を参照してください。次の例は、Java にアクセスする JavaScript のコードの一部です。このセクションでは、JavaScript の知識が必要です。JavaScript ではなくほかの JSR-223 スクリプト言語を使用する場合、このセクションを飛ばして先に進んでください。

top

Java パッケージ、クラスのインポート

Java パッケージおよびクラスをインポートするために、組み込み関数 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.VectorPackages.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 オブジェクトの作成は 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 インタフェースの実装

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 を参照してください

top

独自のスクリプトエンジンの実装

JSR-223 準拠のスクリプトエンジンの実装について、詳細には説明しません。最低限、javax.script.ScriptEngine および javax.script.ScriptEngineFactory インタフェースを実装する必要があります。abstract クラス javax.script.AbstractScriptEngine は、ScriptEngine インタフェースのいくつかのメソッドに有用なデフォルトを提供します。

top

参照

top

Copyright © 1993, 2013, Oracle and/or its affiliates. All rights reserved.