Application
Exception vs System Exception
Una eccezione applicativa è una qualsiasi eccezione
che non estende la java.lang.RuntimeException o la java.rmi.RemoteException.
Sono invece eccezioni di sistema tutte quelle che derivano
dalla java.lang.RuntimeException, compresa la EJBException.
Ogni volta che all'interno di un metodo viene lanciata
una eccezione di sistema, l'eventuale transazione in
corso viene annullata e viene eseguita una rollback
di sistema. Le eccezioni applicative invece non causano
una rollback della transazione in corso.
Questa semplice regola è alla base di tutto il
meccanismo di EJB relativo alla gestione delle eccezione
e della interazione con le transazioni.
Figura
1 - La gerarchia delle eccezioni ed implicazione
sulle
eccezioni applicative e di sistema
System Exception
Le eccezioni di sistema sono tutte quelle che derivano
dalla RuntimeException, compresa quindi anche la EJBException.
Tutte le eccezioni di questo tipo non devono essere
necessariamente dichiarate nella clausola throws nella
firma del metodo, così come non è necessario
inserirle all'interno di un blocco try-catch.
Se l'eccezione si verifica all'interno del metodo che
ha iniziato la transazione allora la rollback verrà
immediatamente effettuata dal container. Se invece il
metodo è un metodo del client, allora la transazione
verrà annullata e verrà propagata notifica
al client invocante.
Eccezioni di questo tipo sono gestite direttamente dal
container che effettua le seguenti operazioni:
- Rollback
della transazione
- Log
della eccezione per notificare eventualmente l'amministratore
di sistema: la specifica in questo caso non definisce
come la notifica debba essere eseguita, particolare
che è lasciata alla particolare implementazione.
JBoss ad esempio aggancia i messaggi di questo tipo
ad un appender Log4J che poi viene configurato in
modo opportuno dall'amministratore di sistema in
modo da dirigere i messaggi verso una coda JMS, una
casella di posta, o un file di log.
- L'istanza
dell'EJB viene dereferenziata e mandata al garbage
collector. In questo caso infatti si presume che lo
stato mantenuto nel bean non sia coerente e quindi
inutile per l'utilizzo.
Non
vi è differenza all'interno di tale meccanismo
a seconda che le eccezioni si siano verificate all'interno
di un metodo di callback (ejbStore(), ejbLoad(), e così
via) o all'interno di un metodo di business logic (tipicamente
quelli di un session).
Relativamente al terzo punto, l'eliminazione del bean,
l'impatto di questo genere di operazione dipende molto
dal tipo di bean: nel caso di session bean stateless
ed entity, non essendo dedicate ad un particolare client,
esse potranno essere rimosse dal container senza particolari
conseguenze. Il client quindi in questo caso non percepisce
questo evento.
Nel caso di session stateful invece l'impatto è
molto più forte, dato che essi sono dedicati
ad un singolo client e ne mantengono lo stato. In questo
caso quindi la rimozione del bean provoca la distruzione
del cosiddetto conversational state con il client: successive
invocazioni da parte di questo provocano il lancio di
una eccezione di tipo NoSuchObjectException, figlia
della RemoteException.
Infine nel caso di MDB una eccezione all'interno del
metodo onMessage() o in uno dei metodo di callback,
provoca la rimozione del bean.
Se la transazione è gestita dal bean (BMT), il
messaggio potrebbe essere nuovamente recapitato a seconda
di quando il container notifica il ricevimento del messaggio.
Nel caso di CMT la transazione viene annullata ed il
messaggio nuovamente inviato dal container a seconda
della implementazione.
Il client di un session o di un entity riceve sempre
una RemoteException: se il client ha iniziato la transazione,
l'eccezione di sistema lanciata dal metodo del bean,
viene wrappata all'interno di una TransactionRolledbackException
in modo da notificare in modo più esplicito il
client che si è verificata una rollback.
Nel caso in cui siano utilizzate le interfacce locali
per l'invocazione, in EJB 2.0 quindi, ai client viene
notificata una EJBException. Anche in questo caso se
è il client che ha iniziato la transazione, esso
riceverà una TransactionRolledbackException.
In tutti gli altri casi (sia CMT che BMT) l'eccezione
viene ripropagata al client invocante tramite una EJBException.
Nel caso in cui l'eccezione si verifichi in un sottosistema
gestito dal bean (SQLException o JMSException se i sottosistemi
sono JDBC o JMS), si dovrebbe sempre generare una EJBException,
anche se in genere il progettista decide di gestire
questi casi anomali agendo in modo da far rientrare
il problema, oppure notificando il client con un messaggio
personalizzato, utilizzando quindi una eccezione applicativa.
Questa scelta però deve essere fatta solo quando
si ha completa conoscenza del funzionamento del sistema
e delle ripercussioni derivanti dall'insorgere di una
eccezione verificatasi nel sottosistema. In tutti i
casi ambigui è bene usare sempre la soluzione
di default basata sul rilancio di EJBException.
Si faccia attenzione che tutti i metodi di callback
dichiaravano nella specifica 1.0 eccezioni di tipo RemoteException:
questa soluzione è stata deprecata a partire
dalla 1.1.
Application Exceptions
Le eccezioni applicative in genere sono lanciate in
concomitanza di errori di business logic, e sono sempre
rinviate verso il client senza che ci sia alcun meccanismo
di wrapping da parte del container. Normalmente non
generano alcuna rollback ed il client quindi non ha
possibilità di intervenire o di recuperare lo
stato dopo una eccezione di questo tipo. Eccezioni di
questo genere vengono utilizzate dal programmatore se
ad esempio un metodo viene invocato ma non tutti i
parametri sono stati forniti al bean, oppure se una
determinata operazione non può essere eseguita
perché lo stato del bean non è congruo
con l'operazione stessa: in tal caso il proseguo delle
operazioni porterebbe ad una eccezione di sistema, eventualità
tipicamente più grave, su cui il programmatore
non ha possibilità di intervenire.
Ad esempio il pagamento di un acquisto senza che sia
stato fornito un numero di carta di credito valido,
oppure un login senza che la password sia stata valorizzata.
Per questo le eccezioni applicative possono essere
considerate casi meno gravi ed vengono spesso utilizzate
come sistemi di notifica di messaggi particolari dallo
strato di business logic verso il client. Se è
questo lo scenario di utilizzo delle eccezioni applicative,
tali controlli devono essere effettuati prima che nel
metodo del bean sia eseguita una qualsiasi operazione
transazionale da e verso il sottosistema, in modo che
il client possa essere avvertito e semmai ritentare
l'operazione remota fornendo tutte le informazioni ed
i dati necessari, prima che i dati nel sistema cambino.
I metodi di business logic possono lanciare ogni tipo
di eccezione applicativa, eccezioni che devono essere
definite nelle firme dei metodi nelle interfacce remote
e locali, così come nelle implementazioni dei
metodi nella classe dell'EJB.
Anche i metodi di callback possono rilanciare alcune
eccezioni definite in javax.ejb che sono dette standard
application exceptions: CreateException DuplicateKeyException,
FinderException, ObjectNotFoundException, RemoveException
che sebbene fornite dal sistema sono considerate eccezioni
applicative a tutti gli effetti e vengono rilanciate
al client senza nessuna intermediazione da parte del
container e senza rewrapping sotto forma di RemoteException.
Non necessariamente tali eccezioni provocano una rollback
della transazione, offrendo al client la possibilità
di ritentare l'operazione. Tali eccezioni sono lanciabili
sia dal bean (nel caso ad esempio di entity CMP) ma
anche direttamente dal container.
Standard Application Exceptions e loro significato
Di seguito è riportata una breve analisi delle
eccezioni applicative standard. Tutte e quattro possono
essere lanciate dal container la persistenza è
di tipo CMT, ma può essere anche uno dei metodi
creazionali ejbCreate() o ejbPostCreate() a lanciarla
esplicitamente.
CreateException
Viene generata dal metodo create() della interfaccia
remota: può essere lanciata dal container. La
sua generazione indica che si è verificato un
errore grave che impedisce la creazione del bean stesso
(ad esempio parametri non validi o non completi). In
un CMT se il container lancia questa eccezione non si
ha la certezza del rollback della transazione e si deve
in tal caso procedere in modo manuale ad effettuare
i dovuti controlli.
Se l'integrità dei dati è un aspetto fondamentale
dovrà essere annullata la transazione prima di
lanciare questa eccezione.
DuplicateKeyException
E' un sottotipo della precedente e viene lanciata all'interno
del metodo ejbCreate(). Indica che la chiave primaria
a cui è associato un entity esiste già
nel database. L'entity non può essere creato.
FinderException
Questa eccezione viene lanciata dai metodi di ricerca
presenti nella interfaccia home.
Indica che la ricerca ha generato un errore per un motivo
applicativo qualsiasi (argomenti non validi o altro).
Questo evento deve essere associato solamente nel caso
di errori di ricerca, non di oggetti non trovati (caso
per il quale si deve utilizzare l'eccezione successiva).
ObjectNotFoundException
Questa eccezione deve essere lanciata se non viene
trovato nessun record nel database corrispondente ai
parametri di ricerca utilizzati. Più precisamente
nel caso di metodi a ricerca multipla deve essere restituita
una collection vuota, mentre per i metodi a ricerca
singola questa eccezione deve essere rilanciata. Genericamente
la transazione non viene annullata.
Non si dovrebbe utilizzare questa eccezione in tutti
quei casi in cui si sia verificato un errore applicativo
dovuto a motivi particolari, come nel caso precedente.
RemoveException
Questa
eccezione viene generata dai metodi remove() della interfaccia
locale e remota in corrispondenza di problemi all'atto
della cancellazione.
La transazione non è detto che sia annullata,
e devono essere fatti controlli espliciti in tal senso.
Conclusione
In
questo breve articolo si sono affrontati alcuni temi
che dovrebbero essere ormai noti alla maggior parte
dei programmatori EJB, ma che mancavano alla serie dedicata
ad EJB pubblicata su queste pagine.
Per chi fosse interessato a maggiori approfondimenti
si rimanda alla lettura di [] che offre una buona base
teorico pratica sull'argomento.
Bibliografia
[EJB] "Enterprise JavaBeans" di Richard Monson
Haefel, Ed. Hops tradotto dalla redazione di MokaByte.
|