2009年10月23日金曜日

FUSE ESB 3.4 で Drools を使ってみる

最初 FUSE ESB 4.1 でやってみたけど、servicemix-drools コンポーネントを含むサービスアセンブリを osgi:install すると、どうしても osgi:start でエラーが発生する。Drools がルールを構築しようとして NullPointerException を発生させているらしい。

同じ目にあった人が FUSE のコミュニティで質問しているが、どうやら解決には至らなかった模様(リンク)。

リモートデバッガを引っ掛けて見てみると、直接的には Drools の dialect が認識されていないのが原因のようだけど、要するにクラスローダが正しく jar に関連付けられていないらしい。だから例えば、dialect関連のプロパティを-D オプションなんかで無理やり認識させたとしても、次はその dialect に必要なクラスがロードできないと言う事になる。(細かく言うと、jar は drools-compiler-4.0.7.jar、dialect が記述されているファイルは、その jar 中の drools.default.packagebuilder.conf、必要なクラスはJavaDialectConfigurationなど)

その後もしばらく、無理やり ServiceMix の lib に drools 関連の jar を追加してみるなどいろいろ悪あがきしたけど、LinkageError が出たりしてなんだかグタグタになり始め、面倒くさいから中断。実は、FUSE ESB 4.x はまだ incubation フェーズということで、発展途上という風にアナウンスされていたりする。今後に期待。

ということで mature フェーズのFUSE 3.4 を使ってみる事にした。せっかく OSGi 環境が好きになりかけていたんだけどなあ…

■ 概要 以下のように読みこんだファイルをルールに従って仕分けさせてみる
  • file サービス は、フォルダ poller/に xml ファイルが置かれるのを、poller エンドポイントで監視する。
  • file サービス、は xml ファイルが置かれるとこれを読み込み、droolsサービスの dispatch エンドポイント に送る。
  • drools サービス は、<purchase>/<price> 要素が 10000未満ならば、file サービスの sender_low エンドポイントにルーティングし、10000以上ならば file サービスの sender_high エンドポイントにルーティングする。
  • file サービス は、sender_low エンドポイントで受け取ったメッセージをフォルダ low/に保存し、sender_high エンドポイントで受け取ったメッセージはフォルダ high/に置く。
  • file サービス は、poller_low エンドポイントで読み込んだ xml ファイルをフォルダから削除する。
上記動作を、以下のようなモジュール構成で実装してみる
  • trial1-file-su:file サービスを定義するサービスユニット
  • trial1-drools-su:drools サービスを定義するサービスユニット
  • trial1-sa:上の二つのサービスユニットを含むサービスアセンブリ
環境は以下のとおり
  • Windows XP SP3
  • eclipse 3.5
  • FUSE ESB 3.4.0.4
  • アーキタイプのバージョンは基本的に最新版
以下、各モジュールのレシピ。

■ trial1-file-su
  • servicemix-file-poller-service-unit アーキタイプで新規プロジェクト作成
  • xbeans.xml を以下のように編集
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns:file="http://servicemix.apache.org/file/1.0"
           xmlns:trial1="urn:net:yasuabe:fuse3"
           xmlns="http://www.springframework.org/schema/beans">
    
      <file:poller service="trial1:file"
                  endpoint="poller"
                  targetService="trial1:drools"
                  targetEndpoint="dispatch"
                  file="file:///c:/devel/tmp/200910fuse/xad/poller" />
      <file:sender service="trial1:file"
                  endpoint="sender_high"
                  directory="file:///c:/devel/tmp/200910fuse/xad/high" />
      <file:sender service="trial1:file"
                  endpoint="sender_low"
                  directory="file:///c:/devel/tmp/200910fuse/xad/low" />
    </beans>
    
