ひよっこ。

I want to…

Posts Tagged ‘amf’

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いいですね。使いやすい感じです。

Posted in program | タグ: , , , | Leave a Comment »

PyAMFを利用してみる

Posted by hikaruworld : 2009 9月 14

ちょっとやりたいことがあってPyAMFを試しています。
というわけで、とりあえずHelloWorldをやってみたのでそのメモです。
基本的にはExamples通りやればOKです。

というわけで、今回はGeneralのHelloWorldから。

PyAMFのインストール

手っ取り早くeasy_installで。できなければダウンロードしてきてsetup.py installでもいいでしょう。

easy_install pyamf

AMFの配備

とりあえず、このserver.pyをサーバに配備します。
当然ですが、このまま利用する場合は8000のportはあけておく必要があります。

なお、今回はサーバとクライアントが別環境で実行しているので、WSGIServerの設定をlocalhostからIPアドレスに変更しておきます(30行目辺り)。

httpd = simple_server.WSGIServer(
	('12.345.678.90', 8000),
	simple_server.WSGIRequestHandler,

あ、ファイルには権限設定を忘れずに。

これで準備が整ったのでserver.xmlを起動します

python server.py

起動するとこんなログが出力されてコンソールが待ち状態になります。

Running Hello World AMF gateway on http://localhost:8000

※localhost:8000になっているのはそこまで先ほど修正しなかっただけなので無視します。

これで、該当サーバのhttp://IPアドレス:8000/にAMFでアクセスすることが可能になります。

ためしに、HTTPのGETでアクセスしてみましょう。
するとこんな感じで起こられました

400 Bad Request

To access this PyAMF gateway you must use POST requests (GET received)

ま、言われたとおりですね。

クライアントからの接続確認

とりあえずFlashDevelopを使って、簡単にRemoteObjectで接続してみます。
(FlexBuilderを利用せずにFlashDevelopを利用するのは今手元にFlexBuilderがないだけですよ)

とりあえず、サンプルに従って(コピペとも言います)MXMLを作成します。
このままだと接続先が違って動かないので、RemoteObjectの接続先だけ変更します(6行目辺り)。

<mx:RemoteObject id="remoteObj" destination="echo" endpoint="http://12.345.678.90:8000">

あとは、起動してみます。

すると画面上にHelloWorldが表示されました
PyAMF_helloWorld

とりあえず動いたので今日は満足。ソースの内容は後日改めて。

以上ー。

TODO: あとで調べること。
SimpleServerとか
WSGIGatewayとか

Posted in program | タグ: , , , | Leave a Comment »