Thursday, July 24, 2008

Building Flex Applications with JSPs

Problem Summary

Flex applications are client side applications which must communicate over the network to retrieve data from and store data in a database. There are many ways to communicate between the client and the server. If you are using Java you may already have JSPs which allow your users to view and modify data. How can you put a Flex UI front-end on top of your existing JSP based infrastructure?

Solution Summary

Flex can make requests to your existing JSPs using the HTTPService object. These requests can work with any JSPs. A Flex application contains all of the view logic so the JSPs should not return HTML but rather just serialized data.

Explanation

You might have a JSP similar to this one:

<%@page import="flex.samples.product.ProductService,
                                flex.samples.product.Product,
                                java.util.List"%>
<html>
<body>
<table>
  <tr>
    <th>Name</th>
    <th>Description</th>
    <th>Price</th>
    <th>Image</th>
    <th>Category</th>
    <th>Quantity</th>
  </tr>
<%
        ProductService srv = new ProductService();
        List list = null;
        list = srv.getProducts();
        Product product;
        for (int i=0; i<list.size(); i++)
        {
                product = (Product) list.get(i);
%>    
    <tr>
        <td><%= product.getName() %></td>
        <td><%= product.getDescription() %></td>
        <td><%= product.getPrice() %></td>
        <td><%= product.getImage() %></td>
        <td><%= product.getCategory() %></td>
        <td><%= product.getQtyInStock() %></td>
    </tr>
<%
        }
%>
</table>
</body>
</html>

This JSP just displays a simple HTML table of data which it fetched from the database.  If you have a Flex application and want to display the same data in a tabular format you may just use a DataGrid in your mxml application.  To get the data into your DataGrid you will need to modify the JSP so that it outputs serialized data rather than HTML:

<%@page import="flex.samples.product.ProductService,
                                flex.samples.product.Product,
                                java.util.List"%>
<?xml version="1.0" encoding="utf-8"?>
<catalog>
<%
        ProductService srv = new ProductService();
        List list = null;
        list = srv.getProducts();
        Product product;
        for (int i=0; i<list.size(); i++)
        {
                product = (Product) list.get(i);
%>    
    <product productId="<%= product.getProductId()%>">
        <name><%= product.getName() %></name>
        <description><%= product.getDescription() %></description>
        <price><%= product.getPrice() %></price>
        <image><%= product.getImage() %></image>
        <category><%= product.getCategory() %></category>
        <qtyInStock><%= product.getQtyInStock() %></qtyInStock>
    </product>
<%
        }
%>
</catalog>

Since this JSP now outputs XML serialized data, Flex will be able to parse that data and turn it into objects which can then be displayed in the DataGrid.  The entire Flex application to request the data and display it in a DataGrid could be as simple as:


<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" creationComplete="srv.send()">
    <mx:HTTPService id="srv" url="catalog.jsp"/>
    <mx:DataGrid dataProvider="{srv.lastResult.catalog.product}"/>
</mx:Application>

The HTTPService tag instantiates an object which will make the request to the JSP and deserialize the results into object.  The url property on the HTTPService should point to the JSP which returns the XML data.  The DataGrid tag instanciates an object which will display objects in a tabular format.  The dataProvider property on the DataGrid tells the DataGrid which data to display.  In this case the value uses data binding.  A binding expression goes between the curly braces.  The data binding tells the DataGrid to watch the specified object for changes and when changes occur the DataGrid will refresh it's view of the data.  In this case the dataProvider is set to bind to the lastResult property on the HTTPService object (srv is the referencable identifier of the HTTPService).  The lastResult object contains an object named "catalog" which corresponds to the catalog node of the XML which is returned from the JSP.  On the catalog node there is another array of nodes in the XML called "product".  Thus the expression "srv.lastResult.catalog.product" corresponds to the array of product which are returned from the HTTPService request to the JSP.  The finial piece of logic in the code is an event handler which causes the Flex application to make a request to the JSP.  The "creationComplete" event handler is triggered when the Flex application has fully initialized.  When this event is triggered the Flex application makes a request to the JSP by calling the send method on the HTTPService.

Remote Object Service in Flex

» Der Remote Object Service in Flex

Was unterscheidet den Remote Object Service vom HTTPService oder dem WebService?
Wie auch der HTTPService und der WebService greift der Remote Object Service über das HTTP Protokoll auf die Ressourcen des Servers zu. Im Gegensatz zu den erst genannten Services benutzt der RemoteObject Service aber nicht das auf Text basierende XML Format für den Informationsaustausch, sondern das proprietäre AMF (Action Message Format). Dieses codiert die auszutauschenden Informationen binär, die gesendete Nachricht wird dadurch wesentlich kleiner und kann schneller übertragen werden.
Java Entwickler mag diese Technik an die RMI API erinnern und der Vergleich ist durchaus angebracht. RMI nutzt jedoch statt des HTTP Protokolls (Port 80) ein eigenes Übertragungsprotokoll, scheitert damit an den meisten Firewalls und macht deshalb nur in firmeninternen (oder anderen geschlossenen) Umgebungen Sinn. Im Gegensatz dazu kann der Remote Object Service ohne Einschränkungen auch im Internet verwendet werden.

