kagamihogeの日記

kagamihogeの日記です。

Flex 勉強に共有ホワイトボードつくってみた - ソースコード

Flex 勉強に共有ホワイトボードつくってみた - kagamihogeのblog の続き。

同期したりしなかったりする原因は結局よくわからず。データプッシュの使い方が激しく間違っている気がする……

前提条件

  • S2BlazeDS Java-Flex 間通信に使用。プロジェクト構成などはコレに従います。
  • yui-frameworks 色々と楽なので使う

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();
		}
	}
}

座標のやり取りの方法だけど、

  1. mx:RemoteObject でサーバに座標を送信
  2. サーバ側はその座標を 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>

を加えてみたが……状況改善せず。やっぱり根本的にデータプッシュの使い方が間違ってる気がするなー。