Montag, 6. April 2009

GWT Lernen: Wie funktionieren die RPCs?

DSCF0005c

Die RPCs (Remote Procedure Calls), die das GWT bietet, sind leicht zu nutzen. Aber wie funktioniert die ganze Sache eigentlich? Und sollten wir die RPCs einsetzen, oder lieber auf vorhandene bzw. allgemeinere Services setzen?

Wie funktioniert die GWT RPC ‘Magie’?

Das Diagramm, welches man in der Doku zum Thema RPCs findet, ist schon recht hilfreich.  Der GWT Compiler arbeitet gerade bei der clientseitigen Programmierung eines RPCs mit Namenskonventionen:

So muss das asynchrone Serviceinterface immer den Namen des synchronen Interfaces plus Async tragen und im gleichen Package liegen. Auch müssen hier die gleichen Methoden definiert werden, allerdings mit einem zusätzlichen Callback Parameter und mit Ergebnistyp void. Auf Java Ebene gibt es ansonsten keine Beziehung zwischen den beiden Interfaces.

Der Callback Parameter AsyncCallback<T> ist ein generisches Element.  Für ‘T’ soll hier der Ergebnistyp der Methode im synchronen Serviceinterface gesetzt werden, dann ist dieser Typ in der onSuccess Methode direkt ohne Casting nutzbar. In der Implementierung verwendet man von AsyncCallback abgeleitete Klassen um mit Erfolg oder Misserfolg des Serviceaufrufs umzugehen.

Bei der serverseitigen Implementierung des RPCs ist hingegen weniger ‘Magie’ im Spiel: Im Grunde geht nur darum ein spezielles Servlet zu implementieren, welches das synchrone Client(!) Interface implementiert und von der Klasse RemoteServiceServlet abgeleitet ist. An dieser Stelle spielt der Klassenname keine Rolle, es gibt hier keine ‘magischen’ Namenskonventionen. Man kann das Servlet also auch im Gegensatz zu den GWT Beispielen komplett anders benennen. Wichtig ist nur, dass diese Klasse nicht im gleichen Package landet wie die Clientklassen/-interfaces.

Die Adresse des Servlets (aka Service) sollte dann noch der Clientseite durch Einträge in der <Projekt>.gwt.xml Datei (der sogn. Modul XML Datei , module XML file) bekannt gemacht werden. Diese Verdrahtung ist gewöhnlichen Servletverknüpfungen in der web.xml nicht unähnlich, kommt aber mit einer einzigen Anweisung pro Service aus. Wichtig: Diese Verdrahtung ist nur für den Test im hosted mode notwendig. Sie hat keinen Einfluss auf die produktive Installation.

Dieser Onlinevortrag enthält eine Darstellung der RPC Programmierung, die an manchen Stellen die GWT Seiten etwas ergänzt.

GWT RPC vs. XML Services plus Struts

In der mit dem GWT  erstellten BIS Anwendung gäbe es zwei denkbare Pfade, wie die Serverkommunikation mit dem Ajax Client durchgeführt werden könnte:

A) Wir setzen komplett auf die GWT RPCs

Vorteil wäre eine zunächst einheitliche und vermutlich rasche Entwicklung, die komplett in der GWT ‘Welt’ bleibt. Hier gibt es Lösungen für Exception Handling und all die anderen kleinen Dinge, die eine Anwendung erst ausreichend robust für den Einsatz machen.

Allerdings müssten einige Teile der Anwendungslogik erneut implementiert werden, die an anderer Stelle schon vorhanden sind. Auch bei dieser Lösung würden auf der untersten Ebene wieder die Struts und iBatis nutzenden Basisklassen eingesetzt werden.

Aufgaben in der Programmierung um eine Datensatzart abzudecken:

  • Erstellung einer GWT Modellklasse, z. B. ‘Person’. Wichtig ist hier die Serialisierbarkeit
  • Erstellung einer RPC Klasse für lesende Zugriffe. Muss aus den vorhandenen Strutsklassen die Instanzen der GWT Modellklassen erzeugen
  • Erstellung einer RPC Klasse für schreibende Zugriffe. Hier müssen Rechtemanagement und Logging eingefügt werden sowie das Rückmapping des GWT Modells auf die Strutsklassen