darstellung des RemoteObject Service mit dem Flex Data Service


Das POJO auf dem Tomcat
Um den Remote Object Service zu verwenden, muss zunächst ein Service Objekt in Java implementiert werden, welches die auf dem Client benötigten Methoden zur Verfügung stellt. Die Methoden, die der Client später verwenden soll, müssen als public deklariert werden. Die Klasse muss sich zudem im CLASSPATH des Flex Data Service befinden und einen leeren Konstruktor haben. Eine simple Testanwendung könnte wie folgt aussehen:

 Die Service Klasse auf dem Tomcat


public class RemoteObjectService {

public RemoteObjectService() {}

public String sayHello() {
return "Hallo Flexwelt";
}
}

Bekanntmachen der Klasse
Damit der Flex Data Service die erzeugte Klasse als RemoteObject erkennt, muss diese in der remote-config.xml bekannt gemacht werden. Die remote-config.xml befindet sich, genau wie die proxy-config.xml, im Verzeichnis WEB-INF/flex unterhalb des Context-Root.
Für die erzeugte Service Klasse wird eine neue Destination angelegt. Diese bekommt eine eindeutige Id, die später in der Flex Anwendung zur Referenzierung des entfernten Objektes gebraucht wird. Zusätzlich wird der vollständige Klassenname und der Scope deklariert.

 Auszug aus der remote-config.xml          




org.fleksray.samples.RemoteObjectService
application



Scoping
Die drei Möglichkeiten um einen Scope zu definieren sind application, session und request. Diese Varianten den Scope zu definieren sind Java Entwicklern hinreichend bekannt, deshalb hier nur eine kurze Erklärung.
Wird für das RemoteObject der Scope application definiert, wird für jede Serverinstanz nur eine einzige Klasse instantiiert. Alle Anwender der Applikation greifen also auf das gleiche Objekt zu. Im session Scope wird für jeden User ein eigenes Objekt angelegt. Dieser Scope wird deshalb in der Regel für Session Tracking, das Speichern von Warenkörben und Ähnlichem verwendet. Im request Scope wird für jeden HTTP Request ein neues Objekt erzeugt. Dieser Scope erhöht die Rechenlast des Servers empfindlich und sollte nur, wenn unbedingt notwendig angewendet werden.
21. Oktober 2007

» Aufruf entfernter Methoden

Verarbeiten der Daten auf dem Client mit Hilfe der Binding Expressions
Um die vom RemoteObject bereitgestellten Daten zu verarbeiten, stehen dem Entwickler zwei Möglichkeiten zur Verfügung: Binding Expressions und in ActionScript geschriebene Event Handler. Erstere Methode ist einfacher und weniger verbose. Werden dagegen eigene Event Handler benutzt stehen mehr Möglichkeiten zur Verfügung, die Daten zu verarbeiten.
Mit Hilfe der Binding Expression ist das entgegennehmen der Daten recht einfach.

 Auszug aus der MXML Datei die das RemoteObject verwendet


destination="myRemoteObjectService"/>




Verwenden der Event Handler
Das RemoteObject löst bei erfolgreicher Datenübertragung ein ResultEvent aus. Lief etwas schief wird ein FaultEvent getriggert. Innerhalb der Event Handler können die Ergebnisse des entfernten Methodenaufrufes vielfältiger gehandhabt werden, als mit den Binding Expressions.

 Die MXML Datei verwendet nun Event Handler





import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;

[Bindable]
private var _message:String;

private function handleResult(ev:ResultEvent):void {
_message = ev.result.toString();
}

private function handleFault(ev:FaultEvent):void {
_message = "das ging daneben: "
+ ev.fault.faultCode + " :: "
+ ev.fault.faultDetail + " :: "
+ ev.fault.faultString;
}
]]>


destination="myRemoteObjectService"
result="handleResult(event)"
fault="handleFault(event)"/>






Ein FaultEvent kann leicht ausgelöst werden, indem im click Handler des Buttons auf eine nicht existierende Methode des RemoteObject zugegriffen wird ( click="{myRemoteObject.sayGoodBye()}")
Result Handler Dispatching im RemoteObject
Wie geht man nun mit Service Objekten um, die dem Client eine ganze Reihe von Methoden zur Verfügung stellen. Im RemoteObject wird im oben gezeigten Beispiel nur einen Result Handler defininiert, der für genau eine Methode zuständig ist..
Ein erster, einfacher Ansatz wäre, im Result Handler eine switch-case Anweisung zu schreiben, die anhand des übergebenen Events die aufzurufende Methode erkennt.
Flex bietet eine elegantere Lösung. Innerhalb des RemoteObject können den Service Methoden jeweils unterschiedliche Result Handler zugeordnet werden.

 Dispatching der Events an unterschiedliche Handler





