Flex – Mate – BlazeDS – RemoteObjects

Fragestellung:

Aus den Flex Vorgaben geht hervor, dass <mx:RemoteObject> nur im Application Namensraum verfügbar ist, nicht aber direkt in Modulen, sondern von der Application dem Modul zur Verfügung gestellt werden. Ist das so korrekt? Welche Vorgehensweise ist an dieser Stelle empfohlen?

Antwort:

Kurze Antwort auf Ihre Frage :

Das ist teilweise korrekt.

Ausführliche Antwort:

Zur Kommunikation zwischen unseren Java- und Flex-Anwendungen wird das mate-flex-framework für den eventbasierten Umgang mit den RemoteObjects (auf der Flex-Seite) und BlazeDS zum Verarbeiten der eingehenden Anfragen (auf der Server-Seite) genutzt. Im Nachfolgenden wird auf die beiden genauer eingegangen und die Erklärungen um passende Code-Snippets ergänzt.

Das mate-flex-framework erleichtert den Umgang mit Events, indem es durch die strenge Vorgabe von Zuständigkeiten und einer klaren Trennung der Aufgaben und Akteuren das MVC-Paradigma umsetzt/verlangt. In der sogenannten EventMap, einer separaten MXML-Datei, werden geworfene Events aufgefangen, Folgehandlungen bestimmten Akteuren (Klassen + Methoden) zugewiesen und RemoteObject(s) definiert.

<?xml version="1.0" encoding="utf-8"?>
<EventMap    xmlns:mx="http://www.adobe.com/2006/mxml" xmlns="http://mate.asfusion.com/">
	<mx:Script>
		<![CDATA[importmx.events.FlexEvent;]]>
	</mx:Script>
	<!-- Native FlexEvents ___________________________________ -->
	<EventHandlers type="{FlexEvent.PREINITIALIZE}" >
	</EventHandlers>
    <EventHandlers type="{FlexEvent.APPLICATION_COMPLETE}" >
	</EventHandlers>
	<!—- Costum Application EventMappers _____________________ -->
	<EventHandlers type="{TypeOfASpecialEvent.INDIVIDUAL_NAME_TO_AVOID_NAMEBASED_CONFLICTS}">
		<RemoteObjectInvoker
			instance="{ nameOfTheRemoteObjectInstance }"
			method="nameOfTheConcreteJavaMethodWichShouldBeExecuted"
			arguments="{[maybeTheMethod, NeedsArguments]}">
			<resultHandlers>
				<MethodInvoker
					generator="{WhatClassShouldBeInstanciatedInCaseOfAPositiveResult}"
					method="nameOfaConcreteFlexMethodWhichShouldBeExecutedOnTheResultGeneratorClass"
					arguments="{maybeTheMethodNeedsArguments}" />
			</resultHandlers>
			<faultHandlers>
				<MethodInvoker
					generator="{WhatClassShouldBeInstanciatedInCaseOfANegativeResult}"
					method="nameOfaConcreteFlexMethodWhichShouldBeExecutedOnTheFaultGeneratorClass"
					arguments="{fault}" />
			</faultHandlers>
		</RemoteObjectInvoker>
	</EventHandlers>

	<!--here you add additional needed EventHandlers. -->

	<!—- Definition RemoteObjects ____________________________ -->

	<mx:RemoteObjectid="nameOfTheRemoteObjectInstance" destination="nameOfTheRemotingService" source="java.class.name.with.full.PackageName" showBusyCursor="true" />

</EventMap>

Das obige Beispiel der EventMap enthält einen EventHandler-Block. Dieser lauscht dem EventBus der Flex Anwendung und wird aktiv, sobald das Event TypeOfASpecialEvent.INDIVIDUAL_NAME_TO_AVOID_NAMEBASED_CONFLICTS geworfen wird. Dann schaut die EventMap nach, welche Aktion(en) durchzuführen sind. In diesem Fall soll eine Remote-Anfrage über ein gewisses RemoteObject (instance="{ nameOfTheRemoteObjectInstance }") durchgeführt werden. Dabei muss der vollständige Name der Java Klasse, sowie der auszuführenden Methode, und eventuell benötigte Parameter für diese Methode, bekannt sein. Service-Name (ID) (für BlazeDS) und vollqualifizierte Paketangabe (SOURCE) (com.nextlevel.producs.ClassNameForTheService) werden im RemoteObject selbst definiert.

Der Umgang und das Zusammenspiel mit der EventMap wird an der nachfolgenden Grafik deutlich:

Die EventMap rutscht in den Mittelpunkt der gesamten Aufgabendelegierung und wird somit zum Controller (siehe Model View Controller). Benötigte Parameter z.B. für Funktionsaufrufe werden als Instanz-Variablen des jeweiligen Event-Typs vor dem werfen (dispatchen) gesetzt und stehen somit in der EventMap zur Verfügung. Nachfolgend eine beispielhafte Event-Klasse (Wichtig: alle Eventklassen müssen von flash.events.Event erben):

