2008-05-20

Springのスクリプト言語サポートを使って BlazeDSと JRubyを統合する

最初に言っておくが、Rubyといっても今回 Railsは使わない。

BlazeDSのオブジェクトファクトリとして Springを用いる方法については過去のエントリで触れたとおり。

これの延長で、Spring Frameworkがスクリプト言語の統合をサポートしている事を利用して Flex向けのサービスを Rubyで記述してしまおうという次第。

Java VM上で Rubyスクリプトを動作させるには JRubyというインタプリタを使用する。Springは JRubyの他に Groovyと BeanShellをサポートしているが、日本人にとっては Rubyが最も馴染み深いだろう。
(余談だが、宗教上の理由から国産のDIコンテナが Rubyをサポートすることは無さそうに見える。まったく不思議なことである)

Rubyクラスを Springの Beanとして登録する方法は比較的簡単だ。

  • JRuby(Spring 2.5で使用できる最新のバージョンは現在のところ 1.0.3)、の配布から jruby.jarを探し出してクラスパスへ追加する

  • 公開インターフェイスを Javaで書く。面倒だがどうしても必要なようだ。

  • Bean定義ファイル(Webアプリケーションの場合たいがい WEB-INF/applicationContext.xmlだろう)へ、普通の bean 要素のかわりに lang:jruby 要素で Beanを定義することで Rubyスクリプトをロードし、Rubyクラスをインスタンス化させる。


Rubyスクリプトを配置する方法は、Bean定義ファイルにインラインでそのまま書き込む方法と クラスパスの通った場所にスクリプトファイルを置く方法の二つある。後者の場合は変更を検出し自動でリロードさせることも可能なようだ。また、一貫して前者のスタイルでスクリプトを記述していけば「applicationContext.xmlの中に全てのビジネスロジックが集約されているアプリケーション」というおかしなものを作ることも可能である(有用性は特にない)。
いずれの方法をとるにせよ、スクリプト内に日本語で文字列リテラルなどを書き入れると化けてしまう。これを回避するには Spring側にパッチを当てる必要がある

具体例


いつも通り適当で申し訳ないが、文字列を引数に取って文字列を返す sayHelloメソッドを持つサービス HelloServiceを例にとって具体例を挙げてみる。まずは HelloServiceの公開インターフェイスを Javaで作成する。

package com.acme;

public interface HelloService {
public String sayHello(String yourName);
}

下記は、この Helloサービスを実装する Rubyクラスを "helloService" という名前のBeanとして定義ファイル内にインラインで記述する例である。

<lang:jruby id="helloService" script-interfaces="com.acme.HelloService">
<lang:inline-script>
<![CDATA[
class HelloService
def sayHello(yourName)
"Hello, " + yourName
end
end
HelloService.new
]]>

</lang:inline-script>
</lang:jruby>

最後に HelloServiceを newしているのは、このクラスのインスタンスが Springの管理すべき Beanのインスタンスであると明示するためである(スクリプト内には複数のクラス定義を書くことが出来るため)。

さて、この helloServiceに対して sayHelloメソッドの呼び出しをしてやれば挨拶が返ってきそうであることは何となく想像できる。しかし、Javaで実装を1行も書いていないばかりか、Bean定義ファイル内にコードらしきものをさらっと書き込んだだけであるのにも関わらず本当にこんなものが Flexクライアントから呼び出せるのだろうか?

答え:呼び出せる

このサービスを BlazeDS経由で Flexから呼び出せるように公開するには、普通に Javaで実装されたサービスに対して行うのと同様 WEB-INF/flex/services-config.xmlの中に destinationを登録する。Springをオブジェクトファクトリとして使うので、factoryに springを指定している。BlazeDSに Springを統合する方法については、このエントリの冒頭でリンクしている過去エントリを参照のこと。

<destination id="helloService">
<properties>
<factory>spring</factory>
<source>helloService</source>
</properties>
</destination>

このアプリケーションに jrubytestという名前を付けて実行させた。コンテキストルートは http://localhost:8080/jrubytest/ で、mx:RemoteObject向けの通信エンドポイントは http://localhost:8080/jrubytest/messagebroker/amf となる。これを起動したままにしてFlex側に移ろう。

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:RemoteObject id="helloService" destination="helloService"
endpoint=
"http://localhost:8080/jrubytest/messagebroker/amf"/>
<mx:VBox>
<mx:HBox width="100%">
<mx:Label text="お名前"/>
<mx:TextInput id="yourName"/>
<mx:Button label="送信">
<mx:click>
<![CDATA[
helloService.sayHello(yourName.text);
]]>

</mx:click>
</mx:Button>
</mx:HBox>
<mx:Label width="100%" text="{helloService.sayHello.lastResult}"/>
</mx:VBox>
</mx:Application>

Flex Builder上ではこのようなデザイン画面になる。


アプリケーションを起動し、テキスト入力欄に文字列を入力して送信したところ、Rubyで実装したとおりの実行結果を正しく得ることが出来た。



これで Flex - BlazeDS - Spring - JRubyの連携はとりあえず可能であるとわかった。
つまり、Railsを使わずとも Rubyで FlexからRPCが可能なビジネスオブジェクトを実装し JavaVM上で作動させることが出来るということだ(ひとまず)。

だが、文字列やプリミティブ型以外の引数や返り値を用いようとするといくらか JRubyや BlazeDSをハックする必要があった。詳細については下記エントリを参照されたし。
JRuby for BlazeDS その1
JRuby for BlazeDS その2

時間があれば、さらに ActiveRecordを組み合わせる方法についても書くようにしたい。なにも ActiveRecordは Railsだけのものではない。

追記


Springの JRubyサポートを使って JSFとJRubyを組み合わせている人を発見したので参考までに。
Creating JSF applications with JRuby and ActiveRecord-JDBC (Part 1)
Creating JSF applications with JRuby and ActiveRecord-JDBC (Part 2)

ラベル: ,

0 件のコメント:

コメントを投稿

<< ホーム