import mx.collections.ArrayCollection;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;

[Bindable]
private var _message:String;
[Bindable]
private var _data:ArrayCollection;

private function handleHelloResult(ev:ResultEvent):void {
_message = ev.result.toString();
}

private function handleDataResult(ev:ResultEvent):void {
_data = ev.result as ArrayCollection;
}

private function handleFault(ev:FaultEvent):void {
_message = "das ging daneben: "
+ ev.fault.faultCode + " :: "
+ ev.fault.faultDetail + " :: "
+ ev.fault.faultString;
}
]]>


destination="myRemoteObjectService" fault="handleFault(event)" >
name="sayHello" result="handleHelloResult(event)" />
name="getTestData" result="handleDataResult(event)" />




click="{myRemoteObject.sayHello()}"/>




click="{myRemoteObject.getTestData()}"/>



Im gezeigtem Beispiel wird für jede Methode des Service Objektes ein eigener Result Handler deklariert, der Fault Handler wird von allen Methoden gemeinsam benutzt. Es ist auch möglich, jeder Methode einen eigenen Fault Handler zuzuweisen.
31. September 2007

» Übergabe von Argumenten

In den meisten Fällen wird von einer Applikation mehr verlangt, als nur einfache Methoden aufzurufen. In der realen Welt müssen Eingaben des Benutzers an den Server weitergeleitet werden und zu einer entsprechenden Response umgesetzt werden. Die Eingaben werden, wie in bei Html auch, oft über Formulare entgegengenommen und dann verarbeitet.
Das RemoteObject muss also auch in der Lage sein, Argumente zu verarbeiten. Im folgendem Beispiel soll das Service Objekt auf dem Server aus zwei übermittelten Strings ein personalisierte Begrüßung bauen.

 Die Java Klasse übernimmt Argumente


public class AnsweringService {

public AnsweringService() {}

public String sayHello(String title, String name) {
return "Hallo " + title +" " + name;
}
}

In der MXML-Datei werden die Argumente innerhalb des <method> Tags deklariert. Hier können statische Werte oder Binding Expressions verwendet werden.
Das Tag erwartet ein Array mit Parametern. Wie diese Parameter benannt werden, ist unwesentlich. Der Konsistenz halber sollten sie aber wie die Argumente der Java Klasse benannt werden. Entscheidend ist die Reihenfolge der Argumente. Das zuerst deklarierte Element wird als erstes Argument an das Objekt auf dem Server übergeben.
Werden Argumente in der beschriebenen Weise übergeben, muss im Click EventHandler die Methode send() an der Remotemethode aufgerufen werden.

 Senden von Argumenten an das Objekt auf dem Tomcat Server





destination="myAnsweringService">


<br /> {title.selectedItem.toString()} <br />

{myName.text}











id="title"
dataProvider="['Frau','Herr','Mrs.','Mr.']"/>


id="myName"/>



click="{myAnsweringObject.sayHello.send()}"/>

Adobe Flex LiveCycle Data Service

» Der Adobe Flex LiveCycle Data Service

adobe flex lifecycle dataservice
Flex Data Service ist jetzt Livecycle Data Service Express
Adobe hat mit der Veröffentlichung des Flexbuilders 3 beta dem Flex Data Service einen neuen Namen gegeben. Die Serverkomponente des Flex Frameworks nennt sich nun Adobe LiveCycle Data Service.
Zu den schon bekannten Komponenten des Flex Data Service gesellen sich nun die Features des LiveCycle Services, wie Dokumenten- und Formularprocessing. LiveCycle war vorher eine eigenständige Software, die hauptsächlich mittlere und große Firmen als Kunden im Visier hatte.
Die Express Version der Software kann kostenlos von der Adobepage heruntergeladen werden. Im folgenden Artikel sollen die Teile des Service vorgestellt werden, die auch schon im Flex Data Service enthalten waren und eben für Flex Entwickler interessant sind.
Adobe nennt die Software abwechselnd "Flex Data Service" oder "LiveCycle Data Service Express". Beide Begriffe werden hier synonym verwendet.
01 Oktober 2007

» Installation und Starten des Adobe LiveCycle Dataservice

