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)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)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)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 |
@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 @DeclareRolesDeklarative 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.