Das ist einer der am haeufigsten missverstandenen Teile des EJB-Modells - und einer der Gruende, warum "EJB verstehen" oft schwerer faellt, als es sein muesste. Dieses Projekt zeigt drei konkrete Exception-Klassen, die zusammen das gesamte Bild ergeben.
Der EJB-Container behandelt jede aus einer Business-Methode geworfene Exception nach einem von zwei grundverschiedenen Mustern:
| Application Exception | System Exception | |
|---|---|---|
| Bedeutung | ein erwarteter, fachlicher Fall | ein unerwarteter, technischer Fehler |
| Transaktion | je nach @ApplicationException(rollback=...) |
immer automatischer Rollback |
| Fuer Remote-Clients | bleibt die urspruengliche Exception-Klasse | wird in EJBException/RemoteException verpackt |
| Aufrufer-Erwartung | soll gezielt behandelt werden | i.d.R. nicht sinnvoll behandelbar |
| In diesem Projekt | PolicyNotFoundException, UnderwritingRejectedException |
LegacySystemUnavailableException |
Beide erweitern bewusst die checked Exception (nicht RuntimeException) - jede
Methode auf PolicyManagementLocal/PolicyManagementRemote, die sie werfen kann,
deklariert das explizit in ihrer Signatur (throws PolicyNotFoundException). Der Compiler
zwingt Aufrufer damit, sich mit dem Fall auseinanderzusetzen.
PolicyNotFoundException - @ApplicationException(rollback = true). Eine
angefragte Police existiert nicht. Das deutet meist auf falsche/veraltete Daten beim
Aufrufer hin - alles, was die aktuelle Transaktion bereits geschrieben hat, soll verworfen
werden.UnderwritingRejectedException - @ApplicationException(rollback = false). Ein
Antrag wird von den Zeichnungsregeln abgelehnt. Das ist ein regulaeres fachliches
Ergebnis, kein Fehler - der (teilweise erfasste) Antrag samt Ablehnungsgrund soll
durchaus in der Datenbank stehen bleiben, damit man spaeter nachvollziehen kann, was
abgelehnt wurde und warum.Wichtig: Der Spezifikations-Default fuer @ApplicationException ist rollback = false.
Beide Klassen in diesem Projekt setzen den Wert trotzdem explizit, damit die Absicht im
Code steht und nicht implizit aus der Spezifikation erinnert werden muss.
LegacySystemUnavailableExceptionErweitert bewusst RuntimeException (unchecked) und traegt keine
@ApplicationException-Annotation. Damit verhaelt sie sich exakt wie eine System Exception:
javax.ejb.EJBException (die die urspruengliche Exception als getCausedByException()
traegt).RuntimeException),
sollen es aber im Regelfall nicht tun - ein nicht erreichbares DB2/Oracle/MQ-System ist
kein Fall, den fachlicher Code sinnvoll "reparieren" kann.Verwendet wird sie durchgaengig in den Datenzugriffsschichten
(LegacyContractDao, JndiLookupHelper, AuditLogDao - wobei letztere sie bewusst NICHT
wirft, siehe docs/10-ejb-konzepte/05-interceptors.md), um checked SQLException/
NamingException in ein einheitliches, unchecked Format zu uebersetzen.
Ohne sie muesste jeder Aufrufer jeder EJB-Methode potenziell mit jeder denkbaren
Exception rechnen - fachlichen wie technischen. Die Trennung macht die Business-Interfaces
selbst zur Dokumentation: throws PolicyNotFoundException sagt dem Aufrufer exakt, mit
welchen fachlichen Ausnahmefaellen zu rechnen ist, waehrend technische Ausfaelle (DB weg,
MQ weg, JNDI kaputt) einheitlich und automatisch behandelt werden, ohne die
Methodensignaturen mit jeder denkbaren technischen Fehlerquelle zu ueberladen.