Primi Passi con gli EJB3

Gli Enterprise Java Beans (EJB) sono componenti scritti in Java che realizzano la logica di business (application layer) di un’applicazione. Vivono all’interno di un EJB Container (un runtime environment nell’Application Server) che ne gestisce il ciclo di vita e le sue invocazioni. Il container fornisce anche meccanismi ausiliari, come la transazionalità e la sicurezza, in modo trasparente ai client. Gli EJB realizzati secondo le specifiche J2EE possono essere dispiegati (deploy) in diversi application server J2EE compliant. Sono quindi portabili e possono essere aggregati per realizzare nuovi EJB.

Gli EJB possono essere classificati in tre categorie:

  • Session Bean
  • Entity Bean
  • Message Drive Bean

Session Bean

Sono classi che implementano la logica di business dell’applicazione e costituiscono il punto di accesso con cui i client possono interagire con tale logica, realizzando delle sessioni di comunicazione. Dal punto di vista del client, ogni Session Bean è associato ad uno ed uno solo Client. Lo stato dell’interazione con un client (lo stato Session Bean) è transiente, ovvero quando termina la sessione a cui è associato, il bean viene distrutto, insieme con lo stato. Una sessione termina quando il client esplicitamente la rimuove oppure per un periodo di inattività superiore ad un tempo prefissato.

Esistono due tipi di Session Bean: stateless e stateful.

Stateless Non mantengono lo stato della sessione con il client. Per tale motivo il container può assegnare un qualsiasi session EJB stateless già esistente a qualsiasi client e, per risparmiare memoria, può anche associare lo stesso bean a più client. Questa politica di allocazione dipende dall’implementazione dell’EJB Container ed è totalmente trasparente ai client che “credono” di avere uno stateless session bean dedicato.
 Stateful Mantengono lo stato conversazionale nella sessione con il client per cui ogni Stateful Session Bean è associato sempre ad un solo client.

Entity Bean

Rappresentano logicamente gli oggetti di business di una applicazione distribuita. Sono oggetti persistiti opportunamente mappati nelle tabelle di un database relazionale, e molto spesso l’istanza di un entity bean corrisponde ad una tupla della corrispondente tabella. La loro definizione prevedono l’esistenza di proprietà definite chiavi primarie per essere identificati univocamente e possono essere legati ad altri entity bean da relazioni di tipo: one-to-one, one-to-many, many-to-one e many-to-many.

Di norma tale bean possono essere condivisi tra diversi client perché questi possono voler accedere agli stessi dati. E’ importante quindi che il container fornisca un servizio per la gestione delle transazioni. La persistenza di tali oggetti può essere gestita i due modalità distinte:

  • Bean Managed Persistent Bean (MDB): il bean implementa anche il codice necessario alla interazione con il database;
  • Container Managed Persistent Bean (CMB) è il ocntainer, che in base alla configurazione del bean, si occupa di interagire con database.

Message Drive Bean

Di fatto sono molto simili ai Session Bean, quindi usufruiscono di tutti i servizi del container, con la differenza che non hanno metodi che possono essere invocati da un applicazione client, ma gestiscono la comunicazione con altri sistemi o all’interno dello stesso container tramite lo scambio di messaggi asincroni e l’utilizzo del protocollo JMS.

EJB3

Le principali differenza tra la versione 3 della specifica EJB e le precedenti versioni sono:

  • l’utilizzo delle annotation che si sostituiscono alle interfacce;
  • l’eliminazione della interfaccia home;
  • l’introduzione delle Java persistence Api (JPA) per l’accesso al database;
  • miglioramento delle performance dovuto all’utilizzo di oggetti POJO annotati piuttosto che descrittori xml;
  • utilizzo della dependency injection per l’iniezione delle dipendenze.

Il Progetto

Consideriamo una applicazione J2EE che implementa un ATM (Automated Teller Machine). Semplificando notevolmente l’applicativo considerando tre semplici componenti:

  • un DAO per l’accesso ai conti correnti che per semplicità saranno interamente gestiti in memoria;
  • un service per l’implementazione della logica di deposito e prelevamento del denaro;
  • un bean di sessione per  l’iinterfacciamento con l’utente.

La relazione tra questi tre componenti è evidenziata nel diagramma UML mostrato nell’immagine seguente:bank-ejb-uml

Generiamo quindi una applicazione denominata ejb-bank di tipo Maven ed inseriamo le dipendenze necessarie.

La prima dipendenza contiene classi ed interfacce per l’implementazione degli EJB, ovvero definiscono il contratto tra EJB e client, e tra EJB e container. La secondo contiene le interfacce che definiscono la dependency injection (CDI).

Cominciamo l’implementazione dell’ATM descrivendo il componente BankAccountDao. Trattandosi di un componente che non ha logica applicativa, ma che semplicemente accede ai dati, si è scelto di non implementarlo come EJB, ma come semplice bean iniettato utilizzando il framework CDI e quindi annotato come @Dependant (per dettagli si rimanda all’articolo Utilizzo di CDI in JSF 2.0). Inoltre non si è voluto inserire nell’esempio la complessità necessaria alla gestione di un database, quindi la persistenza è realizzata in memoria attraverso la gestione di una semplice mappa.

Il componente BankAccountService implementa invece la logica di gestione del conto corrente e quindi è implementato come EJB ma di tipo stateless. Per farlo è necessario definire l’interfaccia del servizio annotandola con @Remote, mentre la sua implementazione dovrà essere annotata con @Stateless. Si noti l’utilizzo dell’iniezione per recuperare il DAO.

Implementiamo infine il componente AtmService. Si tratta del componente con cui interagirà il client per i servizi di accesso al conto, deposito e prelevamento. Poiché si è prevista una fase si autenticazione dell’utente, realizzata banalmente recuperandone il conto corrente su cui poi operare, tale componente sarà un EJB si tipo stateful. Come fatto precedentemente annotiamo l’interfaccia con @Remote e la sua implementazione con @SessionScoped@Stateful.

Si noti che l’iniezione del service BankAccountService avviene questa volta con l’annotazione @EJB che è specifica per l’implementazione della dependency injection tra EBJ. Sono intercambiabili si gli EJB sono ospitati nello stesso container, ma nei casi più complessi l’annotazione @EJB consente di specificare parametri aggiuntivi per l’individuazione del bean target.

Avvio di WildFly

Una volta avviata l’applicazione sull’application server WildFly, notate la sezione di log seguente in cui è mostrato il JNDI name assegnato a ciascun EJB.

Il Client

Per l’utilizzo del servizio ATM remoto implementiamo un client java di tipo maven che utilizza la stringa JNDI per la connessione all’EJB. Il client innanzitutto crea il contesto remoto (Context) quindi recupera l’EJB su cui eseguirà le operazioni di: autenticazione, saldo e deposito.

Il codice seguente crea il contesto iniziale:

Per la generazione della stringa JNDI si rimanda al seguente link: EJB invocations from a remote client using JNDI. Per completezza ne riportiamo il codice:

Codice Sorgente

Il codice sorgente sia del client che del server è scaricabile qui: ejb-bank e ejb-bank-client.