Nashorn スクリプトから外部 Java ライブラリを使うのは意外と面倒です。
Nashorn の基本的な使い方は以下を参照ください。
ここでは、Nashorn スクリプトから Ivy を使い、外部の Java ライブラリを使う方法を紹介します。
クラスパス の指定
Nashorn スクリプトで外部 Java ライブラリを使うには、jjs コマンドに -cp
オプションでクラスパスを設定します。
$ jjs script.js -cp external-lib.jar
Nashorn スクリプト内部で shebang で指定することもできます。
#!/usr/bin/jjs -cp external-lib.jar // ...
しかし外部ライブラリが複数必要な場合、jjs ではクラスパスのワイルドカードインポートに対応していなかったり(たぶん)、依存ライブラリの指定などはちょっと面倒です。
Ivy で依存ライブラリを取得する
以下のようにすれば URLClassLoader で Ivy をロードして実行することができます。
var URL = java.net.URL; var URLClassLoader = java.net.URLClassLoader; var URLArrayType = Java.type('java.net.URL[]'); var ivyJarUrl = new URL('https://repo1.maven.org/maven2/org/apache/ivy/ivy/2.4.0/ivy-2.4.0.jar'); var urls = new URLArrayType(1); urls[0] = ivyJar.toURI().toURL(); var ivyMain = new URLClassLoader(urls).loadClass("org.apache.ivy.Main")
URLClassLoader 経由で Ivy の jar を取得して loadClass() しているだけです。
Ivy は ant から使う意外に、直接 Main メソッドを実行することで、依存解決して必要な Jar を落としてくることができます。
今回は loadClass() でロードした Ivy の Main クラスを使って依存解決してみます。
Ivy の Main は以下のようになっています。
public static void main(String[] args) throws Exception { CommandLineParser parser = getParser(); try { run(parser, args); System.exit(0); } catch (ParseException ex) { System.err.println(ex.getMessage()); usage(parser, false); System.exit(1); } }
System.exit(0)
しちゃっているので、以下のように直接 run
メソッド呼んでやればコマンドラインで Ivy 使うのと同じことができそうです。
var ivyRunMethod = ivyMain.getDeclaredMethod('run', parser.class, StringArrayType.class); ivyRunMethod.accessible = true; var opts = ['-dependency']; opts = opts.concat(dependency.split(':')); opts = opts.concat(['-retrieve', 'lib/[artifact]-[revision](-[classifier]).[ext]']); var ivyArgs = new ObjectArrayType(2); ivyArgs[0] = parser; ivyArgs[1] = Java.to(opts, StringArrayType); ivyRunMethod.invoke(null, ivyArgs);
-retrieve
オプションで依存 Jar を lib
フォルダに格納します。
-dependency
で必要な依存を指定します。
あとは getDeclaredMethod()
で取得した run メソッドを起動するだけです。
依存 Jar が lib
フォルダに保存されるので、クラスパスに無理やり追加してしまいましょう。
var addUrlMethod = URLClassLoader.class.getDeclaredMethod('addURL', URL.class); addUrlMethod.accessible = true; for each (var file in new File('lib/').listFiles()) { if (file.isFile && file.name.endsWith('.jar') && !file.name.endsWith('-javadoc.jar') && !file.name.endsWith('-sources.jar')) { var addUrlArgs = new ObjectArrayType(1); addUrlArgs[0] = file.toURI().toURL(); addUrlMethod.invoke(java.lang.ClassLoader.getSystemClassLoader(), addUrlArgs); } }
SystemClassLoader の addURL
にリフレクションで直接追加しちゃってます。
これらを関数でくるんでおきましょう。
var Ivy = (function() { return { load : function(dependencies) { // ... } }; })();
結構いいかげんですが、スクリプトなので良しとして、ivy.js
とでもして保存します。
全体像は Github にあげておきましたので以下を御覧ください。
実行する
Nashorn スクリプトから外部スクリプト呼ぶには load()
関数を使います。
load("./ivy.js");
依存を指定して先程作成した ivy.js
の関数を呼びます。
commons-lang3
と guava
でも利用してみましょう。
Ivy.load([ 'org.apache.commons:commons-lang3:3.5', 'com.google.guava:guava:19.0' ]);
Java.type
でインポートして使います。
var StringUtils = Java.type('org.apache.commons.lang3.StringUtils'); print('[' + StringUtils.trim(' XX ') + ']'); var Strings = Java.type('com.google.common.base.Strings'); print('[' + Strings.repeat('*', 10) + ']');
出力は以下のようになります。
[XX] [**********]
commons-lang3
と guava
が Nashorn スクリプト で使えるようになりました。
まとめ
Nashorn スクリプトでも簡単にJavaの資産である外部ライブラリを使うことができました。
環境に依存せず、ちょっとした事は概ね Nashorn スクリプト でまかなえちゃいそうです。
Maven で似たようなスクリプトもありました。