Der Flex Dataservice kann unter der URL http://www.adobe.com/cfusion/tdrc/index.cfm?product=livecycle_dataservices für unterschiedliche Betriebssysteme heruntergeladen werden. Für den Download muss man sich einen Account bei Adobe einrichten und anschließend mit Email und Passwort einloggen.
Im Folgenden wird die Installation unter Windows und gängigen JEE Servern beschrieben. Grundsätzlich lassen sich zwei Arten der Installation unterscheiden.
Der Flex Dataservice kann als Komplettpaket installiert werden. Diese Variante enthält JRun als Java Server Runtime. Dieses Paket steht als *.exe-Datei zu Verfügung und installiert eine ready to go Version der Adobe Software.
Für den Einsatz in einer Produktionsumgebung kommt eher die Installation als *.war-File auf einem existierenden JEE-Applicationsserver in Frage, da JRun nur als Entwicklungsumgebung kostenlos zu haben ist.
Installation des vorkonfigurierten Komplettpaketes
Ein Doppelklick auf die ausführbare Datei öffnet einen Wizard der durch die Installation führt. Nach dem Bestätigen der Lizenzbestimmungen wird der Benutzer nach einer Seriennummer gefragt. Für die Installation der Expressversion wird dieses Feld frei gelassen. Die Expressversion des Data Service ist voll funktionsfähig und kann auch für kommerzielle Projekte genutzt werden, ist jedoch auf die Nutzung einer CPU beschränkt. Die genauen Bestimmungen zur Nutzung finden sich auf der Adobe Seite.
Anschließend wird das Installationsverzeichnis gewählt (alle weiteren Angaben im Artikel beziehen sich auf das Standard Installationsverzeichnis c:\lcds). Dann hat der User die Wahl, den Dataservice mit integriertem JRun Servlet Container oder als JEE Applikation zu installieren. Als Entwicklungsumgebung ist JRun ausreichend, als Produktionsserver sollte jedoch einer der Standard JEE Applicationserver zum Einsatz kommen. Es erscheinen noch die üblichen Standardabfragen, dann beginnt die Installation.

JRun Launcher

Nach Beendigung der Installation befindet sich im Verzeichnis c:\lcds ein Unterordner JRun/bin. Wird die im Verzeichniss befindliche jrun.exe ausgeführt, öffnet sich der JRun Launcher, mit der sich der Server starten, restarten und stoppen lässt. Nach Selektieren des default-Eintrages und einem Klick auf den Start Button fährt der Server hoch.
Unter der Adresse http://localhost:8700 kann nun die lokale Instanz von JRun angesprochen werden.
Installation unter einem Java Web Application Server
Die Installation des Flex Data Service unter einem JEE Server ist ebenfalls leicht durchzuführen. Alle *.war Dateien, die sich nach der oben durchgeführten Installation in c:\lcds befinden, laufen ohne Probleme auch in den ensprechenden Deploy Verzeichnissen typischer JEE-Server.
Tomcat: $CATALINA_HOME/webapps
JBoss: $JBOSS_HOME/server/default/deploy
Wird beispielsweise das c:\lcds\samples.war in das webapps Verzeichniss des Tomcat kopiert, ist nach einem restart/redeploy die Applikation unter localhost:8080/samples zu erreichen.
Gleiches gilt für JBoss und andere JEE konforme Applicationserver. Für eigene Entwicklungen sind die mitgelieferten Anwendungen der Standardinstallation also ein guter Ausgangspunkt.

From the scratch
Soll eine Webanwendung, die den Flex Data Service nutzt, von Grund auf entwickelt werden, sind einige Kleinigkeiten zu beachten.
Im WEB-INF Verzeichniss muss ein Unterordner mit dem Namen flex existieren, der sämtliche Konfigurationsdateien des Dataservice enthält. Auch hier empfiehlt es sich, den Ordner aus einer Beispielanwendung zu kopieren.
Im WEB-INF/lib Verzeichniss müssen alle nötigen flex-*.jar Dateien vorhanden sein. Auch diese kopiert man am besten aus einer Beispielanwendung. Dann müssen noch sämtliche Eintragungen der web.xml, die sich im flex/WEB-INF Verszeichnis befindet, in die schon bestehende web.xml übernommen werden.
Ebenfalls eine gute Grundlage für Eigenentwicklungen ist der mit dem Dataservice vorkonfigurierte Tomcat, der auf der Adobe Seite unter http://www.adobe.com/devnet/flex/articles/java_testdrive.html heruntergeladen werden kann.
10. Juli 2007

» Der Proxy Service des Flex Data Service

