ひよっこ。

I want to…

AMF48が便利だったのでSpringFramework3経由でもっと楽してみた

Posted by hikaruworld : 2011 4月 25

先日のfxug@北陸in富山で、わっきーさんがAMF通信用の軽量ライブラリとして、
AMF48を作ったよという紹介をしてくれていました。
某BlazeDSとかに比べてずいぶんお手軽そうだったので、Spring経由でもっと楽しようと思って遊んでいました。
(ちゃんとJava5対応してて、色々BlazeDS側で対応されてない部分も対応してくれてるらしい)

なお、AMF48(えーえむえふふぉーてぃーえいと)の使い方は公式サイトを参照ください。

なぜSpring経由で?

SpringMVCを利用すると、簡単にデータのバインディングを行う事が出来ます。
つまりHTTPリクエストをBeanにバインドしたり、
Beanの内容をHTTPリクエストに変換してViewに送出する事が出来ます。

Springの便利な部分は、これらのバインディグを様々なものに変換する事が簡単に出来る事です。
Spring自身によって提供されている形式としては、JSONや、XML、RSS、それだけはなくPDFやExcelにも対応していたはずです。
これらの実装はHttpMessageConverterを拡張されてそのコンバータが提供されていますので、
これを拡張する形でAMFに対応したものを構築できるはずです。

AMF48は便利なのですが、自力でdecode/encodeする必要があってちょっぴりめんどいので、
その部分はSpringさんに任せてしまえという思惑。

HttpMessageConverterを拡張する

JSONのコンバータとして提供されている、MappingJacksonHttpMessageConverterが
同じような事をやっているので参考になります。

実装するのは、HttpMessageConverterを実装している抽象クラス、AbstractHttpMessageConverterです。
とりあえずソースをば。詳細はコメントを見てください。
なお、細かい例外処理などは省いていますのであしからず。

public class AMFHttpMessageConverter extends AbstractHttpMessageConverter<Object> {
	public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");

	public AMFHttpMessageConverter() {
		// 対応するメディアタイプを指定します。AMFの場合はapplicaiton/x-amf
		super(new MediaType("application", "x-amf", DEFAULT_CHARSET));
	}

	@Override
	public boolean canRead(Class<?> clazz, MediaType mediaType) {
		// 読み込み(AMF -> オブジェクト形式)が読み込み可能な場合trueを返します
		// とりあえずMediaTypeで判断。
		return checkMediaTypeAMF(mediaType);
	}

	@Override
	public boolean canWrite(Class<?> clazz, MediaType mediaType) {
		// 書き込み(オブジェクト -> AMF)が書き込み可能な場合trueを返します。
		// とりあえずMediaTypeで判断。
		return checkMediaTypeAMF(mediaType);
	}

	@Override
	protected boolean supports(Class<?> clazz) {
		// 利用禁止。canRead/canWriteを参照するため。
		throw new UnsupportedOperationException();
	}

	@Override
	protected Object readInternal(Class<?> clazz, HttpInputMessage inputMessage)
			throws IOException, HttpMessageNotReadableException {
		// AMF48を利用してdecode
		return AmfUtil.decode(inputMessage.getBody(), clazz);
	}

	@Override
	protected void writeInternal(Object o, HttpOutputMessage outputMessage)
			throws IOException, HttpMessageNotWritableException {
		// AMF48を利用してencode
		outputMessage.getBody().write(AmfUtil.encode(o));
	}

	private boolean checkMediaTypeAMF(MediaType mediaType) {
		return "application".equals(mediaType.getType())
				&& "x-amf".equals(mediaType.getSubtype());
	}
}

ちなみにこいつらは、org.springframework.web.bind.annotation.support.HandlerMethodInvoker
から呼び出されています。興味あればどうぞ。

AnnotationMethodHandlerAdapterの設定

作成したAMFHttpMessageConverterをDI上にロードします。
これらのConverterはAnnotationMethodHandlerAdapterで読み込まれるので、
このクラスのプロパティにインジェクションします。設定ファイルはこんな感じです。

<beans:bean id="annotationMethodHandlerAdapter" class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
  <beans:property name="messageConverters">
	<beans:array>
		<beans:bean class="com.wordpress.prepro.AMFHttpMessageConverter"></beans:bean>
	</beans:array>
  </beans:property>
</beans:bean>

なお、上記のソースではAMFHttpMessageConverterのみを設定していますが、
通常はコンストラクタで設定されている

this.messageConverters = new HttpMessageConverter[]{
		new ByteArrayHttpMessageConverter(),
		stringHttpMessageConverter,
		new SourceHttpMessageConverter(),
		new XmlAwareFormHttpMessageConverter()
};

あたりも設定しておかないときっと困るでしょう〜(通常のバインディング系が動かないはず)。
また、この設定は

<annotation-driven />

よりも先に設定する必要があります(最初これではまりましたorz…)。

Controllerへの設定

そして、本題。
実際に使う場合の話。Controllerに設定を埋め込みます。

クライアントからAMF通信を受け取る場合

@RequestBodyを使います。
@RequestBodyを使うと設定したオブジェクトに対して、HTTPリクエスト本体を渡します。
この渡されたHTTP情報を元に、対応するHttpMessageConverterが処理されます。
ソースはこんな感じになります。

// HttpリクエストがhelloCommandに変換されて設定されます
@RequestMapping(value="/amf", method=RequestMethod.POST, headers="Accept=application/x-amf")
public Result home(@RequestBody HelloCommand helloCommand) throws IOException {
	//....
}

サーバからAMF形式で送る場合

@ResponseBodyを使います。
@ResponseBodyを設定すると、オブジェクトをHttpレスポンスに変換する際にHttpMessagConverterによって処理されます。

@RequestMapping(value="/amf", method=RequestMethod.POST)
@ResponseBody
public Result home(@RequestBody HelloCommand helloCommand) throws IOException {
	Result result = new Result();
	result.message = "hoge";
	return result;
	// この後resultがHTTPレスポンスに変換されます。
}

おまけ

TODO 後でソースコードはlaunchpadにUpします。

以上です。
AMF48いいですね。使いやすい感じです。

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中

 
%d人のブロガーが「いいね」をつけました。