北海道苫小牧市出身の初老PGが書くブログ

永遠のプログラマを夢見る、苫小牧市出身のおじさんのちらしの裏

Rhino事始め - スクリプトを走らせる(2)

そろそろRhinoの本領発揮と言ったところ。


今回も前回の my_script.js を編集してみます。RhinoJavaScriptの構文を実行出来るのはもちろんのことですが、本当の強みは直接Javaのクラスを触れることにあります。

var out = java.lang.System.out;
var str = new java.lang.String("Hello from Java.");
out.println(str.replaceFirst("Java", "JavaScript"));

/*
[結果]
Hello from JavaScript.
*/

*1


おなじみのSystemクラスとStringクラスをJavaScriptから使っています。RhinoではJavaを呼ぶためのプロパティが二つ用意されています。その一つが"java"と言う名前のプロパティで、これはJavajavaパッケージにMapされています。このプロパティを経由すると、java.lang.Systemのような書き方で簡単にJavaのクラスに到達し、利用することができます。


なお、先ほどのスクリプトで一つだけ注意しておくと、JavaScriptのStringとjava.lang.Stringは違うと言うことです。JavaScriptのStringにはreplaceFirst()メソッドはありません。


さて、Javaを呼ぶためのもう一つのプロパティですが、それは"Packages"です。こいつは、Javaのルートパッケージにマップされています。前回作ったjstest.JSTestは、"Packages.jstest.JSTest"となります。また、"Packages.java"と"java"は同じものになります。

試しにjstestパッケージにJavaでクラスを作って、それを呼び出してみます。WriteLogクラスを作り、Logging API経由でログを吐くだけのメソッドを定義します。

package jstest;

import java.util.logging.Logger;

public class WriteLog {
	final static Logger logger = 
		Logger.getLogger(WriteLog.class.getName());
	
	public void log(String msg){
		logger.info(msg);
	}
}

こいつを呼ぶmy_script.jsは以下のような感じです。

var out = new Packages.jstest.WriteLog();
out.log("Hello from JavaScript.");

/*
[結果]
2006/11/13 23:30:47 jstest.WriteLog log
情報: Hello from JavaScript.
*/

なんとまあ、あっさり・・・。


最後に、import文を紹介して終わりにします。Javaではパッケージ文を何度も書くのが面倒な人のために、import文が使えますが、Rhinoでも似たような関数が用意されています。

var javaClasses = new JavaImporter(java.lang, Packages.jstest);

with(javaClasses){
      var str = new String("Hello from Java.");
      var out = System.out;
      var log = new WriteLog();
      out.println(str.replaceFirst("Java", "System"));
      log.log(str.replaceFirst("Java", "WriteLog"));
}


/*
[結果]
Hello from System.
2006/11/13 23:40:20 jstest.WriteLog log
情報: Hello from WriteLog.
*/

withはスコープを与える構文です。javaClassesスコープを作り、その中にJavaImporterで必要なパッケージをインポートさせています。*2 *3


次回は、Javaでメイン処理をやってJavaScriptはおまけ的に利用する場合の実装手法です。


以下、余談。


importに関しては、importPackageとimportClassと言う関数も用意されており、こちらを利用する例の方が多く見かけます。ただし、これを使うにはinitStandardObjects()でグローバルスコープを作っては駄目で、JSTest.javaを以下のように書いておく必要があります。

Scriptable global = new ImporterTopLevel(cx);

これで、上記の二つの関数が使えるようになります。*4


ただし、ここにはこうかかれています。

Previously such functionality was available only to embeddings that used org.mozilla.javascript.ImporterTopLevel class as the top level scope. The class provides additional importPackage() and importClass() global functions for scripts but their extensive usage has tendency to pollute the global name space with names of Java classes and prevents loaded classes from garbage collection.

*1:前回のJavaのコードを使うと戻り値を出力するのでundefinedと最後に表示されているが、それは省略している。

*2:ただし、java.langをインポートするのは、JavaScriptのクラスと定義が被ってしまうのであまり推奨されてません。

*3:でもって、実はwithも速度的に非推奨な機能だったりしますw

*4:ImporterTopLevelのコンストラクタでinitStandardObjects()を呼んでたりしますので、通常のJavaScriptのオブジェクトもこれで用意されます。