Der Flash Sandkasten
Jedem Flash und Java Applet Entwickler ist die Thematik der sogenannten Sandkastenumgebung der Standardbrowser bekannt. Applikationen, die über das Internet aufgerufen werden, haben nur eingeschränkte Zugriffsrechte auf dem Clientrechner.
Solche Anwendungen können nicht auf das lokale Dateisystem zugreifen und nur uneingeschränkt auf Resourcen des Servers zugreifen, von dem sie geladen wurden.
Mit der crossdomain.xml kann diese Einschränkung zwar umgangen werden, dafür braucht der Entwickler jedoch Zugriff auf den Server, auf dem die benötigten Resourcen installiert sind. Das ist jedoch in den seltensten Fällen so.
Ohne Proxy Service können die Services mx.rpc.http.HttpService und der mx.rpc.soap.WebService mit dem Parameter url nur Daten von dem Server abrufen, von dem sie selbst geladen wurden.
Mit dem Proxy können die Services auch auf Resourcen anderer Server zugreifen. Die Clientanwendung merkt davon nichts. Der Request wird nach wie vor an den eigenen Server gestellt. Die Proxy Kompenente des LiveCycle Dataservice nimmt die Anfrage entgegen und leitet sie an den entsprechenden Server im Netz weiter. Dafür muss die Eigenschaft useProxy auf true gesetzt werden, nur dann spricht der Proxy Service auch an und liest gegebenenfalls die Eigenschaft destination aus.
Der url Parameter
Im folgenden gehen wir von einer Applikation mit dem Namen myApplication aus, die einen HTTPService enthält. Der Sourcecode sieht vereinfacht so aus:

  




url="data/myDataFile.xml" />



Das Programm lädt die Daten vom eigenem Server, das Verzeichnis "data" befindet sich dabei auf der gleichen Ebene wie das swf-File der Flexapplikation.

Im url Parameter kann auch eine absolute Adresse angegeben werden. Vorausgesetzt die Applikation wird über die URL http://localhost/myApplication aufgerufen, kann auch dieser Code verwendet werden.
url="http://localhost/data/myDataFile.xml" />

Das funktioniert nicht und löst einen Cross Domain Security Error aus!
url="http://127.0.0.1/data/myDataFile.xml" />

Die Default Destination
Unter einer Destination ist der Endpunkt einer Kommunikationskette zu verstehen. Auch in der Webservice Entwicklung wird der Begriff Endpunkt (natürlich in englisch -> endpoint ) verwendet. Damit eine bestimmte Ressource über den Proxy Service angesprochen werden kann, müssen in der proxy-config.xml einige Einstellungen vorgenommen werden. Die proxy-config.xml sieht im Normalfall so aus.

 Die proxy-config.xml




proxy-service"
class="flex.messaging.services.HTTPProxyService"
messageTypes="flex.messaging.messages.HTTPMessage,flex.messaging.messages.SOAPMessage">



100
2




http-proxy"
class="flex.messaging.services.http.HTTPProxyAdapter"
default="true" />
soap-proxy"
class="flex.messaging.services.http.SOAPProxyAdapter" />



my-http" />
my-amf" />


DefaultHTTP">




Standardmäßig ist in der proxy-config.xml bereits ein Eintrag für die Default Destination vorhanden, dieser ist jedoch leer.
Um Proxy Zugriffe über den Flex Data Service zu ermöglichen, muss der Eintrag erweitert werden.

 Ergänzung der Destination


DefaultHTTP">

http://127.0.0.1:8700/*



Der Stern am Ende der URL ermöglicht dem Flex Client auf jeden möglichen Webservice des (scheinbar) entfernten Servers zuzugreifen.

Zum genaueren Verständnis.
Hier im Beispiel stellt der der Server mit der IP 127.0.0.1 den Remoteserver dar. Die Clientanwendung, aufgerufen über http://localhost:8700/myApplication kann im Normalfall nicht auf den den eigenen Server über die URL http://127.0.0.1:8700 zugreifen. Die crossdomain-policy sieht in den Servern mit den URL's http://localhost und http://127.0.0.1 zwei verschiedene Hosts.
Um in einer Entwicklungsumgebung mit dem Proxy Service zu experimentieren eignen sich diese URL's gut, um zwei unterschiedliche Server zu simulieren.

Nun muss noch im HTTPService die useProxy Eigenschaft auf true gesetzt werden.

          

id="myService"
url="http://127.0.0.1/data/myDataFile.xml"
useProxy="true" />


Der HTTPService ist nun in Lage die Resource myDataFile.xml über den Umweg des Proxy Service vom (vermeintlich) entfernten Server abzurufen.

Die Named Destination
Mit der named Destination entscheidet der Client nicht mehr selbst, welche URL, von welchen Remoteserver aufgerufen wird. Dem Client ist lediglich die Id des Services bekannt. Den Proxy Service mit der richtigen URL zu spezifizieren, liegt nun in der Hand der Servers. Dadurch wird die Sicherheit des Systems beachtlich erhöht. Anwendungen auf Clientseite können nicht mal eben verschiedene URL's ausprobieren.

 Der geanderte Eintrag in der proxy-config.xml


id="myProxyService">

http://127.0.0.1:8700/flex/data/myService.xml



