Die Neuerungen auf einen Blick
- Integration von Web Services: Dieser Schritt ist wegen der großen Bedeutung des Themas und der bereits in den meisten Application Servern vorhandenen proprietären Lösungen auch dringend nötig.
- Message-Driven Beans mit anderen Messaging Systemen als JMS: Hier kommen ebenfalls Web Services zum Einsatz, denn nun können auch SOAP-Nachrichten von einem Message-Driven Bean verarbeitet werden.
- Container-Managed Timer Service: Da man in Enterprise JavaBeans keine Möglichkeiten zur Kontrolle über Threads hat, kann man Threads nicht unterbrechen und damit auch keine zeitbasierten Ereignisse implementieren, wie dies beispielsweise für Workflow-Systeme notwendig ist.
- Die bekannten Schwächen der Enterprise JavaBeans Query Language (EJB QL) wurden behoben: Es gibt jetzt Funktionen zum Sortieren sowieso zusätzliche Aggregat-Funktionen. Außerdem gibt es zahlreiche kleinere Änderungen.
Web Services
Da Web Services im Moment noch keine standardisierte Möglichkeit zur Verwaltung eines Zustandes über eine ganze Sitzung bieten, sind sie zustandslos genauso wie pures HTTP. Dementsprechend können nur Stateless Session Beans als Web Services angeboten werden. Eines der grundlegenden Prinzipien von Komponenten-Systemen wie EJBs ist, dass der Zugriff auf die Komponenten nur durch wohldefinierte Interfaces stattfindet, wie dies auch im Component Interface Pattern in [1] beschrieben ist. Grundsätzlich stellt ein Web Service lediglich neben Remote und Local Interface eine zusätzliche Möglichkeit zum Zugriff auf eine Enterprise JavaBean dar. Es wird also neben dem Remote und dem Local Interface das Web Service Endpoint Interface eingeführt. Allerdings gibt es beim Web Service Endpoint Interface kein Home Interface.Normalerweise enthält das Home Interface Methoden, um Instanzen zu suchen oder neue zu erzeugen sowie Methoden, die auf alle Instanzen zugreifen müssen. Für Web Services macht ein solches Interface also keinen Sinn, da es wegen der Zustandslosigkeit keine individuellen Instanzen geben kann. Außerdem wäre die Rückgabe einer Referenz auf einen anderen Web Service ebenfalls problematisch. Somit werdem die Aufrufe eines Web Service-Client von einer beliebigen Instanz des Stateless Session Beans bearbeitet. Dies ist möglich, da alle Instanzen eines Stateless Session Beans aufgrund ihrer Zustandslosigkeit identisch sind. Das Web Service Endpoint Interface unterscheidet sich ansonsten technisch nicht sehr von dem Remote Interface: Es wird ebenfalls in Java als Interface definiert und muss den Vorschriften für ein JAX-RPC Interface entsprechen (siehe [2]). Somit muss auch dieses Interface java.rmi.Remote erweitern und jede Methode muss eine RemoteException werfen.
Die übertragenen Daten dürfen Basisdatentypen, Klassen wie String oder Date umfassen aber auch selbst entwickelte Klassen. Die selbst entwickelten Klassen müssen dabei entweder ihre Eigenschaften als JavaBeans Properties exportieren oder public deklariert haben. Außerdem können Arrays dieser Typen verwendet werden und auch Exceptions, die dann in SOAP Faults umgewandelt werden. Somit gibt es praktisch keine Einschränkungen für die Definition der Web Service Endpoint Interfaces. Allerdings darf das Interface keine Remote oder Local Interfaces verfügbar machen oder CMP Managed Collections.
Im Deployment Descriptor des entsprechenden Enterprise JavaBeans wird das Web Service Endpoint Interface mit Hilfe des service-endpoint Tags definiert. Für die Methoden aus dem Web Service Endpoint Interface kann man auch Transaktionsattribute definieren. Dabei sind alle Attribute erlaubt, außer dem Attribut Mandatory. Das ist darauf zurückzuführen, dass das Übertragen von Transaktionskontexten bei Web Services noch nicht spezifiziert ist. So kann außerhalb des EJB-Systems keine Transaktion begonnen werden, wie dies für Mandatory notwendig wäre. In Bezug auf Sicherheitsattribute ist es nach der Spezifikation zwar erlaubt, den Zugang zu Methoden auf bestimmte Benutzer zu beschränken. Allerdings gilt auch hier, dass es auf der Web Services Seite noch keinen etablierten Standard gibt, um diese Information zu übertragen. Damit wird hier wohl in der Realität ein Benutzer definiert werden, unter dessen Identität die Aufrufe des Web Services angearbeitet werden. Man kann dann mit dem run-as Tag im Deployment Descriptor definieren, dass alle Web Services-Aufrufe dieser Identität zugeordnet werden sollen.
Dieses Verfahren wird auch schon für Message-driven Beans verwendet. Aus diesem Interface wird dann beim Deployment durch den Application Server ein Interface in WSDL 1.1 erzeugt. Zur Kommunikation wird SOAP 1.1 verwendet. Im wesentlichen werden hier die bereits aus JAX-RPC bekannten Ansätze verwendet: Man kommt mit den typischen Web Services-Technologien nicht in Berührung, sondern bleibt in der schönen und einfachen Java-Welt, dennoch können andere Entwickler die entstandenen WSDL-Dateien in ihre eigenen Entwicklungsumgebungen importieren, um die EJB-Web Services zu verwenden. In der Implementierung des Beans muss es ein ejbCreate() zur Erzeugung neuer Instanzen geben. Es werden dann vom Application Server je nach Bedarf neue Instanzen erzeugt, die zur Beantwortung von Aufrufen verwendet werden können. Dies entspricht dem Instance Pooling Pattern aus [1].
Ein weiteres Merkmal der Implementierung besteht darin, dass es möglich ist, aus dem SessionContext den MessageContext des Web Service-Aufruf zu ermitteln. Damit kann man Informationen mit sogenannten JAX-RPC Handlern austauschen. Diese bearbeiten SOAP-Nachrichten und können Dienste wie Verschlüsselung oder Logging implementieren. Interessanterweise haben damit Enterprise JavaBeans Zugriff auf technische Interna der Web Services Umgebung, die eigentlich versteckt sein sollten, denn ein wesentliches Ziel von Enterprise JavaBeans ist ja, dass Entwickler sich nicht mit technischen Details beschäftigen müssen, sondern sich auf die Business Logik konzentrieren können. Außerdem wird sich an dieser Stelle die Bean anders verhalten, wenn sie durch die Web Services-Schnittstelle oder durch das normale Remote Interface aufgerufen wurde. Somit sollte dieses Feature nur vorsichtig eingesetzt werden.
Natürlich muss es auch eine Möglichkeit geben, wie der Entwickler einer Enterprise JavaBean einen Web Service aufrufen kann. Dazu ist in der Spezifikation festgelegt, dass ein Web Service als zusätzliche Ressource im lokalen JNDI Context eingeblendet werden kann, wie dies auch schon für Datenbanken oder JMS-Verbindungen der Fall ist. Diese sollen im JNDI-Namenskontext java:comp/env/services zu finden sein. Zur Definition der Referenzen wird das service-ref Tag für den Deployment Descriptor eingeführt. Ein Blick auf ein Beispiel eines entsprechenden Deployment Descriptors (Listing 1) zeigt, dass auch hier wieder lediglich Java-Klassen sowie der Name im JNDI Context definiert werden. Diese Klassen werden natürlich typischerweise wieder mit einem wie in JAX-RPC definierten Compiler aus WSDL erzeugt. Dies ist Aufgabe des Deployer, der die Referenzen mit Leben füllen muss. Wie dies genau geschieht, ist Application Server abhängig und damit nicht in der Spezifikation festgelegt.
Listing 1
<session>...<ejb-name>PortfolioBean</ejb-name><ejb-class>wolff.InvestmentBean</ejb-class>...<service-ref><description>Referenz zum Börsen Ticker</description><service-ref-name>service/Ticker</service-ref-name><service-ref-type>wolff.Ticker</service-ref-type></service-ref>...</session>
Message-driven Bean mit SOAP
Bereits wenn man die EJB 2.0 Spezifikation aufmerksam gelesen hat, konnte einem klar werden, dass Message-Driven Beans (MDBs) neben JMS bald auch andere Arten von Message Oriented Middleware (MOM) unterstützen würden. In EJB 2.1 ist dieser Schritt vollzogen, und so kann ein Message-Driven Bean neben JMS auch mit SOAP oder genauer gesagt mit JAXM (Java API for XML Messaging) entwickelt werden. Konkret bedeutet dies, dass ein MDB wie bisher auch das javax.ejb.MessageDrivenBean Interface implementieren muss. Dieses Interface enthält jedoch nur die setMessageDrivenContext() und die ejbRemove()-Methode und definiert nicht, wie die Business Logik aufgerufen werden soll. Soll diese Logik über JMS zugreifbar sein, so bleibt alles wie bei EJB 2.0: In diesem Fall muss zusätzlich das javax.jms.MessageListener Interface implementiert werden. Soll jedoch JAXM und damit SOAP zum Einsatz kommen, so muss entweder das javax.xml.messaging.OneWayListener Interface implementiert werden oder das javax.xml.messaging.ReqRespListener Interface. Bei dem OneWayListener kann man nur auf eine Nachricht reagieren, im anderen Fall kann man auch eine Antwort zurückschicken.Eine Antwort auf eine Nachricht war bisher mit Message-Driven Bean nicht so einfach möglich und stellt auch einen gewissen Bruch zum asynchronen Charakter der bisherigen MDBs dar. Ein Hinweis noch zum Abstraktionsgrad: Während man bei den oben erwähnten Web Services Endpoint Interfaces seine Java-Welt nicht verlässt, muss man für die JAXM-Programmierung tatsächlich SOAP Nachrichten verarbeiten. Das bedeutet zwar nicht, dass man einen XML-Datenstrom parsen muss, aber immerhin muss man die Nachricht selbst in die einzelnen Bestandteile zerlegen und diese interpretieren. Man bekommt also geparste XML-Daten, auf die man dann entsprechend reagieren muss.
Die Sicherheits- und Transaktionskonzepte der MDBs sind unabhängig davon, ob JMS oder JAXM verwendet wird: In beiden Fällen wird kein Sicherheits- und Transaktionskontext übertragen. Somit mussten für JAXM keine zusätzlichen Probleme gelöst werden. Verwirrend scheint auf den ersten Blick, dass hier noch eine weitere Lösung für Web Services angeboten wird. Dies liegt darin begründet, dass die JAXM und JAX-RPC APIs in EJB integriert werden sollten und JAXM eben eher eine Message Oriented Middleware ist und damit eher zu Message-Driven Beans passt. JAX-RPC ist näher an RMI und bietet sich dadurch als ein zusätzliches Interface für Session Beans an.
Container-Managed Timer Service
Wie bereits erwähnt war es bisher nicht möglich, mit EJBs ein System zu entwickeln, dass zu bestimmten Zeiten bestimmte Aktionen auslöst. Also ist schon ein einfacher Organizer unmöglich, aber vor allem bei Workflow-ähnlichen Systemen fällt dieses Problem stark ins Gewicht: Es ist nicht möglich, zu bestimmten Zeiten die Weiterverarbeitung eines solchen Workflows anzustoßen und auch nächtliche Batchprozesse sind mit Enterprise JavaBeans bisher nicht umsetzbar gewesen. Hintergrund ist, dass EJBs nicht auf Threads zugreifen dürfen, aber nur dadurch kann man das hier benötigte Warten implementieren. Selbst wenn dies erlaubt wäre, würde immer noch das Problem bestehen, dass die wartenden Threads zum einen Systemressourcen belegen und zum anderen bei einem Servercrash verloren gehen. Dementsprechend ist in EJB 2.1 ein Timer Service definiert. Im wesentlichen bietet der Timer Service die Möglichkeit, ein Bean zu bestimmten Zeiten aufzurufen. Genauer gesagt sind Callbacks an Entity Beans und Stateless Session Beans möglich. Message-Driven Beans sind nicht möglich, da diese kein Interface zum Aufruf haben, sondern nur eine JMS Schnittstelle anbieten. Stateful Session Beans sind ebenfalls nicht sinnvoll, da diese durch Timeouts zu dem Zeitpunkt, zu dem sie aufgerufen werden sollen, möglicherweise schon nicht mehr vorhanden sind. Interessanterweise sind auch Entity Beans mit CMP 1.1 ausgenommen, wofür allerdings keine Begründung gegeben wird.Listing 2
public interface javax.ejb.TimerService {public Timer createTimer(long duration,java.io.Serializable info);public Timer createTimer(long initialDuration,long intervalDuration, java.io.Serializable info);public Timer createTimer(java.util.Date expiration,java.io.Serializable info);public Timer createTimer(java.util.Date initialExpiration,long intervalDuration, java.io.Serializable info);public Collection getTimers();}
Listing 3
public interface javax.ejb.Timer {public void cancel();public long getTimeRemaining();public java.util.Date getNextTimeout();public javax.ejb.TimerHandle getHandle();public java.io.Serializable getInfo();}
TimerService timerService = sessionContext.getTimerService();Timer timer=timerService.createTimer(10000, 5000, "Hallo");// rufe den Bean in 10 s das erste Mal auf und dann alle 5 s// der String "Hallo" wird als zusätzliche Information übergeben
public void ejbTimeout(Timer timer) {String infoObject = (String) timer.getInfo();// hier kann man jetzt endsprechend der Information etwas tunif (...) {timer.cancel();// damit wird der Timer nie wieder ausgelöst werden}}
Wie sind nun die Garantien für den Timer Service? Zunächst müssen die Timer - wenig überraschend - activate/passivate oder load/store der Beans-Zyklen überleben. Schon interessanter ist, dass Timer auch noch nach Server-Abstürzen abgearbeitet werden müssen. Allerdings sind die Garantien in Bezug auf den Aufrufzeitpunkt relativ schwach. Falls nämlich gerade der entsprechende Enterprise JavaBean mit der Abarbeitung einer Business Methode beschäftigt ist, kann kein ejbTimeout() aufgerufen werden, denn innerhalb einer Bean kann immer nur ein Thread aktiv sein. Dies kann dazu führen, dass Aufrufe zu spät erfolgen. Wenn mehrere Timer für eine Bean definiert sind, kann es sogar zu Aufrufen in falscher Reihenfolge kommen oder es kann sein, dass ein Timer noch ausgeführt wird, obwohl er kurz zuvor gecancelt wurde. Die ejbTimeout()-Methode hat übrigens keinen Client-Sicherheitskontext, so dass man für die Sicherheitseinstellungen wieder sinnvollerweise das run-as Tag des Deployment Descriptors bemüht.
In Bezug auf Transaktionen wird empfohlen, RequiresNew zu verwenden. Falls die Transaktion in ejbTimeout() zurückgerollt wird, wird die Methode nochmal aufgerufen. Das kann natürlich dazu führen, dass die Methode unendlich oft aufgerufen wird, ein Problem, das bereits aus JMS-Transaktionen bekannt ist: Es ist also sinnvoll, die Transaktion nur dann zurückzurollen, wenn berechtigte Hoffnung besteht, dass der entsprechende Fehler nur vorübergehend ist, also beispielsweise von einem kurzzeitig ausgefallenen Server herrührt. Natürlich ist eine weitere interessante Frage, was passiert, wenn die Transaktion zurückgerollt wird, in der der Timer erzeugt wurde. Die Antwort ist, dass der Timer dann wieder gelöscht wird. Ähnliches gilt, wenn der Timer in einer zurückgerollten Transaktion gecancelt wurde: In diesem Fall wird dann das Canceln wieder rückgängig gemacht.
Erweiterungen in EJB QL
Die EJB Query Language wurde in EJB 2.0 eingeführt, um die Möglichkeit zu schaffen, Anfragen auf Entity Beans mit Container-Managed Persistence unabhängig vom verwendeten Application Server und der verwendeten Datenbank zu definieren.In EJB 2.1 sind in diesem Bereich im wesentlichen kleine Verbesserungen und Präzisierungen vorgenommen worden. Hier sollen allerdings nur die zwei wirklich neuen Features erläutert werden. Zum einen gibt es jetzt Aggregat Funktionen wie sie aus SQL bekannt sind: COUNT, AVG, MAX, MIN und SUM wurden eingeführt. Diese Funktionen pictureen eine Ergebnismenge auf einen einzelnen Wert ab.
- COUNT gibt die Anzahl der Resultate an und kann auf einzelne Felder oder ganze Tabellen angewendet werden. Als Ergebnis wird ein long zurückgegeben.
- SUM und AVG können auf Zahlenwerte angewendet werden. AVG gibt dabei den Durchschnitt als ein double zurück und SUM die Summe als einzigen Wert in einer Collection des Wrappers des entsprechenden Basisdatentyps zurück.
- MAX und MIN können auf alle sortierbaren Datentypen (Zahlen, String, Zeichen, Dates) angewendet werden. Dabei wird wiederum eine Collection mit einem Element zurückgegeben.
Ebenfalls neu eingeführt wurde die ORDER BY-Klausel, die ebenfalls aus SQL bekannt sein sollte. Mit Hilfe dieses Ausdrucksteils können Ergebnisse einer EJB QL-Anfrage sortiert werden. Dabei sind im wesentlichen zwei Fälle zu unterscheiden: Wenn in der SELECT-Klausel eine komplette Entity Bean steht, also entweder OBJECT(x) verwendet wird oder eine Container-Managed Relation referenziert wird, kann im ORDER BY-Teil ein beliebiges Feld diese Bean verwendet werden. Der andere Fall ist, dass eines oder mehrere Felder einer Bean mit Container-Managed Persistence im SELECT ausgewählt wird. In diesem Fall darf nur eines dieser Felder im ORDER BY auftauchen. Es ist also nicht möglich, nur den Namen eines Kunden im SELECT auszuwählen und dann nach der Kundennummer zu sortieren, da dieses Feld nicht im SELECT-Teil vorkommt. Es gibt bei ORDER BY die Möglichkeit, aufsteigend (ASC) oder absteigend zu sortieren (DESC). Null Werte sind dabei entweder am Anfang oder Ende zu finden: Der Application Server-Hersteller hat hier die Auswahl.
Fazit
EJB 2.1 ist - wie die Versionsnummer schon erkennen lässt - ein kleineres Update, das sich im wesentlichen darauf beschränkt, zum einen auf den Web Services Trend zu reagieren und zum anderen EJB QL abzurunden und mit den Funktionen zu ergänzen, die beim Einsatz in der Praxis bislang schmerzlich vermisst wurden. Wirklich neu ist der Timer Service, der die Einsatzmöglichkeiten von EJB um langlaufende Business Prozesse und Workflows erweitert. Dementsprechend kann man davon ausgehen, dass der EJB 2.1-Standard recht schnell verabschiedet wird und dann auch bald Implementierungen folgen werden.Links und Literatur
- [1] Völter, Schmid, Wolff: Server Component Patterns - Component Infrastructures Illustrated with EJB, Wiley
- [2] JSR-101 Experten Gruppe: Java API for XML-based RPC JAX-RPC 1.0















