MokaByte 80 - Dicembre 2003 

EJB e la gestione
delle eccezioni
Interazione fra le eccezioni e le
transazioni all'interno di un container EJB

di
Giovanni Puliti
In questo articolo tratteremo il tema delle eccezioni generate da EJB e come queste si riflettano sulla gestione delle transazioni da parte del container. Dato che una delle funzionalità più importanti del framework EJB è proprio quello della gestione automatica delle transazioni, e dato che la loro gestione dipende in modo molto stretto dall'insorgere delle eccezioni e da come queste poi vengano gestite dal container, l'argomento qui trattato, sebbene non nuovissimo è di particolare importanza per tutti coloro che si avvicinano ad EJB per la prima volta ma anche per chi volesse ripassare un po' l'argomento in modo metodico.

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.

 
x
MokaByte® è un marchio registrato da MokaByte s.r.l. 
Java®, Jini® e tutti i nomi derivati sono marchi registrati da Sun Microsystems.
Tutti i diritti riservati. E' vietata la riproduzione anche parziale.
Per comunicazioni inviare una mail a info@mokabyte.it