Statt einer URL wird im HTTPService jetzt der Parameter destination verwendet. Die Destination, die im HTTPService angegeben wird, muss mit der Destination Id in der proxy-config.xml übereinstimmen.

 Im HTTPService wird die enfernte Ressource uber die Id aufgerufen


id="myService"
destination="myProxyService"
useProxy="true" />

Flex LiveCycle DataManagement Service and Hibernate

» Flex LiveCycle DataManagement Service und Hibernate im Zusammenspiel

Hibernate und Adobe Flex Frontends lassen sich in einfacher Weise mit dem DataManagement Service verbinden. Damit lassen sich in Java entwickelte Klassen eins zu eins in eine Flex Anwendung übernehmen. Alle Änderungen an Objekten im Flex Client werden direkt auf die Java Objekte übertragen.
Wird der DataManagement Service in Verbindung mit Hibernate verwendet, ist es nicht einmal nötig DataAccess Klassen zu entwickeln. Alle Create-, Update- und Delete-Operationen werden direkt auf die Java Objekte angewendet (hinter den Kulissen führt die Klasse flex.data.adapters.JavaAdapter des DataManagement Service die CRUD Operationen aus).
Diese Vorgehensweise vereinfacht den Entwicklungsaufwand enorm. Die Programmierung von DAO's entfällt.
Die Verwendung des DataManagement Service bringt aber auch Nachteile mit sich. Der Service verwendet das Real Time Messaging Protokoll, das im Normalfall Port: 2038 verwendet.
Der Einsatz beschränkt sich deshalb mehr oder weniger auf Intranet Anwendungen. Soll Hibernate in klassischen Internet Applikationen eingesetzt werden, kommt man nicht umhin, Data Access Klassen zu implementieren und deren Interface per WebService im Netz zur Verfügung zu stellen.

Hibernate mit Flex DataManagement Service

Die Studenten-Kurse Anwendung unter Verwendung von Hibernate


Studenten und Seminare, eine m:n Beziehung
Im folgenden Beispiel wird eine Anwendung entwickelt, mit der es möglich ist Studenten und Seminare zu verwalten. Für diese Entities werden zwei entsprechende Klassen, sowohl in Java, als auch in ActionScript erstellt. Für die Persistenz der Objekte ist Hibernate zuständig. Als Datenbank kommt Hypersonic SQL zum Einsatz. Als Webapplication Server wird Tomcat verwendet.
Mit der Flex Anwendung ist es möglich, Create, Update und Delete Operationen für Studenten und Seminare auszuführen.
Der Einfachheit halber wird auf die Funktionalität verzichtet, Studenten bestimmte Seminare zu zuweisen. In der der Hypersonic Datenbank sind jedoch bereits einige Zuweisungen angelegt. Mit der hier entwickelten Applikation können die Seminare nur angezeigt werden, die von einem bestimmten Studenten belegt werden.

Flex Hibernate

Anzeige der Kurse für die ausgewählte Studentin


Da die komplette Anwendung recht umfangreich ist, wird im Artikel nur auf Ausschnitte des Source Code eingegangen. Die kompletten Quellen finden sich in der Box: Source Code im linken Seitenbereich oben.
25, Oktober 2007

» Die Java Klassen und die Hibernate Config-Files

Die Java Klassen
Für die Entities Student und Course werden zwei Java Beans angelegt, deren Aufbau recht simpel ist.

Die Klasse Student besitzt die Eigenschaften und entsprechenden set- und get-Methoden:
studentNumber; studentFirstName; studentLastName
Die Klasse Course besitzt die Eigenschaften und entsprechenden set- und get-Methoden:
courseNumber; courseName; description

Die Java Klasse Student besitzt ein HashSet namens courses. Dieses Set enthält die Kurse, für die sich der Student eingetragen hat.
Ebenso besitzt die Klasse Course ein HashSet students, das alle Studenten enthält, die diesen Kurs belegen.
Im Beispiel handelt es sich also um eine m:n Beziehung.
Ein Student belegt mehrere Kurse.
Ein Kurs wird von mehreren Studenten belegt.

Die Hibernate Config-Files
Die hibernate.cfg.xml enthält allgemeine Einstellungen zur Datenbank-Connection, zum SQL-Dialect usw. auf die hier nicht weiter eingegangen werden soll.
In der StudentCourse.hbm.xml werden die Klassen Student und Course gemappt. Diese Datei wird im folgendem genauer erklärt.
Das Hibernate Mapping File für die Studenten- und die Course Klasse enthält das Root-Tag . Innerhalb dieses Tags werden die Java Klassen auf die entsprechende Tabelle der SQL Datenbank gemappt.
Bei komplexen Anwendungen erhält jede Javaklasse/Sql-Tabelle ein eigenes Mapping-File. Hier im Beispiel werden die Klassen Student und Course der selben Datei eingetragen. Auf das Mapping der Klasse Student soll etwas genauer eingegangen werden. Das Mapping für die Klasse Course ist ähnlich.

 Die gemappte Student Klasse










