← Zur Uebersicht

Session Beans: Stateless, Stateful, Singleton

Session Beans sind der Kern der Geschaeftslogik in diesem Projekt. Es gibt drei Spielarten, alle in novaris-policy-core vertreten - jede loest ein anderes Problem.

Stateless Session Beans (@Stateless)

Beispiele: PolicyManagementBean, PremiumCalculationBean, PolicyRepository.

Eine Stateless-Bean-Instanz haelt keinen Zustand zwischen zwei Methodenaufrufen - zwischen Aufruf 1 und Aufruf 2 (auch vom selben Client) kann der Container eine voellig andere physische Instanz aus einem Pool verwenden. Das ist der Grund, warum Stateless Beans so gut skalieren: der Container muss nur einen kleinen Pool von Instanzen vorhalten und kann sie fuer beliebig viele Clients wiederverwenden, statt pro Client eine eigene Instanz zu brauchen.

Konsequenz fuer den Code: Instanzfelder sind entweder container-injizierte, threadsicher wiederverwendbare Referenzen (@EJB, @Resource, @PersistenceContext - siehe PolicyManagementBean.policyRepository) oder duerfen gar keinen mutable Zustand tragen. Alle Daten, die zwischen zwei fachlichen Schritten ueberleben muessen, muessen entweder in der Datenbank landen oder - falls es sich um einen mehrstufigen Dialog handelt - in eine Stateful Session Bean wandern (siehe unten, PolicyApplicationWizardBean).

Stateful Session Beans (@Stateful)

Beispiel: PolicyApplicationWizardBean.

Eine Stateful-Bean-Instanz ist exklusiv an einen Client gebunden, sobald sie erzeugt wurde - der Container garantiert, dass jeder weitere Methodenaufruf ueber dasselbe Bean-Referenz-Handle wieder bei genau dieser Instanz (und ihrem Instanzzustand) ankommt. Das macht sie zum natuerlichen Werkzeug fuer mehrstufige Dialoge (hier: Antrag → Deckung → Underwriting → Bindung), ohne den Zwischenstand explizit persistieren zu muessen.

Der Preis: Skalierbarkeit. Jeder Client "besetzt" eine eigene Instanz (samt Speicher) fuer die Dauer des Dialogs. Der Container kann inaktive Instanzen passivieren (Zustand serialisieren, aus dem Speicher entfernen) und bei Bedarf wieder aktivieren (deserialisieren) - siehe @PrePassivate/@PostActivate in PolicyApplicationWizardBean. Deshalb muessen alle nicht-transienten Instanzfelder serialisierbar sein.

@Remove: Markiert Methoden, nach deren (erfolgreichem) Aufruf der Container die Instanz endgueltig entfernt - sonst wuerde die Bean (und ihr Speicher) bis zum @StatefulTimeout "leben". retainIfException = true (siehe PolicyApplicationWizardBean.bindPolicy()) sorgt dafuer, dass eine fachliche Ablehnung die Instanz nicht zerstoert, damit der Client mit korrigierten Daten erneut versuchen kann.

Warum praktisch nie @Remote: Ein Remote-Stateful-Handle muesste ueber Prozess-/Netzwerkgrenzen hinweg gueltig bleiben - inklusive der oben beschriebenen Passivierung auf der Serverseite. Das ist historisch fehleranfaellig, und die allermeisten Java-EE-Projekte (auch dieses) beschraenken Stateful Beans deshalb auf @Local.

Singleton Session Beans (@Singleton)

Beispiel: PolicyRenewalTimerBean.

Es gibt in der gesamten Anwendung genau eine Instanz. Sinnvoll fuer Zustand oder Verhalten, das anwendungsweit nur einmal existieren soll - hier: der naechtliche Verlaengerungs-Check. @Startup weist den Container an, die Instanz schon beim Anwendungsstart zu initialisieren (statt lazily beim ersten Zugriff), damit z.B. @Schedule-Timer garantiert von Anfang an registriert sind.

Singleton Beans haben ein eigenes Nebenlaeufigkeitsmodell (Concurrency Management) - per Default Container-Managed mit impliziten Schreib-Sperren auf Business-Methoden (@Lock(WRITE) ist der Default). PolicyRenewalTimerBean nutzt in dieser Ausbaustufe keine von aussen aufrufbaren Business-Methoden (nur @Schedule/@Timeout-Callbacks), daher ist das Thema hier nur am Rande relevant - wichtig wird es, sobald ein Singleton auch normale Business-Methoden fuer andere Beans anbietet.

Local vs. Remote: die wichtigste EJB-Unterscheidung

@Local @Remote
Aufrufer dieselbe JVM ggf. andere JVM/anderer Server
Parameteruebergabe by reference (dieselbe Objektreferenz) by value (serialisierte Kopie, RMI/IIOP)
Voraussetzung keine Parameter/Rueckgabewerte muessen Serializable sein
Ueblich bei fast allen Session Beans selten, nur fuer echte Systemgrenzen

PolicyManagementBean implementiert bewusst beide Sichten (PolicyManagementLocal/PolicyManagementRemote), um den Unterschied konkret zu zeigen - siehe die Javadoc-Kommentare beider Interfaces. In der Praxis ist die Local-Sicht die weit haeufiger genutzte; Remote lohnt sich nur, wenn tatsaechlich ein separat deploytes System (anderer Server, ggf. anderer Hersteller) zugreifen soll.

@RolesAllowed und @DeclareRoles

Deklarative Security: statt in jeder Methode manuell zu pruefen "darf dieser Nutzer das?", deklariert man die erlaubten Rollen als Annotation, und der Container prueft sie vor dem eigentlichen Methodenaufruf (technisch: als eine Art eingebauter Interceptor). @DeclareRoles auf Klassenebene listet alle in der Klasse ueberhaupt verwendeten Rollennamen (relevant, wenn eine Rolle nur programmatisch per EJBContext.isCallerInRole(...) abgefragt wird und deshalb nicht automatisch aus den @RolesAllowed-Annotationen ableitbar waere). Die eigentliche Zuordnung "welcher Benutzer/welche Gruppe hat welche Rolle" ist kein Java-Code, sondern reine Serverkonfiguration - siehe docs/10-ejb-konzepte/07-jndi-und-packaging.md und (ab Phase 4) die WebSphere- Konfigurationsdoku.

⌂ Cockpit