Beim Deployment der Anwendung würde man wie in der GWT Doku beschrieben vorgehen und müsste nur der Anwendung den Connectionpool zur Datenbank bekannt machen, damit die Basisklassen arbeiten können.

B) Erstellung allgemeiner XML Services und Nutzung der vorhandenen Struts Programmierung

Eine Alternative könnte in der Erstellung von ‘allgemeinen’ XML Services sein, die nicht nur für die GWT Anwendung nutzbar wären. Ein Beispiel ist der schon existierende Service für die Publikationsdaten. Auch für andere Daten werden solche XML Services nachgefragt und müssen so oder so implementiert werden.

Im GWT lassen sich solche Services ebenfalls relativ einfach verarbeiten, auch wenn der Aufwand im Vergleich zu den RPCs höher erscheint, insbesondere wenn der XML Service nur für das GWT erstellt werden müsste.

Beim Schreiben der Daten auf den Server könnte aus dem GWT heraus ein ‘normaler’ Formularsubmit ausgelöst werden, welcher dann die entsprechende Strutsklasse populiert. Ab hier würde es ‘wie gewohnt’ weiter gehen.

Aufgaben in der Programmierung um eine Datensatzart abzudecken:

  • Implementierung der lesenden XML Schnittstelle
  • Implementierung einer GWT Modellklasse, die hier aber nur auf Clientseite verwendet wird (könnte u. U. ausgelassen werden, wenn alle Daten sofort in Widgets gesteckt werden, aber dies erscheint zu unflexibel)
  • Abbildung der XML Daten auf die Modellklasse
  • Aufbau einer kompletten Strutsanwendung, die mindestens in der Lage ist die schreibenden Operationen aufzunehmen. Das Rechtemanagement kann wie bisher über Filter erfolgen oder auch direkt in den Actionklassen.

Das Deployment einer solchen Anwendung wäre ähnlich wie das Deployment einer reinen Strutsanwendung. Es müsste nur das Verzeichnis mit den statischen GWT Inhalten in die Anwendung kopiert werden.

Wie ist die Trennung von Client- und Serverseite möglich?

Für die zuvor diskutierten BIS Szenarios wäre es teilweise sinnvoll, wenn wir RPC Services, die nicht weiter geschützt werden müssten, und die auch in anderen Anwendungen genutzt werden könnten, sofort in eine allgemeine Anwendung verlagern würden.

Die Trennung müsste eigentlich vergleichsweise einfach sein:

  • Die serverseitige Implementierung braucht nur Zugriff auf das synchrone Clientinterface. Dies kann über die gemeinsame Codebasis erreicht werden.
  • Die clientseitige Implementierung findet ihre Serviceendpunkte für das Testen durch die in der <Projekt>.gwt.xml definierten Adressen. Diese können sich auf den Serverkontext beziehen und nicht nur auf den Webapplikationskontext. Sobald die Clientimplementierung in JS übersetzt wurde wird beim RPC Aufruf der in den jeweiligen ServiceDefTarget.setServiceEntryPoint(String) Kommandos gesetzte Pfad verwendet. Hier spielen die Servletdefinitionen in der Modul XML Datei keine Rolle mehr. Daher:
  • Es müssen zu den auf der Clientseite definierten Adressen passende web.xml  Verdrahtungen der Serviceservlets eingerichtet werden.

Auf diese Weise ist eine gekoppelte Entwicklung, aber ein getrenntes Deployment im Produktivsystem möglich.

Ein denkbares Szenario ist folgendes:

  • Die übersetzte Clientseite, die ja nur aus statischen Inhalten besteht (HTML Startseite, Javascript, CSS, Bilder), wird auf den Apache Servern in einem Unterverzeichnis unter den htdocs abgelegt. Eine neue Version  der Anwendung wird einfach durch Hochladen dieser Dateien installiert.
  • Die serverseitige RPC Installation erfolgt in einer J2EE Anwendung auf den Tomcat Servern (WAR Archiv).

Eine Eigenschaft dieser Lösung ist die Möglichkeit Client- und Serverseite getrennt zu aktualisieren. Bei Änderungen an den RPC Interfaces müssen natürlich beide Seiten zugleich aktualisiert werden.

Kommentar veröffentlichen