Flex 勉強に共有ホワイトボードつくってみた - kagamihogeのblog の続き。
同期したりしなかったりする原因は結局よくわからず。データプッシュの使い方が激しく間違っている気がする……
Flex クライアント側
main.mxml
<?xml version="1.0" encoding="utf-8"?> <yui:YuiApplication xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:yui="http://akabana.seasar.org/yui/mxml" xmlns:whiteboard.view="view.*" xmlns:whiteboard.dto="dto.*" xmlns:whiteboard.logic="logic.*" layout="vertical"> <whiteboard.logic:WhiteboardViewLogic id="whiteboardViewLogic" /> <whiteboard.view:WhiteboardView id="whiteboardView" x="10" y="10" /> </yui:YuiApplication>
ここは特に言うことなし。yui-frameworks については yui-frameworks さわってみた - kagamihogeのblog などを参照。
view/WhiteboardView.mxml
<?xml version="1.0" encoding="utf-8"?> <mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" width="400" height="366"> <mx:Script> <![CDATA[ import dto.WhiteboardDto; import mx.messaging.events.MessageFaultEvent; import mx.rpc.events.FaultEvent; import mx.messaging.events.MessageEvent; private function messageHandler(event:MessageEvent):void { trace("consumerMessageHandler" + event.message.body); var d:WhiteboardDto = event.message.body as WhiteboardDto; var g:Graphics = whiteBoard.graphics; g.beginFill(0xFFFFFF); g.drawRect(d.x, d.y, 2, 2); g.endFill(); } private function faultA(event:MessageFaultEvent):void { trace("faulatasdfasdf"); } ]]> </mx:Script> <mx:Consumer id="consumer" destination="sharedWhiteboard" fault="faultA(event)" message="messageHandler(event)" /> <mx:Canvas id="whiteBoard" x="10" y="10" width="380" height="280" borderStyle="solid"> </mx:Canvas> <mx:Button id="sub" x="10" y="298" label="ボタン"/> <mx:Button id="unsub" x="74" y="298" label="ボタン"/> <mx:Label x="10" y="328" text="subscribed:" width="75"/> <mx:Text id="subscribedStatus" x="93" y="328" width="105" text="{consumer.subscribed}"/> </mx:Canvas>
mx:Consumer id="consumer" が座標を受け取るヒトです。で、messageHandler は受け取った座標をポチッと描画。サーバとやりとるする DTO はデータプッシュの場合もリモート呼び出しのときと同じようにすればよいらしい。
dto/WhiteboardDto.as package dto { [RemoteClass(alias="sharedwhiteboard.dto.WhiteboardDto")] public class WhiteboardDto { public var x:int; public var y:int; public function WhiteboardDto() { } } }
サーバ側の Java クラスと RemoteClass を合わせること以外はこれといった特徴なし。
logic/WhiteboardViewLogic.as
package logic { import dto.WhiteboardDto; import flash.display.Graphics; import flash.events.MouseEvent; import mx.rpc.events.FaultEvent; import mx.rpc.events.ResultEvent; import mx.rpc.remoting.RemoteObject; import view.WhiteboardView; public class WhiteboardViewLogic { [View] public var whiteboardView:WhiteboardView; public var remote:RemoteObject; public function WhiteboardViewLogic() { remote = new RemoteObject("whiteboardService"); remote.addEventListener( FaultEvent.FAULT, faultHandler ); remote.draw.addEventListener( ResultEvent.RESULT, drawResult ); } private function faultHandler(event:FaultEvent):void { trace("fault remote object"); } private function drawResult(event:ResultEvent):void { trace("draw success"); } public function whiteBoardMouseDownHandler(event:MouseEvent):void { trace("mouseDown" + event.localX + ":" + event.localY); whiteBoardMouseMoveHandler(event); } public function whiteBoardMouseMoveHandler(event:MouseEvent):void { var g:Graphics = whiteboardView.whiteBoard.graphics; if (event.buttonDown) { trace("mouseMove" + event.localX + ":" + event.localY); g.beginFill(0xFFFFFF); g.drawRect(event.localX, event.localY, 2, 2); g.endFill(); var whiteboardDto:WhiteboardDto; whiteboardDto = new WhiteboardDto(); whiteboardDto.x = event.localX; whiteboardDto.y = event.localY; remote.draw(whiteboardDto); } } public function subClickHandler(event:MouseEvent):void { whiteboardView.consumer.subscribe(); } public function unsubClickHandler(event:MouseEvent):void { whiteboardView.consumer.unsubscribe(); } } }
座標のやり取りの方法だけど、
- mx:RemoteObject でサーバに座標を送信
- サーバ側はその座標を Consumer に配信
という形にしている。実のところ BlazeDS のサンプルのチャットみたく mx:Producer と mx:Consumer 使えばもっと楽にできる。が、Flex がリモート呼び出しをしてサーバ側の Java が何がしか処理したあと何がしかのデータ配信、をやってみよーってことでこんな形になってます。
コードの中身は……まぁ見たままといいますか。イベントハンドラでテキトーに処理してるだけです。
Java サーバ側
package sharedwhiteboard.dto; public class WhiteboardDto { public int x; public int y; }
Flex 側の dto/WhiteboardDto.as と対応する DTO クラス。座標保持してるだけのクセにアレなクラス名なのはスルーで……
package sharedwhiteboard.service; import org.seasar.framework.container.annotation.tiger.Component; import org.seasar.framework.container.annotation.tiger.InstanceType; import sharedwhiteboard.dto.WhiteboardDto; import flex.messaging.MessageBroker; import flex.messaging.messages.AsyncMessage; import flex.messaging.util.UUIDUtils; @Component(instance = InstanceType.SINGLETON) public class WhiteboardService { public void draw(WhiteboardDto whiteboardDto) { System.out.println(whiteboardDto.x + ":" + whiteboardDto.y); MessageBroker msgBroker = MessageBroker.getMessageBroker(null); String clientID = UUIDUtils.createUUID(); AsyncMessage msg = new AsyncMessage(); msg.setDestination("sharedWhiteboard"); msg.setClientId(clientID); msg.setMessageId(UUIDUtils.createUUID()); msg.setTimestamp(System.currentTimeMillis()); msg.setBody(whiteboardDto); msgBroker.routeMessageToService(msg, null); System.out.println(whiteboardDto.x + ":" + whiteboardDto.y); } }
Flex から座標受け取って mx:Consumer に配信するサービスを定義したクラス。logic/WhiteboardViewLogic.as#whiteBoardMouseMoveHandler で remote.draw(whiteboardDto) したときに呼びだれるのがこのクラスの draw メソッド。例によってメソッド名がアレですね、すいません。
メソッドの中身は flex.samples.service.FeedService のパクリ。msg.setDestination("sharedWhiteboard") ぐらいかな? 違うのは。view/WhiteboardView.mxml の
<destination id="sharedWhiteboard"/>
以上。あとは env.txt を product にすることで Cool Deploy にしないとデータプッシュが動かないらしいので変更。これで完成。かなり不完全だけど……
エラーについて
サーバ側にときたまこんなエラーが出る。
[BlazeDS] [ERROR] Endpoint with id 'my-streaming-amf' cannot grant streaming connection to FlexClient with id 'C0F32033-EB1F-A0F7-0B9D-9F48923FAC7E' because max-streaming-connections-per-session limit of '1' has been reached.
読んで字の如くなエラーなわけですが。BlazeDS Developer Guide に色々書いてある。なので services-config.xml の id="my-streaming-amf" に、
<properties> <user-agent-settings> <user-agent match-on="MSIE" kickstart-bytes="2048" max-streaming-connections-per-session="3"/> <user-agent match-on="Firefox" kickstart-bytes="2048" max-streaming-connections-per-session="3"/> </user-agent-settings> </properties>
を加えてみたが……状況改善せず。やっぱり根本的にデータプッシュの使い方が間違ってる気がするなー。