name="courses"
table="Course_Student"
inverse="true"
lazy="true"
cascade="save-update">







From Student

Im öffnendem Tag wird der Name der Klasse und die entsprechende SQL-Tabelle angegeben.
Im Tag wird der Primary-Key vereinbart. Im Fall der Klasse Student ist das die Eigenschaft studentNumber, die der Spalte student_number der Tabelle Student in der Hypersonic Datenbank entsprechen soll.
Dann werden die Eigenschaften studentFirstName und studentName deklariert. Solange der Name der Eigenschaft mit dem Namen der Tabellenspalte übereinstimmt, müssen hier keine weiteren Einstellungen vorgenommen werden (Hibernate bietet eine Fülle von Möglichkeiten um die Eigenschaften der Properties feiner zu spezifizieren, darauf soll an dieser Stelle jedoch verzichtet werden).

Im Tag wird ein Collection deklariert, die die Kurse enthält, für die sich der Student eingeschrieben hat. Der Name des Set muß dem Namen des Property in der Klasse Student entsprechen. Als table wird die Tabelle angegeben, die die Beziehung zwischen der Klasse Course und der Klasse Student managed.

Die Tabelle Course_Student enthält lediglich die zwei Spalten student_number und course_number, welche die primary keys der Tabellen Student und Course repräsentieren. Im Tag wird Hibernate die Art der Beziehung, die Klasse zu der die Beziehung aufgebaut werden soll und der primary key eben dieser Klasse mitgeteilt.
Wird die Eigenschaft lazy auf true gesetzt, wird die Student Klasse per default mit einem leeren courses-Set instantiiert. Die Eigenschaft cascade gibt an, wann das Set gefüllt werden soll.

Am Ende wird ein HQL-Query Statment definiert, das in der Flex Anwendung dazu dient, eine Collection mit Studenten Objekten zu füllen. Dazu mehr in der Box: Die Flex Applikation weiter unten.
Das Mapping für die Klasse Course ist nahezu identisch aufgebaut.
25, August 2007

» Die Config-Files für den DataManagement Service

Konfiguration des RMTP-Channels und der Destinations
Wie alle Konfigurationdateien für den Adobe LiveCycle Service sind auch die Config-Files für den DataManagement Service unterhalb des [CONTEXT-ROOT]/WEB-INF/flex Verzeichnisses zu finden. Die beiden relevanten Dateien, für die hier vorgestellte Applikation, sind services-config.xml und data-management-config.xml.
In der services-config.xml muss folgender Eintrag (meist schon per default) vorhanden sein.

 Die Definition des RTMP-channels



class="flex.messaging.endpoints.RTMPEndpoint"/>

20
100K
100K



In der data-management-config.xml Datei werden für die entwickelten Java Klassen so genannte destinations deklariert. Im Tag wird eine frei wählbare ID vergeben. Diese ID wird später in der Flex Anwendung benötigt, um den DataService für die entsprechende Klasse anzusprechen. Innerhalb der Destination werden folgende Angaben gemacht:
Hier wird eine Refererenz auf den Adapter angegeben, der die Verbindung zu den Java Objekten aufnehmen soll. In unserem Fall ist das die Adapter Klasse flex.data.adapters.ASObjectAdapter.

Dann folgen eine Reihe von Eigenschaften, die das Zusammenspiel der entwickelten Java Klassen, Hibernate und dem DataManagement Service regeln. Im folgenden wird nur auf die für unsere Anwendung relevanten Properties eingegangen.

Hier wird die Klasse des Hibernate Assemblers eingetragen, eine Klasse des Flex LiveCycle Service, die die Schnittstelle zu Hibernate darstellt.
Einstellung des "Sichtbarkeitsbereiches" der Student Klasse. Möglich sind: application, session und request.
Angabe des primary key der Student Klasse.
Angabe des kompletten Klassepfades zur Student Klasse.
Name der Fill-Methode und Angabe der Collection Klasse, die später die Studenten Objekte in der Flex Anwendung aufnehmen soll.
Hier ist der Eintrag true wichtig. Dieser erlaubt in der Flex Applikation HQL-Queries aufzurufen, die in der StudentCourse.hbm.xml definiert wurden.

 Die Definition der Student destination in der data-management-config.xml



id="hibernate-student">
ref="java-dao" />

true
flex.data.assemblers.HibernateAssembler
application

property="studentNumber"/>


20





org.fleksray.samples.model.Student

fill
java.util.List


false
true






Der Aufbau für die Course Destination ist ähnlich.
25, August 2007

» Die Flex Applikation

