Aufgabenstellung
Unter Verwendung der FluorineFX Messaging-API soll eine Montagsmaler-Anwendung implementiert werden. Dies bedeutet dass ein User auf eine Leinwand zeichnet und die anderen Seitenbesucher in Echtzeit eine Aktualisierung auf dem Bildschirm erhalten. Dies kann auch als Grundlage für weitere Kollaborationsanwendungen genutzt werden, in denen man gemeinsam ein Dokument konstruieren möchte.
Ansatz
Nachdem die Zeichnenfunktionalität implementiert ist, werden die Bildpunkte beim loslassen der Maustaste (MOUSE_RELEASE_EVENT) an alle Clients übertragen. Die Clients reagieren bei einer einkommenden Menge an Bildpunkten und zeichnen sofort auf die Leinwand. Es wird immer die komplette Leinwand mit allen Bildpunkten beim Mouse-Release gesendet.
Durch das Publisher/Subscriber-Prinzip abonniert die Flex-Anwendung beim Start sofort die Datenquelle „painter„. Die Datenquelle erhält Bildpunkte und verteilt sie an alle Abonennten, wenn diese nach dem zeichnen die Maus gesehn lassen.
Vorraussetzungen
Die FluorineFX-Konfigurationsdatei wird angepasst, so das es eine neue Messaging Quelle „painter“ existiert.
Konfiguration des Backends
services-config.xml:
<?xml version="1.0" encoding="utf-8" ?> <services-config> <services> <service-include file-path="remoting-config.xml" /> <service-include file-path="messaging-config.xml" /> </services> <!-- Custom authentication --> <security> </security> <channels> <channel-definition id="my-amf" class="mx.messaging.channels.AMFChannel"> <endpoint uri="http://{server.name}:{server.port}/{context.root}/Gateway.aspx" class="flex.messaging.endpoints.AMFEndpoint"/> <properties> </properties> </channel-definition> <channel-definition id="my-rtmp" class="mx.messaging.channels.RTMPChannel"> <endpoint uri="rtmp://{server.name}:2037" class="flex.messaging.endpoints.RTMPEndpoint"/> <properties> <idle-timeout-minutes>20</idle-timeout-minutes> </properties> </channel-definition> </channels> </services-config>
messaging-config.xml:
<?xml version="1.0" encoding="UTF-8"?> <service id="message-service" class="flex.messaging.services.MessageService" messageTypes="flex.messaging.messages.AsyncMessage"> <!-- DO NOT CHANGE <adapters> SECTION--> <adapters> <adapter-definition id="messagingAdapter" class="FluorineFx.Messaging.Services.Messaging.MessagingAdapter" default="true"/> </adapters> <destination id="chat"> <adapter ref="messagingAdapter"/> <properties> <network> <session-timeout>0</session-timeout> </network> </properties> <channels> <channel ref="my-rtmp"/> </channels> </destination> <destination id="textchat"> <adapter ref="messagingAdapter"/> <properties> <network> <session-timeout>0</session-timeout> </network> </properties> <channels> <channel ref="my-rtmp"/> </channels> </destination> <destination id="painter"> <adapter ref="messagingAdapter"/> <properties> <network> <session-timeout>0</session-timeout> </network> </properties> <channels> <channel ref="my-rtmp"/> </channels> </destination> <destination id="opminboxrefresher"> <adapter ref="messagingAdapter"/> <properties> <network> <session-timeout>0</session-timeout> </network> </properties> <channels> <channel ref="my-rtmp"/> </channels> </destination> </service>
Konfiguration des Frontends
Bei den Frontend Konfigurationsdateien kann einfach die Datei services-config.xml und die Datei messaging-config.xml kopiert werden. Es können aber absolute Pfade anstelle der Platzhalter wie {server.name},{server.port},{context.root} genutzt werden. Hierdurch ist eine lokale Entwicklung der Flex-Anwendungen mit einem entfernten IIS Server möglich.
Lösung
PaintMessaging.mxml (Projektdatei)
<?xml version="1.0" encoding="utf-8"?> <mx:Application creationComplete="{init()}" xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" xmlns:components="components.*"> <mx:Script> <![CDATA[ import models.ModelLocator; import mx.controls.Alert; import mx.messaging.events.MessageFaultEvent; import mx.messaging.events.MessageEvent; import mx.messaging.messages.AsyncMessage; import mx.managers.CursorManager; [Bindable] [Embed(source="../assets/stiftklein.png")] private var stift:Class; public var cursorID:int; public function init():void { consumer.subscribe(); } public function mouseOverHandler(event:MouseEvent):void { } public function downEvent(event:MouseEvent):void { } public function upEvent(event:MouseEvent):void { } private function messageHandler(event:MessageEvent):void { ModelLocator.getInstance().lines=event.message.body as Array; dc_main.invalidateDisplayList(); } private function messagefaultHandler(event:MessageFaultEvent):void { Alert.show(event.faultCode+' '+event.faultDetail+' '+event.faultString); } ]]> </mx:Script> <mx:Consumer id="consumer" destination="painter" message="messageHandler(event)" fault="messagefaultHandler(event)"/> <mx:Producer id="producer" destination="painter" fault="messagefaultHandler(event)"/> <mx:Panel mouseOver="{ cursorID = CursorManager.setCursor(stift); }" mouseOut="{CursorManager.removeCursor(cursorID);}" layout="absolute" title="Montagsmaler by Björn Karpenstein" backgroundColor="#ffffff" borderThicknessBottom="10" left="20" right="20" top="20" bottom="20"> <components:DrawableCanvas id="dc_main" backgroundColor="#ffff80" backgroundAlpha="0" width="100%" height="100%" y="0" x="0"> </components:DrawableCanvas> </mx:Panel> </mx:Application>
DrawableCanvas.mxml
<?xml version="1.0" encoding="utf-8"?> <mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" width="400" height="300" creationComplete="init()"> <mx:Script> <![CDATA[ public var lineWidth:Number = 5; public var lineColour:uint = 1; public var lineAlpha:Number = 1; private var enabler:DrawingEnabler; private function init():void { enabler = new DrawingEnabler(this); } protected override function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void { super.updateDisplayList(unscaledWidth, unscaledHeight); if( enabler != null ) enabler.drawLines(graphics, lineWidth, lineColour, lineAlpha); } ]]> </mx:Script> </mx:Canvas>
DrawingEnabler.as
package components
{
import flash.display.Graphics;
import flash.events.MouseEvent;
import models.ModelLocator;
import mx.core.Application;
import mx.core.UIComponent;
import mx.messaging.messages.AsyncMessage;
public class DrawingEnabler
{
private var _target:UIComponent;
private var _curentLine:Array;
public function DrawingEnabler( target:UIComponent ):void
{
_target = target;
_target.addEventListener(MouseEvent.MOUSE_DOWN, downEvent);
_target.addEventListener(MouseEvent.MOUSE_UP, upEvent);
_target.addEventListener(MouseEvent.MOUSE_OUT, upEvent);
}
public function sendMessage(stream:Array):void
{
var message:AsyncMessage = new AsyncMessage();
message.body = stream;
Application.application.producer.send(message);
}
public function newLine():void
{
_curentLine = new Array();
ModelLocator.getInstance().lines.push( _curentLine );
if( ModelLocator.getInstance().lines.length > 10 )
ModelLocator.getInstance().lines.shift();
}
public function addPoint(xIN:int, yIN:int):void
{
_curentLine.push( { x:xIN, y:yIN } );
_target.invalidateDisplayList();
}
public function drawLines(graphics:Graphics,lw:Number,lc:uint,la:Number):void
{
graphics.clear();
graphics.lineStyle(lw,lc,la);
for( var j:int = 0; j