未分類

逆引きNashorn – やりたいことからNashornのサンプルコードを引く

機械校正ツールであるRedPen(ライブデモ)は、デフォルトで内蔵している沢山の校正ロジックに加え、ユーザーがカスタムロジックを実装することができます。
自分で実装するにはJavaでValidatorというクラスを拡張すれば良いのですが、間口を広げるためにJavaScriptでもロジックを実装できるようにしました。
RedPen自体はJavaで実装しており、JavaからV8などを呼び出すのは大変なのでJava8に内蔵されているJavaScriptエンジンであるNashornを使いました。

あちこちの情報をかき集めて工夫をしたので、ここにまとめておきます。
やりたいことと、実現するためのコードを逆引き的に並べています。実現方法は全部違うけれども、どのコードもHello Nashornと出力するだけです。

JavaScriptを呼び出す
JavaScriptの関数をJavaから呼び出す
JavaのstaticメソッドをJavaScriptから呼び出す
JavaScriptからJavaのオブジェクトを参照する(バインドする)
JavaScriptをコンパイルしてから呼び出す
JavaオブジェクトのメソッドをJavaScriptの標準関数のように呼び出す

簡単にビルド、実行できるようまとめたものはGitHubに置いてあります。(ASLと書いてあるけど、創造性を微塵も感じさせないコードなので好き勝手コピペしていただいても大丈夫かと)

JavaScriptを呼び出す
print関数を呼び出すだけのJavaScriptを実行します。getEngineByName()の引数はjsでもJavaScriptでもNashornでもok。
SimpleNashorn.java

public class SimpleNashorn {
public static void main(String... args) throws ScriptException {
ScriptEngineManager manager = new ScriptEngineManager();
// works also with "js" / "javascript"
ScriptEngine engine = manager.getEngineByName("nashorn");
// prints "Hello Nashorn"
engine.eval("print('Hello Nashorn');");
}
}

JavaScriptの関数をJavaから呼び出す
JavaScript内で定義してある関数をJavaコードから呼び出す。invokeFunctionの第一引数が関数名、第二引数は可変長引数で、第一引数で指定した関数に渡すパラメータを任意の個数渡せる。
InvokeJavaScriptFunctions.java

public class InvokeJavaScriptFunction {
public static void main(String... args) throws ScriptException, NoSuchMethodException {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("nashorn");
engine.eval("function hello(message){print(message);}");
Invocable invocable = (Invocable) engine;
// prints "Hello Nashorn"
invocable.invokeFunction("hello", "Hello Nashorn");
}
}

JavaのstaticメソッドをJavaScriptから呼び出す
Java.typeっていうNashorn固有の機能を使います。
InvokeJavaStaticMethod.java

public class InvokeJavaStaticMethod {
public static void main(String... args) throws ScriptException {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("nashorn");
// prints "Hello Nashorn"
engine.eval("Java.type('nashornsampler.InvokeJavaStaticMethod').hello('Hello Nashorn');");
}

public static void hello(String message) {
System.out.println(message);
}
}

JavaScriptからJavaのオブジェクトを参照する(バインドする)
JavaScriptとJavaで相互に作用のあるコードを書くときはこんな風に。
BindJavaObject.java

public class BindJavaObject {
public static void main(String... args) throws ScriptException {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("nashorn");
engine.put("myObj", new BindJavaObject());
// prints "Hello Nashorn"
engine.eval("myObj.hello();");
}

public void hello(){
System.out.println("Hello Nashorn");
}
}

JavaScriptをコンパイルしてから呼び出す
JavaScriptコードを一旦Javaのバイトコードにコンパイルしてから実行します。繰り返し呼び出す場合は高速。Java8u40以降であればコンパイル結果はキャッシュされるのでJVMを再起動しても速攻でコンパイル結果が得られるらしい。
InvokeCompiledJavaScriptFunction.java

public class InvokeCompiledJavaScriptFunction {
public static void main(String... args) throws ScriptException, NoSuchMethodException {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("nashorn");
CompiledScript compiledScript = ((Compilable) engine).compile("function hello(message){print(message);}");
compiledScript.eval();
Invocable invocable = (Invocable) engine;
// prints "Hello Nashorn"
invocable.invokeFunction("hello", "Hello Nashorn");
}
}

JavaオブジェクトのメソッドをJavaScriptの標準関数のように呼び出す
JavaオブジェクトメソッドをJavaScriptよりobj.method()ではなく、単にmethod()と呼び出したい場合はこんな風に。
(恐らく要Java8u40以降)
BindJavaMethod.java

public class BindJavaMethod {
public static void main(String... args) throws ScriptException {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("nashorn");
engine.put("toBeBound", new BindJavaMethod());
engine.eval("var hello = Function.prototype.bind.call(toBeBound.hello, toBeBound);");
// prints "Hello Nashorn"
engine.eval("hello('Hello Nashorn')");
}

public void hello(String message) {
System.out.println(message);
}
}

関連記事:
Heroku Buttonで簡単にHerokuにデプロイ #herokujp – #侍ズム
WebアプリケーションをIntelliJ IDEAからHerokuへデプロイする #herokujp #jbugj – #侍ズム
Java WebアプリケーションをHerokuへデプロイする #herokujp – #侍ズム
Getting Started with Java on Heroku | Heroku Dev Center
RedPen を公開しました (ベータバージョン) | Advanced Technology Lab
社内共有会で使用した RedPen 資料と進捗 | Advanced Technology Lab