Introduzione
I
più noti modelli di programmazione a oggetti distribuiti sono
-
CORBA:
è una specifica per l'interoperabilità di oggetti remoti
mediante un bus software (ORB - Object Request Broker). Ultimamente è
stato proposto un modello di componenti chiamato Corba Component Model
(CCM). E' importante osservare che un oggetto CORBA può essere scritto
in qualsiasi linguaggio per il quale sia previsto un binding come
ad esempio C++ e Java. Il protocollo di trasporto utilizzato è IIOP
(Internet Inter-ORB Protocol);
-
Java
RMI: il modello di programmazione distribuita fornito da Java è
ormai consolidato e fornisce una solida base per lo sviluppo di applicazioni
multipiattaforma in Java che è l'unico linguaggio per cui RMI è
disponibile. Una delle novità più importanti introdotte ultimemente
con RMI ([6]) è data dalla possibilità di utilizzare IIOP
come protocollo di trasporto: in questo modo un qualsiasi client CORBA
può accedere ai servizi esposti da un oggetto sviluppato con il
più semplice modello Java/RMI;
-
COM+:
il modello di componenti proposto da Microsoft si basa sull'utilizzo di
componenti che offrono una serie di servizi definiti in un'apposita interfaccia.
Un'interfaccia COM definisce il formato in memoria dell'oggetto server
che può essere sviluppato nei linguaggi per i quali esiste l'apposito
mapping, ovvero prevalentemente C++, Visual Basic e C# ma anche produttori
terze parti possono fornire il supporto COM+ per altri linguaggi. COM+
è alla base dalla piattaforma .NET presentata negli ultimi mesi
da Microsoft.
La
soluzione proposta da Sun prende il nome di J2EE, Java 2 Enterprise Edition
([1]). La piattaforma J2EE è uno standard aperto per lo sviluppo
di applicazioni enterprise con il linguaggio Java e comprende diverse tecnologie
come
-
Transazioni
- JTA
-
Servizi
di naming - JNDI
-
Accesso
a database - JDBC
-
Messaging
asincrono - JMS
-
Programmazione
per componenti - EJB
In questo
articolo approfondiremo la programmazione degli Enterprise Java Beans esaminando
anche le novità introdotte con la versione 2.0 delle specifiche.
Enterprise Java
Beans
EJB
non è un prodotto ma una specifica per lo sviluppo di applicazioni
enterprise, cioè applicazioni basate su componenti distribuiti e
transazionali. Più precisamente Sun fornisce
-
un modello
di programmazione basato su componenti server-side;
-
una API
vendor-neutral che definisce le interfacce del modello, ovvero il contratto
che i componenti sviluppati devono rispettare per garantire la portabilità
fra application server diversi.
L'architettura
di base EJB è basata su
-
un server
EJB
-
i container
-
i componenti
-
client
EJB
-
servizi
ausiliari (JTS, JMS, ...)
Approfondiamo
ora in dettaglio la tecnologia EJB che rappresenta il nucleo della piattaforma
J2EE.
EJB
Server
Il
server EJB fornisce l'ambiente di esecuzione dei container, li rende visibili
all'esterno e rende loro disponibili vari servizi come ad esempio il bilanciamento
del carico.
Container
I
container rappresentano l'ambiente di esecuzione degli EJB e forniscono
ai bean i servizi di base (dipendenti dall'implementazione) attraverso
la API standard definita nelle specifiche EJB. Il client di una applicazione
EJB non accede mai direttamente al componente perchè le sue chiamate
vengono intercettate dai container. Questo concetto di interception
è molto importante nella programmazione distribuita ed è
alla base anche di altri modelli come ad esempio COM+ di Microsoft. Il
container in questo modo è in grado di gestire il ciclo di vita
dei componenti, le transazioni, la sicurezza, gli aspetti di persistenza
e vari altri servizi in modo trasparente ai client e allo sviluppatore
di un EJB. Quest'ultimo può quindi concentrarsi sull'implementazione
della logica applicativa dei propri componenti.
Per
ottimizzare le prestazioni un EJB container gestisce un pool di risorse
in cui 'riporre' i bean non utilizzati. Il riferimento remoto rimane comunque
attivo e a una seguente invocazione del client il container provvede a
recuperare il bean dal pool.
L'interazione
fra un EJB e il suo container può avvenire attraverso metodi callback,
l'oggetto EJBContext o JNDI.
-
Ogni EJB
deve implementare una apposita interfaccia (che estende EnterpriseBean)
la quale definisce vari metodi callback che comunicano al bean il verificarsi
di determinati eventi come attivazione, persistenza e terminazione di una
transazione;
-
ogni EJB
mantiene inoltre un riferimento al container mediante l'interfaccia
EJBContext;
il bean può quindi ad esempio richiedere informazioni sullo stato
della transazione;
-
JNDI è
un'interfaccia a servizi di naming e attraverso JNDI un bean può
accedere a varie risorse come ad esempio servizi JMS o altri EJB.
Componenti
Un
componente EJB è costituito da diversi file:
-
Remote
interface: definisce tutti i metodi applicativi del componente ed estende
l'interfaccia EJBObject. Si osservi che lo sviluppatore non deve
implementare questa interfaccia: saranno infatti i tool a disposizione
del particolare EJB server che genereranno il codice necessario per intercettare
le chiamate del client e redirigerle sul componente. Supponiamo di voler
realizzare un'applicazione per la gestione di libri: la remote interface
avrà il seguente codice
import javax.ejb.EJBObject;
import javax.rmi.RMIException;
public interface Book extends EJBObject {
public String getCode() throws RemoteException;
public String getAuthor() throws RemoteException;
public void setAuthor(String author)
throws RemoteException;
public String getTitle() throws RemoteException;
public void setTitle(String title)
throws RemoteException;
}
L'interfaccia
Book
definisce metodi per accedere e modificare i dati che rappresentano un
libro e che saranno memorizzati su un'apposita tabella su database. Come
vedremo in seguito questi sono i tipici metodi di un particolare tipo di
EJB, gli entity bean, che rappresenta un oggetto memorizzato su
database.
Osserviamo
come il codice precedente non definisca metodi applicativi; questi vengono
infatti implementati da un diverso tipo di EJB, i session bean,
e saranno definiti in una remote interface distinta. Supponiamo ad esempio
di voler implementare la gestione di ordini di libri on-line: questa attività
sarà realizzata attraverso un session bean che chiameremo OrderAgent
e che avrà la seguente remote interface
import javax.ejb.EJBObject;
import javax.rmi.RMIException;
public interface OrderAgent extends EJBObject {
public void placeOrder(Book book, Customer customer)
throws RemoteException;
}
Il metodo
placeOrder()
esegue l'inserimento di un ordine per un libro da parte di un cliente (le
cui informazioni sono rappresentate dal componente Customer) coinvolgendo
tutti gli entity bean necessari.
Home
interface: questa interfaccia, che estende EJBHome, definisce dei metody
factory per creare un componente e per recuperare uno o più EJB
in base a determinati criteri; ad esempio la home interface per il nostro
componente Book sarà così definita
import javax.ejb.*;
import java.util.Collection;
import java.rmi.RemoteException;
public interface BookHome extends EJBHome {
public Book create(String code)
throws CreateException, RemoteException;
public Book findByPrimaryKey(String code)
throws FinderException, RemoteException;
public Collection findByAuthor(String author)
throws FinderException, RemoteException;
}
I metodi
findBy
della home interface permettono di recuperare un EJB o un'intera collezione
in base a vari criteri di selezione: findByPrimaryKey() restituisce
l'EJB corrispondente alla chiave primaria specificata mentre
findByAuthor()
restituisce una collezione di EJB di libri scritti dall'autore specificato.
Se non è possibile trovare i bean richiesti viene sollevata una
FinderException.
EJB:
il componente vero e proprio che implementa la logica applicativa definita
nella remote interface. Fra breve vedremo i vari tipi di EJB ed alcuni
esempi di codice;
qualsiasi
altro file o risorsa necessario, come ad esempio i file per la definizione
delle eccezioni o le classi che rappresentano la chiave primaria degli
entity bean;
un file,
chiamato deployment descriptor, che contiene le proprietà del componente
e che viene redatto in formato XML;
Come accennato
in precedenza sono previsti tre tipi di EJB: entity bean, session bean
e message driven bean. Ognuno di essi permette di modellare un determinato
aspetto dell'applicazione, rispettivamente l'accesso ai dati, l'esecuzione
di attività/processi e l'interazione con servizi di messagging asincrono.
Vediamoli ora in dettaglio.
Gli
entity
bean implementano l'interfaccia EntityBean e modellano i dati
presenti su database. Lo sviluppatore non deve quindi scrivere codice SQL
per accedere o modificare i dati ma userà i metodi della remote
interface. Vediamo come implementare il bean per il componente Book
import javax.ejb.*;
public class BookEJB implements EntityBean {
private String code;
private String author;
private String title;
private EntityContext context;
// business methods
public String getCode() throws RemoteException {
return code;
}
public String getAuthor() throws RemoteException {
return author;
}
public void setAuthor(String author)
throws RemoteException {
this.author = author;
}
public String getTitle() throws RemoteException {
return title;
}
public void setTitle(String title)
throws RemoteException {
this.title = title;
}
// EntityContext handling
public void setEntityContext(EntityContext context) {
this.context = context;
}
public void unsetEntityContext() {
this.context = null;
}
// callback methods
public Integer ejbCreate(String code) {
// using container managed persistence
this.code = code;
return null;
}
public void ejbPostCreate(String code) {}
public void ejbLoad() {}
public void ejbStore() {}
public void ejbActivate() {}
public void ejbPassivate() {}
public void ejbRemove() {}
}
Il codice
precedente presenta i metodi applicativi definiti in precedenza nella remote
interface. Osserviamo però che la classe non implementa l'interfaccia
Book
perchè l'accesso a tali metodi è infatti mediato dal container.
I metodi setEntityContext() e unsetEntityContext() permettono
di impostare l'oggetto EntityContext che, come visto in precedenza,
rappresenta un riferimento al container. Seguono infine alcuni metodi callback
invocati dal container al verificarsi di determinati eventi durante il
ciclo di vita del bean.
Le
gestione della persistenza per gli entity bean può essere di due
tipi:
-
container-managed
persistence: la persistenza del bean su database viene gestita automaticamente
dal container e le informazioni relative a quali campi serializzare su
database vengono mantenute nel deployment-descriptor del bean;
-
bean-managed
persistence: in questo caso il bean contiene il codice per accedere a database
e gestire la coerenza dei dati.
I session
bean, a differenza degli entity bean, non rappresentano dati ma modellano
attività e possono essere stateless o stateful. I bean stateless
non mantengono alcuna informazione relativa al loro stato e possono quindi
essere gestiti in pool per ottimizzare le prestazioni. I bean stateful
mantengono invece informazioni relative alla sessione client ma non possono
comunque sopravvivere a un crash di sistema. vediamo una possibile implementazione
di OrderAgent:
import javax.ejb.SessionBean;
public class OrderAgentEJB implements SessionBean {
public void placeOrder(Book book, Customer customer)
throws RemoteException {
Order order;
OrderHome orderHome;
orderHome = ... // home reference
order = orderHome.create(book.getCode(),
customer.getId());
}
}
Il codice
precedente, anche se molto semplificato, mostra comunque la possibilità
di poter distinguere componenti basati sui dati e componenti basati su
task.
Una
delle maggiori novità introdotte con la nuova versione 2.0 delle
specifiche EJB riguarda l'introduzione di un nuovo tipo di bean, i message
driven bean, che si occupano di gestire i messaggi JMS. JMS è
una API vendor-neutral per la gestione di sistemi di messaging asincrono.
Si veda [3] per una introduzione a JMS. Prima della versione 2.0 degli
EJB questi potevano accedere a servizi JMS attraverso JNDI; ora è
possibile sviluppare client JMS che vengono eseguiti in un ambiente robusto
e affidabile quale un container EJB. Vediamo uno scheletro di codice di
un message driven bean.
import javax.ejb.*;
import javax.jms.*;
public class MessageHandlerEJB implements MessageDrivenBean {
public void onMessage(Message message) {
// handle message
// ...
}
}
Quando
si esegue il deploy di un message driven bean si assegna ad esso quali
tipi di messaggio devono essergli instradati. E' inoltre importante osservare
che i messaggi non devono essere necessariamente generati da un altro EJB
ma anche ad esempio da un'applicazione legacy che pubblica un messaggio
in un prodotto compatibile con JMS.
Osserviamo
che per garantire la massima integrazione fra applicazioni è possibile
accedere ad un EJB come oggetto CORBA.
EJB 2.0
Le
novità principali introdotte con la nuova versione 2.0 delle specifiche,
oltre all'introduzione dei message driven bean, riguardano la container-managed
persistence. Come accennato in precedenza è possibile configurare
un EJB in modo che il container si faccia carico di mappare i suoi attributi
in colonne di una tabella su database. Il modello introdotto con la versione
2.0
rende questo meccanismo molto più potente introducendo un nuovo
componente all'architettura, il persistence manager, che incapsula
le funzionalità di persistenza mentre il container mantiene la gestione
si sicurezza, transazioni e risorse.
Il
persistence manager è in grado di generare il mapping di entity
bean verso database relazionali in base alle informazioni contenute nel
descrittore del bean, come in EJB 1.1, ma sono state introdotte diverse
novità
-
poiché
il persistence manager è un componente separato dal container è
possibile sviluppare manager custom non necessariamente legati a un database
ma ad esempio ad un sistema ERP;
-
il mapping
degli attributi del bean avviene in modo diverso e ora consente di gestire
attributi di tipo non primitivo (i cosiddetti dependant object)
con i quali il bean può avere relazioni del tipo 1-1 o 1 a molti;
-
è
possibile modellare relazioni con altri EJB aventi cardinalità 1-1,
1 a molti o molti a molti;
-
la modalità
di implementazione di un entity bean è diversa e non è necessario
elencare i campi persistenti. Ad esempio il codice del componente Book
diventa
import javax.ejb.*;
public abstract BookEJB implements EntityBean {
// instance fields
private EntityContext context;
// business methods
public abstract String getCode() throws RemoteException;
public abstract String getAuthor() throws RemoteException;
public abstract void setAuthor(String author)
throws RemoteException;
// ...
}
Possiamo
osservare come nessuno degli attributi persistenti venga elencato nella
classe: questi verranno specificati nel deployment descriptor e il persistence
manager genererà la classe concreta corrispondente;
quando
abbiamo introdotto la home interface degli EJB abbiamo visto anche i metodi
find. Con gli EJB 2.0 viene introdotto EJB Query Language (EJB QL) che
permette di specificare come il persistence manager deve implementare i
metodi find;
la home
interface degli entity bean può ora includere metodi applicativi,
chiamati metodi ejbHome.
Per maggiori
informazioni è possibile consultare, oltre alla documentazione ufficiale
Sun, l'articolo in [4].
Conclusioni
In
questo articolo abbiamo visto in modo dettagliato i punti chiave della
tecnologia J2EE. I vari esempi di codice presentati ci hanno permesso di
comprendere come lo sviluppo di applicazioni modellate come EJB possa semplificare
lo sviluppo stesso grazie all'architettura delle applicazioni J2EE e ai
vantaggi che derivano dall'utilizzo di un linguaggio object-oriented come
Java. Vorrei infine ringraziare Raffaele Spazzoli per il supporto avuto
nella realizzazione di questo articolo.
Riferimenti
-
Documentazione
ufficiale Sun,
http://java.sun.com/j2ee
-
Speciale
Componenti - Computer Programming, n. 91, Maggio 2000
-
Gordon
Van Huizen, JMS: An infrastructure for XML-based business-to-business
communication, JavaWorld,
http://www.javaworld.com/jw-02-2000/jw-02-jmsxml.html
-
Richard
Monsol-Haefel, Read all about EJB 2.0, JavaWorld, http://www.javaworld.com/jw-06-2000/jw-0609-ejb.html
-
Gopalan
Suresh Raj - Articoli e tutorial - http://www.execpc.com/~gopalan/java/ejb.html
-
Akira
Andoh, Simon Nash - RMI over IIOP - http://www.javaworld.com/jw-12-1999/jw-12-iiop.html
|