package com.nextlevel.b2b.spammonitor.ui.events {
       import flash.events.Event;
       public class SpamManagerMailEvent extends Event {
             public static const GET_MAIL_BEAN_RANGE: String      = "getMailBeanRange_SpamManagerMailEvent";
             public static const DELETE_MAIL_NUMBER: String       = "deleteMailNumer_SpamManagerMailEvent";
             public var user: String;
             public var from: int;
             public var downTo: int;
             public function SpamManagerMailEvent(type: String, bubbles: Boolean=true, cancelable: Boolean=false) {
                    super(type, bubbles, cancelable);
             }
       }
}

Möchte man nun an einer Stelle ein solches SpamManagerMailEvent erstellen und anschließend werfen könnte das folgender maßen aussehen:

var myNewMailEvent: SpamManagerMailEvent = newSpamManagerMailEvent(SpamManagerMailEvent.GET_MAIL_BEAN_RANGE);
myNewMailEvent.from = 22;
myNewMailEvent.downTo = 15;

this.dispatchEvent( myNewMailEvent );

In der EventMap muss nun ein EventHandler-Block definiert sein, welcher auf ein Event vom Typ SpamManagerMailEvent.GET_MAIL_BEAN_RANGE horcht:

<EventHandlerstype="{SpamManagerMailEvent.GET_MAIL_BEAN_RANGE}">
    <RemoteObjectInvoker
        instance="{ nameOfTheRemoteObjectInstance }"
        method="nameOfTheConcreteJavaMethodWichShouldBeExecuted"
        arguments="{[event.from, event.downTo]}">
            <resultHandlers>
                <MethodInvoker
                    generator="{WhatClassShouldBeInstanciatedInCaseOfAPositiveResult}"
...

In der EventMap kann man nicht einfach auf die Instanz des aufgefangenen SpamManagerMailEvent.GET_MAIL_BEAN_RANGE-Events zugreifen – um zum Beispiel die gesetzten Instanz-Variablen irgendwo als Parameter (Argumente) zu übergeben. Um dennoch auf diese zugreifen zu können schreibt man einfach event.nameDerVariablen. In meinem Fall hießen diese from und downTo arguments="{[event.from, event.downTo]}">.

Aus den Beispielen der offiziellen mate-flex-framework-Seite kann man den nachfolgenden Projekt-Aufbau abstrahieren. So oder so ähnlich könnte ein Projekt strukturiert sein:

Abschließend muss die EventMap nur noch in das Modul oder die Anwendung eingebunden werden:

<?xml version="1.0" encoding="utf-8"?>
<mx:Modulexmlns:mx="http://www.adobe.com/2006/mxml" xmlns:maps="com.nextlevel.b2b.spammonitor.ui.maps.*" xmlns:views="com.nextlevel.b2b.spammonitor.ui.ui.views.*" creationComplete="creationCompleteHandler(event)">
    <mx:Script>
        <![CDATA[
            import com.nextlevel.b2b.spammonitor.ui.model.businesslogic.ReferenceHandler;
            import mx.events.FlexEvent;

                protected function creationCompleteHandler(event:FlexEvent): void {

                    // … do something
                }
        ]]>
    </mx:Script>

    <mx:Stylesource="style/yflexskin.css"/>
        <maps:MainEventMap/>    <!-- Hier wird die EventMap in das MODUL eingebunden -->
        <views:MainUI/>

Da man prinzipiell beliebig viele EventMaps haben kann gibt es auch kein Problem, wenn jedes Modul in einer Anwendung seine eigene (oder sogar mehrere eigene) hat.

Nun noch ein paar Hinweise zur Server-Seite mit BlazeDS:

Im Tomcat-Verzeichnis unter <Anwendung>/WEB-INF/flex gibt es eine remoting-config.xml. Dort muss der Service, welchen, das in der EventMap definierte, RemoteObject nutzen will, in folgender Form deklariert werden:

<service>
...
    <destination id="nameOfTheRemotingService ">
        <properties>
            <source>java.class.name.with.full.PackageName</source>
            <scope>scopeOfRequests (session,request,application etc.)</scope>
        </properties>
    </destination>
...
</service>

Der Scope ist optional. Wird kein Scope angegeben ist er per default auf request. Dadurch wird bei jeder Anfrage eine neue Instanz der Java-Klasse erstellt.

Natürlich muss auch darauf geachtet werden, dass der Web-Server die passende JAR in seinem classpath z.B. LIB-Verzeichnis hat. Sonst wird er zur Laufzeit eine eingehende Anfrage nicht an eine konkrete Instanziierung der geforderten Klasse übergeben können und mit einem „Ich kenne diese Klasse nicht“-Fehler abbrechen. Copy & Paste bei der Angabe der Namen (in EventMap, Events, RemoteObject und BlazeDS-Service) hat sich bewährt und vermeidet an dieser Stelle unnötige Fehler.

View Me   Edit Me