Die Student- und die Course Bean in ActionScript
Um erfolgreich mit den Java Objekten auf dem Tomcat kommunizieren zu können, müssen zwei Klassen in ActionScript implementiert werden, die das Gegenstück zu den Java Klassen Student und Course darstellen. Diese Klassen enthalten die gleichen Properties wie die Java Klassen. Die Properties werden als public Variablen deklariert, auf get- und set-Methoden wird hier verzichtet.
Per Definition muss in den ActionScript Klassen ein leerer Konstruktor deklariert werden. Die Klassen werden als Managed deklariert und ein Alias auf die entsprechenden Java Klassen wird gesetzt.

 Die ActionScript Klasse Student



package org.fleksray.samples.model
{
import mx.collections.ArrayCollection;

[Managed]
[RemoteClass(alias="org.fleksray.samples.model.Student")]


public class Student {

public var studentNumber:Number;
public var studentName:String;
public var studentFirstName:String;
public var courses:ArrayCollection;

public function Student(){}
}
}

Die Klasse Course.as hat einen ähnlichen Aufbau.
Der DataService in Flex Anwendung
In der Flex Applikation ist ein DataGrid zuständig für die Anzeige der Eigenschaften der Studenten. Dieses DataGrid verwendet eine ArrayCollection, die mit Objekten vom Typ Student gefüllt wird. Dafür muss in der Anwendung eine Instanz der Klasse Student und eine Instanz der zu füllenden ArrayCollection erzeugt werden.
Im Tag ist für die destination ein Name erforderlich. Dieser Name entspricht genau der Id, die in der data-management-config.xml für die Student destination vereinbart wurde.

 Auszug aus der Flex Application






destination="hibernate-student"
fault="handleFault(event)"
autoCommit="true" />


autoCommit="true"
Hier im Beispiel wird die Eigenschaft autoCommit auf true gesetzt. Damit wird erreicht, dass alle Änderungen, die an der ArrayCollection studentArray direkt auf den Server übertragen werden. Wird also die Eigenschaft editable des DataGrid auf true gesetzt, werden alle vom Anwender vorgenommen Änderungen, auf die von Hibernate verwalteten Objekte übertragen (ohne das ein Submit-Button o.ä. geklickt werden muss). Anwender die ebenfalls mit dem Server verbunden sind, sehen die vorgenommenen Änderungen sofort, ohne einen Reload ausführen zu müssen. Ein entscheidender Vorteil des Real Time Messaging Protokoll.
Füllen der ArrayCollection
Nun muss noch die ArrayCollection, die als DataProvider für das DataGrid dient, gefüllt werden. Dafür wird die Methode fill() am DataService aufgerufen. Diese Methode wurde oben ebenfalls in der data-management-config.xml unter deklariert.
Der fill() Methode werden drei Argumente übergeben. Erstes Argument ist die zu füllende ArrayCollection, dann folgt der Name des HQL-Query, wie er in der Student.hbm.xml vereinbart wurde. Der dritte Parameter wird hier nicht verwendet.

Der dritte Parameter der fill() Methode.
Der dritte Parameter wird verwendet, wenn direkt HQL-Querys aus der Flex Anwendung abgesetzt werden sollen. Das ist sinnvoll, wenn die Abfrage Variablen enthält, deren Wert zur Kompilierzeit nicht bekannt sind. Der dritte Parameter erwartet eine Array. In diesem Array ist der erste Wert der HQL-Query selbst, gefolgt von den dynamischen Parametern für diesen Query:
hibernateStudent.fill(studentArray, "flex:hql", ["from Student s where s.studentFirstName = :firstName", parameterMap]);
Die parameterMap enthält unter dem key "firstname" den Namen des Studenten, nach dem gesucht werden soll.

oder:

hibernateStudent.fill(studentArray, "flex:hql", ["from Student s where s.studentFirstName = ?", parameterArray]);
hier enthält das parameterArray den Namen des Studenten nach dem gesucht wird. Bei mehreren Parametern ist die Reihenfolge der Parameter zu beachten

Die Methode hibernateStudent.fill() wird in einer Mehtode mit dem Namen initialFill() aufgerufen. Diese Methode wiederum wird beim creationComplete() Event der Applikation aufgerufen. Damit wird das DataGrid beim ersten Aufruf der Applikation gefüllt.
Der DataService stellt weiterhin die Methoden deleteItem und createItem zur Verfügung, also die typischen Methoden einer DAO Klasse. Diese Methoden wurden im Beispiel an keiner Stelle definiert. Die Arbeit dafür übernimmt voll und ganz der Flex DataManagement Service.

 Die Methode zum Löschen von Studenten


private function deleteStudent():void {
hibernateStudent.deleteItem(dgStudent.selectedItem as Student);
}


Der Quellcode für die Anzeige, das Löschen und das Erzeugen von Kursen sieht ähnlich aus.

25, August 2007