JDKのJavascript(nashron)で外部のJavaクラスを呼び出す方法

自分が作ったクラスや外部のライブラリを呼び出して、
機能を作りたい時があります。
普通にJavaで作ってもいいですが、
コンパイルが必要ですし、
早く作りたいのにJavaの厳格な文法がちょっと面倒です。

ScalaやGroovyなどを使って
軽くスクリプトを作るのもいいですが、
JDKに標準で入っていないから、やはり面倒。
それでJDK(バージョン8)に標準で入っているJavascriptエンジンnashronを
使ってみました。

しかし、nashronはなかなか外部クラスを認識してくれませんでした。
jrunscriptコマンド(またはjjsコマンド)の実行時に
classpathや-cpにクラスパスをjavaコマンドと同じ方法で指定したのにも関わらず、
いつもClassNotFoundExceptionが発生して困っていました。

色々と試した結果、やっと外部のJavaクラスを認識してくれたので、その方法をメモします。

まず、jrunscriptの実行時に以下のようにクラスパスを指定します。
Linux(Ubuntu)を基準に説明します。

例えば、/dukelab/libに10個のjarファイルがあって、それらをjrunscriptにクラスパスとして通したいなら、以下のように起動します。

jrunscript -cp `echo /dukelab/lib/* | tr ' ' ':'` 

ちょっと複雑で、これ以外の方法もあると思いますが、
今のところ、これで行けました。

単純にjrunscript -cp /dukelab/lib/*とすると、うまく行きません。
Linuxのシェルで"/dukelab/lib/*"が式として評価されて、
jrunscript -cp /dukelab/lib/a.jar /dukelab/lib/b.jar... のように
展開されて実行されます。
コマンドプロンプトでは空白文字で引数を分離していますから、
/dukelab/lib/b.jarがスクリプトファイルとして認識され、jrunscriptが実行されます。
当然/dukelab/lib/b.jarはスクリプトと関係ないバイナリのファイルですから、エラーになってしまいます。
それで上記の`echo /dukelab/lib/* | tr ' ' ':'`のように/dukelab/lib/*の展開後に、
trコマンドで空白文字をクラスパスの区切り文字である:に変換する処理を追加しました。
こうすると、実際には

jrunscript -cp /dukelab/lib/a.jar:/dukelab/lib/b.jar...

のようになるので、クラスパスとしてうまく認識できるようになります。

もし、jarファイルでなく、classファイルが置かれているディレクトリなら、もっと簡単です。
例えば、/dukelab/classes配下にJavaクラスがあるなら、
以下のように指定します。

jrunscript -cp /dukelab/classes

クラスパスを複数指定する時は:(コロン。Windowsでは;(セミコロン))で区切って下さい。

これで準備が終わり、Javascriptの話になりますが、
dukelab.js.test1パッケージにあるTestOneクラスと
dukelab.js.test2パッケージにあるTestTwoクラスをJavascriptで呼び出してみます。
2つのクラスはSystem.out.printlnを呼び出すごく単純なクラスなので、内容は割愛します。

var pkgs = new JavaImporter( 
    Packages.dukelab.js.test1, 
    Packages.dukelab.js.test2
);
with(pkgs) {
    print("TestOne : " + TestOne.getString());
    print("TestTwo : " + TestTwo.getInt());
}

実行すると、以下のように表示されます。

TestOne : wow!
TestTwo : great!

つまりいうと、Packagesオブジェクトの次に使うJavaクラスの完全修飾名を指定して呼び出すということです。
ただ、いつもPackages.dukelab.js.test1.TestOne.getString()...ように呼び出すのは辛いから
上記のようにwith文を使ってパッケージを省略して使うのがいいでしょう。
with文はJavascriptではスコープが曖昧になってしまうため、使わない方がいいと言われていますが、
JavascriptJavaクラスを使う時は便宜上、必須ではないかと思います。
ということで少なくともJavaの中のJavascriptでは、with文は当分健在だろうと勝手に思っています。
そのうち、新しい文法ができるかもしれませんが...

最後にJavascriptにおけるJavaクラスの呼出方法をまとめておきます。

// 完全修飾名で呼出し
print(Packages.dukelab.js.test1.TestOne.getString());

// with文でパッケージ(一つ)を省略
with (Packages.dukelab.js.test1) { 
    print(TestOne.getString());
}

// with文でパッケージ(一つ以上)を省略
with (new JavaImporter(Packages.dukelab.js.test1, Packages.dukelab.js.test2)) { 
    print(TestOne.getString());
    print(TestTwo.getInt());
}