JSR 223 JRuby script engineを使うサンプルコード
JRubyを使うと、JavaのインタフェースをRubyで実装できるようになります。これをJDK1.6で導入されたJSR 223 Scripting APIを使うと、こんなふうにプログラミングできます。
まずは、Javaのインターフェースを定義します。
package lycoris; public interface Greetings { public void hello(); public void bye(String to); }
これをJRubyで実装するには、こんなふうにします。
#greetings.rb require 'java' class GreetingsImple import 'lycoris.Greetings' def hello puts "What's up?" end def bye(to) print "See you, ", to, "\n" end end greetings = GreetingsImple.new greetings.hello greetings.bye("Scarecrow") greetings
import文がJavaでいうimplementsにあたります。最後の行でインスタンスgreetingsを返していますが、これがないとJRuby engine側でGreetingsインタフェースを取得できないので忘れないようにします。
Scripting APIを使ってGreetingsインタフェースのメソッドを実行するにはこんなプログラムを書きます。
package lycoris; import java.io.FileNotFoundException; import java.io.FileReader; import java.util.logging.Level; import java.util.logging.Logger; import javax.script.Invocable; import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; import javax.script.ScriptException; public class InvocableSample { public static void main(String argv) { try { //JRuby用エンジンを取得 ScriptEngineManager manager = new ScriptEngineManager(); ScriptEngine engine = manager.getEngineByName("jruby"); //JRubyのスクリプトを読み込んで一度evalしておきます。 //Invocableインタフェースを使うにはあらかじめevalされていることが前提です。 String testname = "/home/yoko/NetBeansProjects/Learning/lib/greetings.rb"; //ScriptEnfine.FILENAMEでファイル名を指定するとスクリプトがファイルとして扱われます。 //指定しなくても問題ありませんが、大きいスクリプトのときは指定するといいです。 engine.put("ScriptEngine.FILENAME", testname); //スクリプトからインスタンスが返ってくる Object obj = engine.eval(new FileReader(testname)); //Invocableインタフェースを取得 Invocable invocable = (Invocable)engine; //Greetingsインタフェースで定義した二つのメソッドを名前を指定して実行します。 invocable.invokeMethod(obj, "hello", new Object{}); invocable.invokeMethod(obj, "bye", new Object[]{"Santa Claus"}); //evalしたときに返されたインスタンスからGreetingsインタフェースを取得します。 Greetings greetings = invocable.getInterface(obj, Greetings.class); //Greetingsインタフェースで定義した二つのメソッドを実行します。 greetings.hello(); greetings.bye("Witch"); } catch (NoSuchMethodException ex) { Logger.getLogger(InvocableSample.class.getName()).log(Level.SEVERE, null, ex); } catch (FileNotFoundException ex) { Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex); } catch (ScriptException ex) { Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex); } finally { } } }
このプログラムを実行すると
What's up?
See you, Scarecrow
What's up?
See you, Santa Claus
What's up?
See you, Witch
のように出力されます。一つめのメッセージの組はスクリプトをevalしたときに出力されたものです。
このサンプルで使っているのはjruby 1.0.2のjruby.jarとhttps://scripting.dev.java.net/のCVSから取得できるバージョンのJRuby engineです。このサンプルでは日本語を使っていませんが、日本語のメッセージはputsする限りは大丈夫だと思います。printは文字化けすることがありますが、これはJRuby側の問題で、jruby 1.1b1なら文字化けしません。ただし、JRuby engineは1.0.xで動くバージョンしかCVSに置いていないので。。。(手元にはjruby 1.1版JRuby engineがあるので、近いうちになんとかしたいと思います。)
JavaのインタフェースをRubyで実装する例として
#runnable.rb require 'java' class RunnableImple import java.lang.Runnable def run puts "Hi, everyone." end end java.lang.Thread.new(RunnableImple.new).start RunnableImple.new
のようなものをあちらこちらで見かけます。これをScripting APIで
String testname = "/home/yoko/NetBeansProjects/Learning/lib/runnable.rb"; engine.put("ScriptEngine.FILENAME", testname); Object obj = engine.eval(new FileReader(testname)); Runnable runnable = invocable.getInterface(obj, Runnable.class); runnable.run();
のようにしてrunメソッドを実行できるのですが、これもjruby 1.1b1じゃないと動きませんでした。そもそも、runnable.rbが1.0.2じゃ動きません。