2010年4月19日月曜日

作業ログ:BlazeDSのRemotingServiceでJavaのenumを扱う方法

ActionScrip側からAMFで送られた文字列をBlazeDS4側でenumに変換しようとしたところ、
No enum const class 
と怒られてしまい、うまくいきませんでした。

flex 側で IExternalizable を実装して String を投げるようにしてみたのですが、そうすると今度は BlazeDS 側で配列に変更しようとしてエラーで落ちました。

方法が間違っていたのかもしれませんが、どうも解決法が見つけられなかったので、とりあえずはワークアラウンドで対応することにしました。

(´・ω・`) 正規の方法を教えてください、、、

4.21.2010追記:正規の方法がありました。詳細はこちらを参照のこと。


workaround

  • ActionScript 側の Enum (のような)実装では、内部に必ず Enum.valueOf() メソッドの第二引数に対応する文字列を持つpublicなインスタンス変数(or getter/setter)を配置する。
  • ActionScript 側の Enum (のような)実装では、必ず public var name:String を定義し、java 側の Enum#getName() と対応させる。
  • それ以外の変数(or getter/setter)を配置してもよいが、すべてメタタグ [Transient] をつける。
  • BlazeDS4のソースにパッチを当てる。
AMFから受け取った情報が、値がString型のエントリを一つだけ持つASObjectになればOKです。
この方法だと、副作用として flex 側で IExternalizable を実装する必要がないのでラクチンです。


パッチ

対象のソースコードは、flex.messaging.io.amf.translator.decoder.EnumDecoder クラスです。

元のコードは以下のようになっています。

55: Enum value = Enum.valueOf(desiredClass, encodedObject.toString());

encodedObject の型が flex.messaging.io.amf.ASObject 型なので、encodedObject.toString() の値は以下のようなフォーマットの文字列に変換されます。

ASObject(33469162){name=HOGE}

上記のワークアラウンドを適用すると、ASObjectはひとつだけエントリを持つマップとなるはずなので、そのエントリの値を使えば Enum が復元できます。

以下が変更内容です。ASObject以外が来る場合もあるかもしれないので(まだそこまでソースを追ってません)一応、オリジナルのコードも残しておきます。

final Enum value;
if (ASObject.class.isInstance(encodedObject)) {
 // patch for workaround
 value = Enum.valueOf(desiredClass, ASObject.class.cast(encodedObject).values().iterator().next().toString());
} else {
 // original
 value = Enum.valueOf(desiredClass, encodedObject.toString());
}


ActionScript 側のEnum(のようなもの)の例

public final class Hoge {
 public var name:String;
 public function Hoge(name:String) { this.name = name; }
 public static const HOGETAROU:Hoge = new Hoge("HOGETAROU");
 public static const HOGEJIROU:Hoge = new Hoge("HOGEJIROU");
 public static const HOGENOSUKE:Hoge = new Hoge("HOGENOSUKE");
}

0 件のコメント:

コメントを投稿