Mit MOM-Entwicklungen lässt sich im Enterprise-Markt gegenwärtig recht viel Geld verdienen. Und wie das so üblich ist, kocht jeder Hersteller sein eigenes Süppchen. Bei Java ist natürlich alles anders. Hier gibt es mit JMS (Java Message Service) ein standardisiertes API, das einfach zu verwenden ist. Wie, das zeigen die folgenden Zeilen anhand eines einfachen Beispiels.
Warum nicht selber machen?
Eine Client-Server-Anwendung mit Java schreiben ist eine einfache Angelegenheit. Mit den beiden Klassen java.net.ServerSocket und java.net .Socket ist es kein Problem, eine TCP/IP-Verbindung zwischen zwei Programmen aufzubauen und zu nutzen. Beispiele dazu sind in jedem besseren Lehrbuch zu Java zu finden (zum Beispiel in [1], dass sich speziell mit der Netzwerk-Programmierung unter Java beschäftigt).
Steht die Verbindung, wird bei sehr vielen Client-Server-Anwendungen das gleiche Design-Pattern verwendet: Der Client schickt Daten, deren Bedeutung der Server interpretieren kann, woraufhin der Server Daten an den Client zurückschickt. Klassische Beispiele sind FTP und HTTP.
Fasst man die Daten als Nachrichten (Messages) auf, die hin- und wieder zurückgeschickt werden, ist man beim Begriff der Nachrichten-basierten Client-Server-Anwendung gelandet. Dabei ist der beschriebene Request-Response-Mechanismus nur eine von vielen Möglichkeiten. Es liegt allerdings nahe, für ein Design-Pattern eine einheitliche Lösung einzusetzen.
Hier schlägt die Stunde der so genannten Middleware, die eine Lösung für den Nachrichtentransport auf generische Art bietet. Da aber jedes zusätzliche Stück Software ausgesucht, getestet, installiert und konfiguriert werden muss, sollte sich der Aufwand auch lohnen.
Bei eigenen Programmen kann man es sich sehr einfach machen und nur minimale Informationen (etwa Integer-Werte) über die Leitung schicken, um beispielsweise Netzwerk-Bandbreite zu sparen. Trotzdem ist ein gewisser Aufwand nicht zu umgehen: Die zu übertragenden Informationen müssen in den Socket geschrieben, eventuelle Exceptions abgefangen und die Antworten des Servers interpretiert werden. Das Design effizienter Anwendungen wird zusätzlich komplizierter, wenn man nur bestimmte Ports verwenden darf oder optimale Routen wählen muss, um die Last zu verteilen.
Ein weiteres wichtiges Argument gegen eine eigene Implementation der Kommunikationsschicht ist auch das Problem von asynchronen Nachrichten. Diese sind zwar nicht für jede Anwendung, aber oft genug sinnvoll. Man denke an eine Anwendung, die auf einem mobilen Gerät läuft. Sie produziert Nachrichten, beispielsweise Buchungssätze, die abends über eine Wählverbindung an einen zentralen Server übertragen werden sollen. Wenn dabei die Middleware das Puffern übernimmt, dann erspart man sich die Implementation bei der Anwendung selbst.
Als weiteres Argument für eine unabhängige Kommunikationsschicht ist die Kommunikation von einem Sender zu vielen Empfängern zu nennen (wie beispielsweise für Newsgroups). Das geht zwar auch über Multicast-Sockets, doch ist bei denen die Anwendungs- und nicht die Transportschicht für den fehlerfreien Datenaustausch verantwortlich. Auch für dieses Problem bietet die richtige Middleware eine geeignete generische Lösung.
Architektur des JMS-API
Nach der hoffentlich motivierenden Einleitung zum Thema Middleware nun ein Blick auf das JMS-API, das in der Version 1.0.2 vorliegt. Erhältlich ist es über [2], das gezippte File ist etwa 300 KByte groß. Zusätzlich ist noch ein Paket mit Beispielcode verfügbar.
Das JMS-API ist flach. Es besteht nur aus dem Package javax.jms und enthält neben zwei Hilfsklassen, einer Reihe von Exceptions (mit Basis javax.jms.JMSException) nur noch Interfaces. Diese Interfaces, von denen einige optional sind, müssen von JMS-Providern implementiert werden.
Der JMS definiert zwei Kommunikationsmodelle: Point-to-Point (kurz PTP) und Publish/Subsribe (kurz Pub/Sub). PTP-Kommunikation erfolgt zwischen einem Sender und einem Receiver mittels einer Queue, die Pub/Sub-Kommunikation zwischen einem Publisher und einem oder mehreren Subscribern. Hier heißt die Abstraktion Topic. Im angeführten Beispiel einer Newsgroup ist der Name der Newsgroup mit dem Topic gleichzusetzen.
Welches Kommunikationsmodell verwendet wird, hängt von der Anwendung beziehungsweise dem gewählten Design ab. Bei einer Logistik-Firma mit mehreren Fahrradkurieren könnten beispielsweise die Java-fähigen Fahrräder über eine Queue regelmäßig ihre Position an die Zentrale melden. Dort wird dann anhand aller vorhandenen Informationen ein neuer Auftrag optimal einem Kurier zugeteilt. Bei diesem Design muss man in der Zentrale sehr viel Logik implementieren. Alternativ dazu könnte ein neuer Auftrag über ein Topic veröffentlicht und von allen (freien) Kurieren gelesen und angenommen werden. Dieses Design verzichtet auf die aufwändige Implementation der Logik, führt aber eventuell zu einer weniger optimalen Ressourcenauslastung.
Im JMS finden sich für jede der beschriebenen Abstraktionen entsprechende Interfaces. Diese sind sehr allgemein gehalten, so definiert das javax.jms.Topic-Interface nur die beiden Methoden getTopicName() und toString(). Das erlaubt dem JMS-Provider eine große Freiheit beim Implementieren und den existierenden MOM-Anbietern eine einfache Möglichkeit, JMS-Schnittstellen zur Verfügung zu stellen.
Abb. 1: Erzeugen von QueueSender und QueueReceiver.