Ruby と Java がひとつになった世界に足を踏み入れるにはひとつの命令が必要だ。
include Java
この命令を実行することで、あなたは Java のクラスをインスタンス化し、それらのもつメソッドを呼び出し、またそれらのクラスを拡張することすらできるようになる。あたかもそれらがごく普通の Ruby のオブジェクトであるかのように。だが、そこにはいくつかの微妙な違いがある。この記事では、それらのクラスをどのように扱えば、新しいアプリケーションをすばやく生み出し、イナズマのようなスピードでそれをユーザに提供できるのかを見ていく。
この記事は JRuby と Swing を使ったシンプルな ObjectSpace ブラウザを実装するサンプルアプリケーションをベースに進めていく。ObjectSpace はシステム内のすべてのオブジェクトにアクセスする手段を提供してくれる Ruby のモジュールだ。たとえば、存在するすべての文字列を表示するには次のようにする。
ObjectSpace.each_object(String) do |string|私の irb セッションでこれを実行すると約 28,000 件の文字列が表示される。Swing と JRuby を使えば、さまざまなクラスとそのインスタンス、そして利用可能なメソッドをグラフィカルなインタフェースで表示することができる。さらに、パラメータをもたないメソッドを右端のパネル上でクリックして実行することも可能だ。
puts string
end
JRuby の ObjectSpace サポートはランタイムのパフォーマンスに悪影響を与えるため、デフォルトで無効になっている。これを有効にする方法については後ほど紹介する。まずは実装の興味深い詳細部分をいくつかを指摘し、JRuby の Java との統合を利用し始めるためのヒントを提供したい。
Java との統合
スクリプト中で Java をインクルードすれば、既存の Java クラスのサブクラス化を始めることができる。単純に Java クラスの完全限定名を指定すれば OK だ。サンプルアプリケーションではメインウインドウは JFrame を拡張する。また、次のように javax.swing と java.awt パッケージをクラスのスコープにインクルードすることもできる。こうすれば毎回クラスを利用するたびにフルネームを指定する必要がなくなる。
class MainWindow < javax.swing.JFrame
include_package 'javax.swing'
include_package 'java.awt' ...
あるいは、include_class ファンクションを使ってクラスを個別にインクルードしてもよい。この方法なら、利用しないクラスのための定数で名前空間が汚染されることもない。
super によるコンストラクタ呼出は通常の Ruby コードと同じように動作する。つまり、このクラスの initialize メソッドの先頭行で super("JRuby Object Browser") という呼出を行えば、フレームのタイトルを設定することができる。
javax.swing パッケージ全体をクラスにインクルードしていれば、Java クラスのインスタンス化は次のとおり簡単だ。
list_panel = JPanel.new
list_panel.layout = GridLayout.new(0, 3)
二行目をよく見ると、JPanel の layout フィールドに直接アクセスしているように思うかもしれないが、そうではない。JRuby が Java のオブジェクトにいくつかの便利なメソッドを追加しているのである。だから、上述のコードは次のように見慣れた書き方をすることもできる。
list_panel.setLayout(GridLayout.new(0, 3))
ゲッターとセッターを使う代わりに、一見したところフィールドへ直接アクセスしているようなコードを書ける。コーディングのエクスペリエンス全体がより Ruby ライクに感じられるように JRuby が Java オブジェクトに追加しているシンタックスシュガーは他にもあり、すべてのメソッド用に実際に定義されているキャメルケースの名前の代わりにスネークケースの名前を使うことができる。これに従えば setLayout メソッドを呼び出す三番目の方法は次のようになる。
list_panel.set_layout(GridLayout.new(0, 3))
セッターメソッドをそれが定義されているクラス内部で呼び出すときは、Ruby がそれをローカル変数の作成であると勘違いしないように、明示的に self をレシーバとしてメソッドを呼び出す必要がある。
self.default_close_operation = WindowConstants::EXIT_ON_CLOSE
Java と Ruby の間にある別の違いは、前述のコード片にある EXIT_ON_CLOSE のような定数へのアクセスだ。コードを Java から Ruby に変換するときは . (ドット)によるアクセスを全部 :: に置き換えると覚えておこう。
ここまでは JRuby が Swing 開発にもたらした多くの変化はさほど革命的なものではないように見える。しかし私たちはまだ重要な側面をひとつ忘れている。イベントリスナだ。Java ではリスナとイベントを結びつける場合、無名クラスを使ってインタフェースを実装することが多い。
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
...
}
});
この方法だと、たくさんのリスナを追加する必要があるときにコードがごちゃごちゃしてしまう。JRuby を使えば、これを次のような二行のコードに切り詰めることができる( event 引数は必要がなければ省略することもできる)。
button.add_action_listener do |event|
...
end
以上が JRuby で Swing を使い始めるために知っておかなければならない基本事項である。JRuby が Swing を使った GUI 開発を快適にしてくれるとはいえ、やはり多くのコードを自分で書かなくてはならない。複雑なレイアウトを利用したいときは特にそうだ。Swing UI の生成をさらにシンプルにしたければ Three approaches to JRuby GUI APIs (参考記事・英語)を見てほしい。
JRuby アプリケーションのデプロイを簡単に
Ruby のアプリケーションやライブラリは通常 RubyGems を使って配布される。その恩恵を受けるにはあらかじめ Ruby と RubyGems がインストールされていなければならないが、平均的なエンドユーザの環境にはおそらくインストールされていない。この問題は、従来の( MRI / C-Ruby )プログラムを Ruby インタプリタといっしょにパッケージングしてくれる RubyScript2Exe [1] によってすでに解決済である。RubyScript2Exe は複数プラットフォームで動作する。だが JRuby ユーザが雨のなかに取り残されていると感じる必要はない。実際はまったく逆で、彼らはアプリケーションをすばやくデプロイするためのもっと強力なツールを手にしている。Java Web Start だ。
Java Web Start to the Rescue
Java Web Start は Java Runtime Environment に含まれているため、ほとんどのシステム上にインストールされている。Web Start 対応アプリケーションを作成するのは非常に簡単で、アプリケーションを構成する全ファイルがパッケージングされている Jar ファイルと JNLP( Java Network Launching Protocol )ディスクリプションファイルを作成するだけでよい。ObjectSpace ブラウザアプリケーションを実例として使い、Web Start 可能な Ruby アプリケーションを作成する過程を以下に示す。
Web Start に必須の要件は Jar ファイルにアプリケーションが含まれていることなので、まずはそこから見ていこう。JRuby は jruby-complete.jar と、機能を最小限に抑えた jruby.jar という二つの異なるライブラリを用意している。いずれも Ruby の標準ライブラリが全部バンドルされている。もし標準以外のライブラリを一切使わないのであれば、小さいほうの jruby.jar を使えばよい。そうすれば、大雑把に言って 1 MB ほどダウンロードサイズが削減できる。
自分で作成したスクリプトを実行するもっともシンプルな方法は .rb ファイルを jruby.jar に追加することだ。次のコマンドでは、今回のサンプルである rob.rb をアーカイブに追加している。
jar uf jruby.jar rob.rb
これがちゃんと動くかどうかを確認するには、アプリケーションを java で実行すればよい。このアプリケーションには ObjectSpace モジュールが必要なので、Java に jruby.objectspace.enabled=true というプロパティを指定してこれを有効にしなければならない。
java -Djruby.objectspace.enabled=true -jar jruby.jar -r rob
-r オプションでファイルを指定し、スクリプトを実行する。
事前コンパイル
JRuby 1.1 の新しいエキサイティングな機能のひとつは事前( Ahead of Time, AOT )コンパイルのサポートだ。現在の JRuby における JIT コンパイルではメソッド数が 2048 個までという制約(source)がある。AOT コンパイルはこの制約を和らげる助け(source)になる。JRuby のコンパイラである jrubyc はまだ開発途中なので、利用可能な最新の JRuby を使うのがよい。通常の Ruby ファイルをクラスファイルにコンパイルする方法は単純で、次に示すようにスクリプトを指定してコンパイラを実行するだけでよい。
jrubyc rob.rb
これで rob.class ファイルを含む ruby ディレクトリが作成される。 上でやったように ruby ディレクトリを jruby.jar に追加するかわりに、アプリケーションを格納する別の Jar ファイルを作成してみよう。やはり、既存の Jar ファイルに変更を加えるというのはきれいな解決方法ではない。Jar ファイルは jar という同名のツールを使って作成できる。
jar -cfe rob.jar ruby/rob.class rubyこれで rob.jar という小さなサイズの Jar ファイルが作成される。ruby/rob.class は main-class として Manifest ファイルで指定され、Jar に追加される。これで、実行にはクラスを指定するだけで他にはコマンドラインで何も指定する必要がなくなり、起動が単純になる。これを実行するには rob.jar がクラスパスに含まれている必要がある。
java -Djruby.objectspace.enabled=true -cp rob.jar:jruby.jar ruby.rob
Web Start
続けて JNLP ファイルを記述する前に、先ほど作成した Jar ファイルに署名をしなくてはならない。JRuby はリフレクションを使っておりパーミッションの拡大を必要とするので、この手続きは残念ながら不可欠だ。JRuby Wiki により詳しい情報が載っている。もっとも簡単な方法は次のように JDK 付属の keytool を使って自分でテスト用の証明書を作成することだ。
keytool -genkey -keystore myKeystore -alias myself
keytool -selfcert -alias myself -keystore myKeystore
これ以降、 Jar ファイルのうちのひとつを変更するたびに署名を更新しなければ、実行時に SecurityException がスローされる。
jarsigner -keystore myKeystore jruby.jar myself
jarsigner -keystore myKeystore rob.jar myself
Jar ファイルの準備ができたので、今度こそ JNLP ファイルを見ていこう。以下に示しているのがアプリケーションのための最小限の設定だ。 title、 vendor、 j2se といったタグや security セクションなど、一部のフィールドは仕様によって必須とされている。jar タグは Jar ファイルが最終的に置かれる場所を指している。file://- という URL を使えばローカルファイルを指定することもできる。これは開発中に便利だ。ObjectSpace プロパティはここでも設定する必要があり、これは property タグを使って行う。
Mirko Stocker
ひとつ上で説明した事前コンパイル( AOT )の項をスキップした場合や、Jar ファイルをひとつしか用いない方法を使いたい場合は、JNLP ファイルに -r オプションが含まれるように変更しなければならないので、application-desc は次のようになる。最後のステップは Jar と JNLP ファイルの指定した場所へのアップロードだ。それが終われば、ブラウザを使ってリンクを開くか、シェルから直接 javaws ツールを使えばアプリケーションを実行できる。
[...]
-r
rob
[...]
最後のステップは Jar と JNLP ファイルの指定した場所へのアップロードだ。それが終われば、ブラウザを使ってリンクを開くか、シェルから直接 javaws ツールを使えばアプリケーションを実行できる。
トラブルシューティング
ブラウザが Web Start を使ってアプリケーションを起動するためには、JNLP ファイルは application/x-java-jnlp-file という MIME タイプで配信されなければならない。もしブラウザが JNLP の内容を表示するだけで javaws を自動的に起動してくれないのであれば、Web サーバの設定を適切に行う必要がある。たとえば Apache なら mime.types 内に次のようなディレクティブが必要だ。
application/x-java-jnlp-file jnlp
さらなる情報源
JRuby に関する一般的な情報源としては JRuby Wiki が有用である。
Ruby Object Browser のコードはこちらから(source)ダウンロードできる。
原文はこちらです:http://www.infoq.com/articles/jruby-deployment-with-webstart