Die SessionBeans sind der erste von drei Bean-Typen und sollen in diesem Kapitel genauer betrachtet werden. Die SessionBeans realisieren serverseitge Service- und Session-Komponenten und werden zur Darstellung der Geschäftslogik genutzt. Um die Auswirkungen von Prozessen abzubilden, bedienen sich SessionBeans häufig einer oder mehrerer Entities. Es existieren mit den Stateless-, Stateful- und Singleton-SessionBeans drei verschiedene Ausprägungen der SessionBeans, welche innerhalb dieses Kapitels behandelt werden. Zunächst soll jedoch auf die drei unterschiedlichen Szenarien für den Zugriff auf eine SessionBean eingegangen werden.
Eine Möglichkeit stellt der lokale Zugriff dar. Bei einem lokalen Zugriff befinden sich sowohl der Aufrufer als auch die Bean im selben Prozess bzw. derselben JVM, weshalb die Übergabe der Parameter und Ergebniswerte als Objektreferenz durchgeführt werden kann (Call-By-Reference-Semantik)
und aufgrund der Objektreferenz eine Serialisierung wie bei einem Remote-Zugriff nicht benötigt wird. Potentielle Clients für ein solches Szenario sind andere EJBs oder andere JEE-Komponenten, die innerhalb desselben Applikations-Servers laufen. Um ein Zugriff auf lokale Beans zu ermöglichen,
muss die SessionBean ein sogenanntes Local-Interface bereitstellen und implementieren, welches die Sicht des lokalen Clients auf die SessionBean definiert. Für die Deklaration eines Interfaces als Local-Interface wird die Annotation @Local
verwendet.
Über den entfernten (remote) Zugriff kann ein Client entfernte synchrone Aufrufe ausführen. Remote bezeichnet dabei ein Szenario, bei dem der Aufrufer und die SessionBean in der Regel in unterschiedlichen Prozessräumen bzw. unterschiedlichen virtuellen Maschinen auf unterschiedlichen Rechnern
existieren. Damit eine Bean remote erreichbar ist, muss sie ein Remote-Interface bereitstellen und implementieren. Die Parameter der Methoden werden serialisiert und als Kopie an die SessionBean übertragen (Call-By-Value-Semantik). Über Remote-Interfaces ist es nicht möglich, Datenbankverbindungen,
Datenströme oder Datei-Referenzen zu übergeben. Für die Deklaration eines Interfaces als Remote-Interface wird die Annotation @Remote
verwendet.
Das letzte Szenario ist der Zugriff über einen Webservice. Diese Zugriffart ist eine besondere Art des Remote-Zugriffs und ermöglicht ein Kommunizieren zwischen Client- und Server-Applikationen, die in unterschiedlichen Programmiersprachen wie z.B. C++, Java oder C# vorliegen. Allerdings sind
Funktionen wie die Transaktionssteuerung oder die Zugriffskontrolle direkt nicht mehr möglich. Für diese Art des Zugriffs muss ein Webservice-Endpunkt erstellt werden, sodass Methoden von beliebigen SOAP-Clients aufrufbar sind.
Allgemein stellt eine stateless SessionBean Dienstfunktionen zur Verfügung, für die sie keine Informationen über den Zustand des Aufrufers kennen muss, merkt sich zudem keine Client-spezifischen Kontextinformationen und kann von verschiedenen Clients verwendet werden. Da keine Informationen gespeichert werden können, ist sie nicht von anderen SessionBeans der gleichen Klasse unterscheidbar und besitzt auch keine eigene Identität. Bei jedem Aufruf von Methoden der stateless SessionBean müssen alle Informationen als Parameter übergeben werden, die für die Abarbeitung des Aufrufs benötigt werden. Dieser Bean-Typ kann vom Applikationsserver in einem Pool beliebiger Größe verwaltet werden. Der Pool definiert in diesem Kontext eine Menge von Bean-Instanzen, die keinem Client direkt zugeordnet sind. Wird durch einen Client auf eine stateless SessionBean zugegriffen, so nimmt zunächst ein Proxy-Objekt den Aufruf entgegen. Im Anschluss wird eine Bean dem Pool entnommen und zugeordnet. Ist die Bearbeitung abgeschlossen wird die Bean wieder in den Pool zurück gelegt. Dies ermöglicht eine ressourcenschonende Arbeitsweise auf dem Server. Die nachfolgende Abbildung zeigt den Lebenszyklus einer stateless SessionBean.
Zunächst befindet sich die Bean im Zustand "Does not Exist". Um in den Zustand "Method Ready Pool" zu wechseln, muss zunächst eine neue Instanz der Bean-Implementierung mittels des Default-Konstruktors erzeugt werden. Im nächsten Schritt werden die Abhängigkeiten der Bean mittels DI aufgelöst.
Im dritten und letzten Schritt wird die Bean über die Erzeugung informiert, in dem die Callback-Methode PostConstruct aufgerufen wird. Der Zustand "Method Ready Pool" sagt aus, dass die Bean initialisiert und zum Pool hinzugefügt wurde. Der Aufbau einer stateless SessionBean entspricht der einer
normalen Java-Klasse mit gewünschten Schnittstellen. Um diese als Bean kenntlich zu machen, muss die Annotation @Stateless
hinzugefügt werden. Die Interfaces wiederum sind einfach POJIs (Plain Old Java Interfaces) mit den Annotationen @local
oder @remote
, je nach Typ von Interface.
Bei vielen Arten von Geschäftsprozessen wird es bei einer Interaktion mit einem Client verlangt, einen spezifischen Zustand über mehrere Schritte hinweg aufzubauen oder zu halten. Für diese Art von Prozessen eignen sich die stateful SessionBeans. Dieser Bean-Typ besitzt ein eigenes Gedächtnis und eine 1:1 Zuordnung zwischen dem Client und einer einzelnen Bean-Instanz. Ein gängiges Beispiel ist die Realisierung eines Warenkorbs unter Zuhilfenahme der stateful SessionBean. Der Interaktionsvorgang mit einem Client kann sich über mehrere Aufrufe hinweg erstrecken, ohne dass die Bean dabei "den Faden verliert". Die Zustandsbehaftung wird durch eine Vergabe einer eindeutigen ID umgesetzt, an der auch die Unterscheidung der verschiedenen stateful SessionBeans vorgenommen wird. Die nachfolgende Abbildung zeigt den Lebenszyklus einer stateful SessionBean.
Der "Does not Exist" Zustand unterscheidet sich nicht von dem der stateless SessionBean. Auch der Aufruf des Default-Konstruktors, die Injizierung mittels DI und der Aufruf der mit @PostConstruct
annotierten Methode unterscheiden sich nicht. Anders als bei der stateless SessionBean entscheidet bei der
stateful Variante nicht der Applikationsserver wann die Bean erzeugt wird, sondern indirekt der Client, in dem er via Lookup den Prozess der Initialisierung anstößt. Jedes Lookup erzeugt somit eine neue Bean-Instanz. Im Status "Method Ready" ist die Bean initialisiert, einem Client zugeordnet und kann Methodenaufrufe
entgegennehmen. Die statefull SessionBean verlässt diesen Zustand wenn:
Erfolgt ein Methodenaufruf transaktional, wechselt die Bean in den Zustand "Method Ready in Transaktion". Innerhalb dieses Zustands nimmt die Bean an der Transaktion teil. Dies bedeutet, dass sie nicht außerhalb der Transaktion oder innerhalb einer weiteren gerufen werden darf. Ist die Transaktion beendet,
kehrt die Bean in den Zustand "Method Ready" zurück. Falls der Server zur Schonung der Ressourcen die Bean auslagert, wechselt diese in den Zustand "Passive". Innerhalb dieses Zustands arbeitet die Bean keine Methodenaufrufe ab. Trifft ein Methodenaufruf ein, wechselt die Bean wieder in den Zustand "Method Ready" und
arbeitet den eingegangenen Aufruf ab. Wie auch schon die stateless Varianten, besteht auch diese Bean aus einem Remote- oder/und einem Local-Interface sowie aus einer einfachen Java-Klasse, nur diesmal mit der Annotaion @Stateful
. Nach Ausführung einer innerhalb der Bean-Implementierung mit der Annotation @Remove
versehenen
Methode wurde der Endzustand des Geschäftsprozesses erreicht und im Anschluss wird die Instanz der stateful SessionBean mit dem Client-spezifischen Zustand vom Applikationsserver entfernt. Eine stateful SessionBean kann nicht über einen Webservice-Endpunkt angesprochen werden, da zurzeit kein Standard für statusbehaftete Webservices existiert.
Der letzte der drei Bean-Typen ist die singleton SessionBean. Dieser Typ unterscheidet sich grundsätzlich von den anderen SessionBean-Typen, da anstelle mehrerer Instanzen bei diesem Bean-Typ nur eine einzige Instanz im Container enthalten sein darf. Alle Client-Anfragen werden folglich nur über eine Bean-Instanz abgearbeitet, welche erst beim Herunterfahren des Servers oder beim Undeployment zerstört wird. Die Abbildung zeigt den Lebenszyklus der singleton SessionBean.
Exakt wie bei den anderen Bean-Arten existiert diese Bean im Zustand "Does Not Exist" nicht. Im Zustand "Method Ready" kann die Bean Methodenaufrufe entgegennehmen. Anders als bei den vorherigen Bean-Typen, existiert in diesem Zustand weder ein Pooling (stateless) noch eine konkrete Zuordnung mit einem Client (stateful). Alle Clients, welche eine Referenz auf die singleton SessionBean halten, können Methoden der Bean aufrufen. Eine wichtige Frage bei diesem Bean-Typ ist die Regelung und Steuerung des parallelen Zugriffs auf die einzige Bean-Instanz. Der EJB-Standard erlaubt für singleton SessionBeans, dass zeitgleich mehrere Threads auf eine Bean-Instanz zugreifen können und löst sich somit vom Single-Thread-Modell, bei dem nur ein Thread in der Bean-Instanz stehen darf. Allerdings entsteht so ein konkurrierender Zugriff der Threads auf Instanz-Variablen, der synchronisiert werden muss. Für diese Aufgabe stehen zwei unterschiedliche Varianten zur Verfügung, die im Folgenden näher erläutert werden.
Wird diese Art der Zugriffsverwaltung gewählt, so übernimmt der EJB-Container die Steuerung. Jeder Thread, der die singleton Bean betreten möchte, benötigt einen Lock, der das Recht zum Aufruf einer Methode darstellt. Die Annotation @Lock
kann für einzelne Methoden festgelegt werden, sodass mehrere Threads lesend auf die Bean zugreifen zu können,
da sie die Konsistenz selbiger nicht verändern. Wird der Zustand der Bean jedoch verändert, sodass Inkonsistenten auftreten können, muss sichergestellt werden, dass immer nur ein Thread die jeweilige Methode aufruft. Der Annotation @Lock
kann mittels eines Attributs mitgeteilt werden, ob es sich um einen lesenden (read) oder verändernde (write)
Methode handelt. Die Annotation kann für Business-Interfaces, öffentliche Methoden der Bean ohne Schnittstelle und für Webservice-Methoden definiert werden. Wir an einer Methode kein Lock verwendet, wird diese als schreibend eingestuft, sodass gleichzeitige Zugriffe parallel abgearbeitet werden. Erhält ein Thread den angeforderten Lock nicht,
blockiert dieser bis der Lock zugeteilt wird. Über die Annotation @AccessTimeout
kann eine Zeitspanne definiert werden, die ein Thread wartet bevor er mit einer Exception zurückkehrt. Um Deadlocks bei sich gegenseitig aufrufenden Methoden zu vermeiden, können Methoden andere Methoden nur aufrufen, wenn deren Lock-Definition nicht stärker ist
als die eigene Lock-Definition. Beispielsweise darf eine schreibende Methode sowohl schreibende als auch lesende Methoden aufrufen, wohingegen eine lesende Methode nur lesende Methoden aufrufen darf. Ein Write-Lock wird nur dann ausgeteilt, wenn kein anderer Lock (read oder write) von einem Thread für die Bean gehalten wird.
Bei dieser Variante wird der parallele Zugriff auf die Bean programmatisch vorgenommen. Der Entwickler muss bei dieser Variante Java-Bordmittel zur Threadsynchronisierung einsetzen, beispielsweise synchronized und volatile. Mittels des Bean-Managed-Concurrency ist eine feinere granulare Synchronisierung der aufrufenden Thread möglich, wodurch eine höhere Parallelität erreicht werden kann. Zeitgleich ist die Gefahr versteckte Fehler einzubauen, um ein vielfaches höher, so dass eine solche Lösung intensiver getestet werden sollte.
[01] H.Rupp: EJB3 für Umsteiger - Neuerungen und Änderungen gegenüber dem EJB-2.X-Standard, dpunkt.verlag, Heidelberg, 2007
[02] W. Everling, J. Leßner: Enterprise JavaBeans 3.1: Das EjB3-Praxisbuch für Ein- und Umsteiger, 2. Auflage, Carl Hanser Verlag, München, 2011
[03] O. Ihns, S. Heldt, et. al: EJB 3.1 professionell - Grundlagen und Expertenwissen zu Enterprise JavaBeans 3.1 - inkl. JPA 2.0, 2., aktualisierte und erweiterte Auflage, dpunkt.verlag, Heidelberg, 2011