S2JMS開発記 S2JMS-Container の仕様を考える - メッセージのマッピング

さて、ここからが大変なところ。JMSのメッセージをどのようにメッセージハンドラにマッピングするかです。

これができると、S2JSFでアクションクラスに対してHTMLのフォームに入力した値が自動的にマッピングされるのと同じようなことができます。「HTMLフォームの入力内容」=「JMSのMessage」ですね。S2JSFの場合も、この機構のおかげでアクションクラスがHttpRequestを直接意識しなくて済んだわけです。

JMSの仕様をみると、Messageは以下の5種類にわかれます。

  1. BytesMessage
  2. MapMessage
  3. ObjectMessage
  4. StreamMessage
  5. TextMessage

簡単なところから行きましょう。

TextMessageのマッピング

TextMessage の場合、ペイロードはただのStringオブジェクトなので、たとえばメッセージハンドラにString型のプロパティが存在すればそこに自動的にセットすることが考えられます。String型のプロパティが複数存在する場合は、@Param アノテーションで明示的に指定します。

例えば、以下のようにString型のフィールド foo、bar が存在している場合、@Paramアノテーションが付加されている foo に TextMessage のペイロードがセットされます。

@Param
private String foo;

private String bar;

蛇足ですが、以下のように@PropertyアノテーションもつければPropertyInterTypeによってアクセサメソッドが不要になりますね。(^^;

@Param
@Property
protected String foo;

ObjectMessageのマッピング

ObjectMessageも簡単です。TextMessage と同じ考え方で、型が一致するプロパティが存在すれば、そのままセット。複数あれば@Paramアノテーションで明示的に指定します。とにかく、できるだけアノテーションを使わなくても済む規約重視の考え方で。

MapMessageのマッピング

さて、一番用途が多いと思われる、MapMessageの場合はどうすれば良いでしょうか。

MapMessage の場合、「名前」「値」のペアが文字通りMapに格納されています。この場合、メッセージハンドラ側に「名前」に相当するプロパティが存在した場合にセットしてあげれば良いでしょう。型の変換はJMSの仕様に従って「良きに計らい」ます。

例えば、

MapMessage mapMessage = session.createMapMessage();
mapMessage.setInt("age", 88);
mapMessage.setFloat("weight", 234);
mapMessage.setString("name", "Smith");
mapMessage.setObject("height", new Double(150.32));

と生成されていたら、ハンドラ側には次のようなフィールドがあれば良いわけです。

private Int age;
private Float weight;
private String name;
private Double height;

マッピングに失敗したときの振る舞い

さて、メッセージハンドラのonMessageメソッドが呼び出される際、メッセージハンドラが想定しているすべてのパラメータがセットされていれば良いのですが、不幸にしてセットされていない場合も考えられます。

  1. 受信したMessageが想定外だったり、MapMessageの中の要素が不足していた
  2. 型が違っていてマッピングできなかった

実は私が過去に開発に携わった非同期メッセージング指向のフレームワークでも同様の問題が頻発し、ユーザは受信したメッセージの妥当性をチェックするコードを大量に書かなければなりませんでした。

今回はこのようなことを防ぎたいので、メッセージハンドラ側のフィールドになんらかのアノテーションでパラメータの必須/オプション指定ができるようにしたいと考えています。
S2Container の bindingType 指定のような考え方ですね。

蛇足ですが、S2JSFのアクションクラスにもこれが欲しいと思ってます。S2JSFも、パラメータのバインドに失敗した場合、フィールドが null のままアクションが呼び出されるので、ヌルポの嵐(^^; なんど泣かされたことか。(その原因の1つが、アクセサメソッドの作り忘れだったので、PropertyInterTypeを作ったのですが)

長くなったので、今日はここまで。