■ trial1-drools-su
  • servicemix-drools-service-unit アーキタイプで新規プロジェクト作成
  • xbeans.xmlを以下のように編集
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns:drools="http://servicemix.apache.org/drools/1.0"
           xmlns:trial1="urn:net:yasuabe:fuse3"
           xmlns="http://www.springframework.org/schema/beans">
    
      <drools:endpoint service="trial1:drools"
                       endpoint="dispatch"
                       ruleBaseResource="classpath:router.drl"/>
    </beans>
    
  • router.drl を以下のように編集
    package org.apache.servicemix.drools
     
    import org.apache.servicemix.drools.model.Exchange;
    
    global org.apache.servicemix.drools.model.JbiHelper jbi;
     
    rule "cheap"
     when
      Exchange(status == Exchange.ACTIVE, inMsg : in != null )
      eval(inMsg.xpath("/purchase/price<10000"))
     then
      jbi.route("endpoint:urn:net:yasuabe:fuse3:file:sender_low");
      jbi.getLogger().info("cheap");
    end
    
    rule "expensive"
     when
      Exchange(status == Exchange.ACTIVE, inMsg : in != null )
      eval(inMsg.xpath("/purchase/price>=10000"))
     then
      jbi.route("endpoint:urn:net:yasuabe:fuse3:file:sender_high");
      jbi.getLogger().info("expensive");
    end
    
■ trial1-sa
  • servicemix-service-assembly アーキタイプで新規プロジェクト作成
  • pom.xmlを編集して以下のような dependencies にする
  •   <dependencies>    
        <dependency>
          <groupId>net.yasuabe.fuse3</groupId>
          <artifactId>trial1-drools-su</artifactId>
          <version>0.0.1-SNAPSHOT</version>
        </dependency>
        <dependency>
          <groupId>net.yasuabe.fuse3</groupId>
          <artifactId>trial1-file-su</artifactId>
          <version>0.0.1-SNAPSHOT</version>
        </dependency>
      </dependencies>
    
■ ビルドして動かしてみる
  • ビルドは Eclipse 上で [mvn install]した
  • デプロイは maven の jbi:projectDeploy ゴールを試したが、この質問(→リンク)と同様に Repository が見つからない現象が再現したので、意地を張るのは止めて hotdeploy を使ってデプロイすることにした。
  • で、次のように確認
    • 以下のようなファイルを poller に置いて、low に移動することを確認した。
      <?xml version="1.0" encoding="UTF-8"?>
      <message>
       <amount>9999</amount>
      </message>
      
    • 以下のようなファイルを poller に置いて、high に移動することを確認した。
      <?xml version="1.0" encoding="UTF-8"?>
      <message>
       <amount>10000</amount>
      </message>
      
    • どちらでもログが出力されることを確認した。
■ その他メモ
上記の試行はfile コンポーネントとdrools コンポーネントだけのものだけど、他にもいろいろな組み合わせを試したので、困った事・分かった事を書いておく。

・servicemix-drools コンポーネントの挙動が分かりにくい
Camel のルーティングの from() にタイマやファイル監視を指定、to() に Droolsサービスを指定するような設定で、Drools から返ってこないまま止まっている感じになる事があった。この場合、例えば タイマなら3秒に一回メッセージが来るはずが、二つ目以降のメッセージが流れて来ず、ファイル監視なら処理の終わりで削除されるはずのファイルがロックされたまま消されずに残っているといった状況。jbiHelper の answer()やroute()、あるいは fault()のようなメソッドを呼ばないと返らないのかも。

・JMS が大量にソケットを消費して、しまいにポート 61616(JMSのコネクション)につながらなくなる
(java.net.BindException) ここでレポートされている(→リンク)のと同様の現象。
試しに Spring で最大コネクション数1のコネクションプールを指定してみると、キューイングしたところで、最初のキューイングで JMS が固まっている。という事はプール不使用の場合は新たなコネクションを作り続けて限界に達しているという事か。だけど、そもそも何故そんなに何百回も接続しようとするのか分からない。
ちなみに上のリンクでは、結局 ActiveMQ と Camel を subversion のトランクから落としてリビルドしたようだけど、面倒だからパス。と言うわけで、上述のレシピのように、JMS の代わりにfile:sender を用いることにした。
いろいろ不可解な現象があったり情報が少なかったり、面倒くさいことがちょっと多かったけど、まあ手間がかかった分わかった事も多いので、良しとしよう。

終わり

0 件のコメント:

コメントを投稿