Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
Giorgio Maone ` E responsabile tecnico per larea sviluppo software di InformAc` tion, societa di consulenza che dirige dal 1998. Si occupa di soluzioni multi-tiered su piattaforme Enterprise Java (J2EE) e Microsoft (.NET), di produzioni multimediali e di formazione negli ambiti Web, middle-tier, DB e multimedia.
Limpaginazione automatica di questa rivista e realizzata al ` 100% con strumenti Open Source usando OpenOffice, Emacs, BHL, LaTeX, Gimp, Inkscape e i linguaggi Lisp, Python e BASH
For copyright information about the contents of Computer Programming, please see the section Copyright at the end of each article if exists, otherwise ask authors. Infomedia contents is 2004 Infomedia and released as Creative Commons 2.5 BY-NC-ND. Turing Club content is 2004 Turing Club released as Creative Commons 2.5 BY-ND. Le informazioni di copyright sul contenuto di Computer Programming sono riportate nella sezione Copyright alla ne di ciascun articolo o vanno richieste direttamente agli autori. Il contenuto Infomedia e 2004 Infome` dia e rilasciato con Licenza Creative Commons 2.5 BYNC-ND. Il contenuto Turing Club e 2004 Turing Club ` e rilasciato con Licenza Creative Commons 2.5 BY-ND. Si applicano tutte le norme di tutela dei marchi e dei segni distintivi. ` E in ogni caso ammessa la riproduzione parziale o totale dei testi e delle immagini per scopo didattico purch e vengano integralmente citati gli autori e la completa identicazione della testata. Manoscritti e foto originali, anche se non pubblicati, non si restituiscono. Contenuto pubblicitario inferiore al 45%. La biograa dellautore riportata nellarticolo e sul sito www.infomedia.it e di norma quella disponibi` le nella stampa dellarticolo o aggiornata a cura dellautore stesso. Per aggiornarla scrivere a info@infomedia.it o farlo in autonomia allindirizzo http://mags.programmers.net/moduli/biograa
FOCUS
a qualche tempo la comunit degli sviluppatori Java cosiddetti enterprise in fermento. La parola dordine sembra essere semplicit, e in molti iniziano ad esplorare percorsi alternativi alla piattaforma Java 2 Enterprise Edition tradizionalmente intesa. Nessuno mette in discussione le tecnologie fondanti, come Servlet o JDBC, ma linsofferenza da pi parti manifestata fin dalla loro comparsa verso astrazioni di livello pi alto (leggi EJB), si concretizza adesso in proposte variamente convincenti ma quantomeno tangibili. Ci si deve, almeno in parte, allattenzione che ormai riscuote lAspect Oriented Programming (AOP), non pi considerata speculazione teorica bens strumento pratico e, in quanto tale, valutabile e sfruttabile in termini pragmatici. SullAOP e sul principio non nuovissimo dellInversion of Control, o pi propriamente sulla sua specializzazione chiamata Dependency Injection, si basano alcuni framework applicativi che ambiscono a sostituire la monoliticit dellApplication Server con un approccio modulare e non invasivo, teso a cucire insieme i componenti eterogenei di unapplicazione riducendo le dipendenze esplicite, allo scopo di assicurare massima libert nelle scelte implementative. Prima di indagare le nuove tendenze, per opportuno fare brevemente il punto su ci che oggi J2EE rappresenta ed offre.
Tecnologie per la gestione della piattaforma J2EE Deployment Specification J2EE Management Specification J2EE Client Provisioning Java Authorization Contract for Containers Altre tecnologie Java DataBase Connectivity (JDBC) Java Data Objects (JDO) JavaMail Infrastruttura transazionale, cio Java Transaction API (JTA) e Java Transaction Service (JTS) Come si vede, gli EJB spesso considerati la tecnologia J2EE per antonomasia non sono che una tessera in un mosaico ricco e articolato: nessuno vieta di scrivere applicazioni J2EE facendone a meno. Molti progetti si basano effettivamente sullaccoppiata Servlet Container/JDBC e saltano a pi pari i livelli intermedi, magari con laiuto di un framework MVC come Struts e adottando il pattern DAO per non sporcare troppo il controller. Cionondimeno, gli EJB costituiscono un modello a componenti robusto, consolidato e prevedibile, che mostra allo sviluppatore un sentiero da seguire senza eccessivi sforzi di fantasia per partizionare lapplicazione ed usufruire gratuitamente di servizi indispensabili per progetti medio-grandi: gli Entity Bean rispecchiano le entit persistenti e le relazioni che tra esse intercorrono, e sono essenzialmente oggetti Java che vivono nel database, salvati e recuperati in maniera transazionale e trasparente (almeno quelli di tipo CMP); i Message-Driven Bean reagiscono ad eventi scatenati da messaggi accodati tramite JMS, e rappresentano la via ufficiale alla programmazione asincrona, visto che lEJB container proibisce il multi-threading esplicito; i Session Bean, infine, ospitano la logica applicativa, demarcano i confini delle transazioni (in modo dichiarativo o programmatico) ed interagiscono con altri EJB e con i client. In definitiva, i principali vantaggi ideali di unapplicazione composta da EJB sono: dislocazione dei componenti, che rispetto ai client possono essere locali (residenti nella stessa JVM) o remoti (anche su unaltra macchina); persistenza indipendente dal tipo di DBMS e dalluso di SQL, poich gli Entity Bean sono una forma di Object/Relational Mapping (ORM) e consentono al programmatore di lavorare su oggetti tipizzati piuttosto che su ResultSet generici;
Computer Programming n. 136 - Giugno 2004
gmaone@infomedia.it
responsabile tecnico per larea sviluppo software di InformAction, societ di consulenza che dirige dal 1998. Si occupa di soluzioni multi-tiered su piattaforme Enterprise Java (J2EE) e Microsoft (.NET), di produzioni multimediali e di formazione negli ambiti Web, middle-tier, DB e multimedia.
22
Application Server
caching e pooling, dal momento che tutti gli accessi ai dati avvengono attraverso lApplication Server (persistenza container managed) e che questultimo, pertanto, pu decidere cosa trattenere in memoria e quando, invece, il caso di scomodare il database; demarcazione dichiarativa delle transazioni, che sono in grado di propagarsi tra diversi metodi e coinvolgere risorse eterogenee (transazioni distribuite XA); sicurezza a livello di metodo (prima di eseguire una chiamata il container verifica che il chiamante abbia privilegi sufficienti). Daltra parte, com noto, gli EJB sono soggetti a dure critiche. Alcune, per cos dire storiche, riguardano soprattutto la quantit di codice necessaria a scriverli e configurarli nonch le performance non proprio brillanti. Al primo problema si pu ovviare adottando strumenti di generazione del codice, mentre le prestazioni sensibilmente migliorate con lintroduzione delle interfacce locali e la conseguente riduzione delle chiamate a metodo remoto dipendono molto dalla qualit dellApplication Server, dalla bont della sua configurazione e, ovviamente, dallaccortezza del programmatore. Le obiezioni pi solide, invece, vertono sulleccessiva invasivit dellAPI (interfacce da implementare e classi da estendere che rendono difficoltoso il riuso in altri contesti) e sulle rigidit degli Entity Bean come strumento ORM (granularit, ereditariet, relazioni, linguaggio di interrogazione). Parliamo quindi di criticit strutturali, che inducono a battere strade diverse verso unarchitettura J2EE priva degli EJB ma provvista dei vantaggi ad essi tradizionalmente associati.
oggi, essendo la riflessione sullAOP maturata giungendo a un buon livello di formalizzazione, si voglia offrire allo sviluppatore finale la possibilit di intervenire in modo semplice e selettivo sul processo di intercettazione. Lo scenario prospettato da JBoss 4 il ramo attualmente in fase di sviluppo affascinante: lapplicazione vera e propria composta da Plain Old Java Objects (POJO), vale a dire comunissimi oggetti Java a cui non sono prescritte interfacce o linee ereditarie particolari (in contrasto con gli EJB), e quindi in generale riutilizzabili anche fuori dallApplication Server. Aspetti come la persistenza, la transazionalit, linvocabilit remota, la sicurezza ecc. vengono specificati attraverso metadati esterni (descrittori XML) o interni al codice (speciali commenti JavaDoc o, in futuro, le annotations introdotte da J2SE 1.5): il framework JBossAOP li applicher magicamente al momento del deployment. Questo approccio si traduce, per la gioia di molti, in un J2EE senza EJB che mantiene intatta la sua potenza di fuoco. Ciononostante, il supporto per gli EJB sar conservato, anche se ovviamente limplementazione interna degli stessi far leva sullo strato AOP. Non si tratta solo di una scelta politica tesa a preservare la compatibilit con le applicazioni e i componenti esistenti: ladesione di JBoss Inc. allEJB Expert Group, annunciata il 5 aprile scorso, induce viceversa a sperare in una specifica EJB 3.0 pi leggera, semplice e potente che tesaurizzi lesperienza accumulata dal progetto open source.
FOCUS
LISTATO 1
La classe Localita, unentit persistente scritta come un normale JavaBean e annotata con i tag XDoclet per Hibernate
oggetto Localita (Monreale, in provincia di Palermo) e salvarlo sul database potrebbe essere:
net.sf.hibernate.SessionFactory factory; // omessa configurazione preliminare
package com.informaction.geo.model; /** * @hibernate.class */ public class Localita { private private private private private Integer id; String des; String cap; String prefisso; Provincia provincia;
// della SessionFactory [...] net.sf.hibernate.Session session = factory.getSession(); Localita monreale = new Localita(); monreale.setDes(Monreale); monreale.setCap(90046); monreale.setPrefisso(091); // recupera un oggetto Provincia tramite ID Provincia palermo = (Provincia)session.get(Provincia.class, PA); monreale.setProvincia(palermo); session.save(monreale); // salva la localit session.flush(); // sincronizza il database
public Localita() { } /** * @hibernate.id generator-class=identity */ public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } /** * @hibernate.property */ public String getDes() { return des; } public void setDes(String des) { this.des = des; } /** * @hibernate.property */ public String getCap() { return cap; } public void setCap(String cap) { this.cap = cap; } /** * @hibernate.property */ public String getPrefisso() { return prefisso; } public void setPrefisso(String prefisso) { this.prefisso = prefisso; } /** * @hibernate.many-to-one * column=id_prov not-null=true */ public Provincia getProvincia() { return provincia; } public void setProvincia(Provincia provincia) { this.provincia = provincia; } }
Un punto di forza di Hibernate il suo linguaggio di interrogazione Hibernate Query Language (HQL), che risulta spesso molto pi espressivo dello stesso SQL, e comunque di gran lunga pi potente di EJBQL. Ecco, ad esempio, la query HQL che genera un report con il conteggio delle localit appartenenti a ciascuna provincia, raggruppando i totali per regione:
select localita.provincia.regione.des, localita.provincia.des, count(localita) from Localita localita group by 1, 2 Hibernate la tradurr pressappoco nella seguente join SQL: select regione.des, provincia.des, count(localita.id) from localita, provincia, regione where localita.id_prov=provincia.id and provincia.id_reg=regione.id and localita.id_prov=provincia.id group by 1, 2
LAPI Criteria permette, inoltre, di impostare dinamicamente i criteri di ricerca in maniera object oriented, piuttosto che esprimerli sotto forma di stringhe, e supporta le cosiddette query by example, cio la ricerca di oggetti che somigliano ad un certo modello. Viceversa, nei rari casi in cui non c alternativa, si possono eseguire query SQL a basso livello tramite Hibernate (che provveder comunque a mappare i risultati), oppure recuperare la java.sql.Connection utilizzata dalla Session corrente e operare direttamente tramite JDBC. Dulcis in fundo, il tool interattivo Hibern8IDE (Figura 1) aiuta a testare query HQL e codice Java (o, pi precisamente, BeanShell) nel contesto di Hibernate.
vo univoco che si tradurr in una primary key. Ad esplicitare la relazione molti a uno intercorrente tra la propriet provincia e lomonima classe, anchessa mappata, il tag hibernate.many-to-one, che in questo caso specifica anche la colonna da usare come foreign key. Unapplicazione che usa Hibernate innanzitutto configura un oggetto SessionFactory indicando, tra laltro, i mapping da usare e i parametri di connessione al DB. La factory sar in seguito utilizzata per ottenere oggetti Session, che sono linterfaccia principale per il dialogo con il motore di persistenza. Il codice Java per istanziare un 24
Application Server
import javax.naming.*; import javax.sql.*; public class DataManager { private NamingContext ctx = new InitialContext(); private DataSource dataSource = (DataSource)ctx.lookup(jdbc/mainDS); [...] }
intercettabilit tramite metodo factory, che consente di scegliere unimplementazione particolare, decorare e/o configurare loggetto prima di fornirlo allutilizzatore. Un effetto collaterale non trascurabile, daltro canto, il proliferare del codice di lookup un po dovunque, che si traduce in una forte dipendenza dellapplicazione dallinfrastruttura e, di conseguenza, in pesanti difficolt nel riutilizzare componenti al di fuori dellApplication Server, specie in unapplicazione desktop disconnessa o, comunque, in assenza di un server JNDI. Tale problema pu essere mitigato dalladozione del pattern Service Locator [8], che aiuta a centralizzare, schermare e ottimizzare gli accessi al servizio di Naming. Tuttavia, anche nellipotesi di sostituire limplementazione JNDI di un Service Locator con una versione in grado di fare a meno del container J2EE, come configurarla? Una possibile soluzione viene dai framework basati sulla Dependency Injection.
Nellesempio la classe DataManager recupera dal contesto JNDI (NamingContext) una sorgente dati tramite il nome simbolico jdbc/mainDS, al quale lamministratore del server lavr precedentemente associata intervenendo su un file di configurazione oppure operando su una GUI (laddove disponibile). Il codice lavora sullinterfaccia DataSource, la cui implementazione concreta varier a seconda del tipo di database, delleventuale gestione del pooling, del supporto per transazioni distribuite (XA) ecc. Lo scenario non dissimile da quello che, sotto Windows, riguarda ODBC: il sistemista configura una sorgente di dati particolare associandole un nome (DSN), che il programma sfrutter per cercarla ed utilizzarla secondo uninterfaccia uniforme. JNDI, tuttavia, generalizza il paradigma: consente di registrare (bind) e recuperare (lookup) risorse di ogni tipo (oggetti di qualunque classe, anche definita dallutente); una tecnologia distribuita, basata di serie su RMI, LDAP o CORBA ma estensibile con altri protocolli; offre supporto esplicito, tramite linterfaccia javax.naming.spi.ObjectFactory, per la creazione dinamica delle istanze richieste. I benefici evidenti sono: disaccoppiamento dellimplementazione dei servizi dai client, che ne conoscono solo le interfacce; trasparenza di posizione (un oggetto pu essere remoto, cio risiedere fisicamente su un elaboratore diverso da quello del client, senza che la procedura di lookup cambi);
FIGURA 1
Hibern8IDE, un tool per testare interattivamente query e codice Java in un contesto Hibernate
FOCUS
nente specifica le proprie dipendenze in un file di configurazione, e implementa uninterfaccia (Serviceable, nel caso di Avalon) attraverso la quale il container fornir un contesto di lookup personalizzato (ServiceManager) che consentir di recuperare tutti gli oggetti necessari e solamente quelli. Tornando allesempio del DataManager che ha bisogno di una DataSource, esso andrebbe riscritto cos:
import javax.sql.*; import org.apache.avalon.framework.*; public class DataManager implements Serviceable { private DataSource dataSource; public void service(ServiceManager sm) { dataSource = (DataSource)sm.lookup(dataSource); } [...] } <bean id=dataSource class=org.apache.commons.dbcp.BasicDataSource destroy-method=close> <property name=driverClassName> <value>org.hsqldb.jdbcDriver</value> </property> <property name=url> <value>jdbc:hsqldb:hsql://localhost:9001</value> </property> <property name=username> <value>sa</value> </property> <property name=password> <value></value> </property> </bean> } [...] }
La BeanFactory, cuore dellarchitettura di Spring, serve ad istanziare e collegare tra loro i diversi componenti (di servizio e applicativi) che formano lapplicazione, e pu essere configurata in maniera programmatica o tramite limmancabile descrittore XML. Il markup della BeanFactory semplice, ridotto allo stretto necessario per creare bean, assegnare loro un identificativo e popolarne le propriet. Tornando a noi, innanzitutto dovremo preparare una DataSource:
Non sembra un gran progresso: il nostro componente non pi accoppiato a JNDI, ma dipende comunque da Serviceable e da ServiceManager. Per di pi, riusare questo componente in assenza di Avalon sembra alquanto farraginoso: per configurarlo bisogna fornire unimplementazione alternativa di ServiceManager, riempirla e passarla al metodo service(). Allimprovviso una mente brillante, rimuginando sulla faccenda, folgorata dallesistenza di uninterfaccia implementata implicitamente dalla maggior parte degli oggetti Java e utilizzabile per configurare le dipendenze: i setter delle propriet. Sembra luovo di Colombo, al punto che si ritiene di aver scoperto un nuovo tipo di IoC, il cosiddetto Type 2, attorno al quale sar edificato Spring [12].
La rivoluzione leggera
Spring un framework applicativo open source, modulare e non invasivo, basato su AOP e Dependency Injection. Iniezione delle dipendenze la designazione proposta da Martin Fowler [13] per deflazionare luso dellespressione Inversion of Control, il cui contenuto semantico andava scivolando dal principio generale enunciato in [9] verso laccezione pi riduttiva di pattern impiegato per risolvere le dipendenze tra gli oggetti. Seguendo il consiglio di Fowler, non si parler pi di IoC Type 2 e IoC Type 3 bens, rispettivamente, di Setter Dependency Injection e Constructor Dependency Injection. Questultima, introdotta da PicoContainer [14], analizza il costruttore delloggetto per dedurne le dipendenze e tentare di soddisfarle al momento della creazione. Una scuola di pensiero (a cui il sottoscritto aderisce) tende a preferire tale procedura perch riduce il rischio di trovarsi a maneggiare oggetti non correttamente inizializzati, che Joshua Bloch chiamerebbe cattivi cittadini. Adottando la Setter Injection, scelta predefinita in Spring, il DataManager si riscrive come un normale Java Bean, privo di dipendenze dal framework:
import javax.sql.*; public class DataManager { private DataSource dataSource;
Si tratta di una BasicDataSource (cfr. [15]) che punta ad un database HSQL e che chiamiamo semplicemente dataSource. A questo punto possiamo dichiarare il nostro DataManager, iniettandogli la DataSource di cui sopra:
<bean id=dataManager class=DataManager> <property name=dataSource> <ref bean=dataSource/> </property> </bean>
In questo caso abbiamo risolto la dipendenza in maniera esplicita, indicando il nome della propriet e quello del bean da assegnarle. Spring, tuttavia, offre diverse opzioni di autowiring, cio di risoluzione automatica, tra le quali byName, che cerca tra i bean definiti nella factory quello il cui id coincide col nome della propriet da valorizzare; byType, che invece cerca un bean il cui tipo sia tale da renderlo assegnabile alla propriet (funziona se c uno ed un solo bean idoneo). Nel nostro esempio, essendoci una sola DataSource che per di pi si chiama come la propriet, le varianti
<bean id=dataManager class=DataManager autowire=byType />
e
public void setDataSource( DataSource dataSource) { this.dataSource = dataSource <bean id=dataManager class=DataManager autowire=byName />
26
Application Server
si equivalgono. Spring, inoltre, supporta anche la Constructor Injection (altrimenti non lo userei). Quindi, supposto che DataManager preveda un costruttore siffatto:
public DataManager(DataSource dataSource) { this.dataSource=dataSource; }
si potr scrivere
<bean id=dataManager class=DataManager autowire=constructor />
dietro dipendenze inutili legate al Web-tier. Lestrema flessibilit di Spring e la sua discrezione (intesa come scarsa propensione ad intromettersi nel codice applicativo) sono le fondamenta della sua popolarit: al momento in cui scriviamo, viaggia attorno agli 8.000 download mensili. La mia azienda lo sta adoperando per realizzare due versioni distinte della stessa applicazione: una desktop con interfaccia utente Swing e database embedded (HSQLDB), laltra Web based (Tapestry) con RDMBS MySQL. Entrambe sfruttano Hibernate, ed il riuso delle classi business, grazie alla Dependency Injection e allAOP, pressoch totale, a dispetto delle differenze infrastrutturali.
oppure, esplicitamente:
<bean id=dataManager class=DataManager> <constructor-arg> <ref bean=dataSource/> </constructor-arg> </bean>
Conclusioni
Quanto scritto finora non vuole essere un manifesto contro J2EE: al contrario, i framework ed i principi illustrati vanno considerati un approccio alternativo per combinare tra loro le valide tecnologie che compongono la piattaforma. Anche lo slogan J2EE senza EJB va preso con le dovute cautele: sarebbe un peccato disperdere il patrimonio di conoscenze accumulato da fior di sviluppatori su strumenti che, se maneggiati con sapienza, producono risultati egregi. Daltronde ci sono ottimi motivi per credere che la specifica EJB 3.0 assorbir molti dei concetti fin qui esposti, dallAOP alla Dependency Injection passando per i metadati espressi in annotation: finalmente gli Enterprise Java Bean si programmeranno come i POJO. Se tutto va bene, dopo una gestazione lunga e travagliata, siamo vicini alla nascita di J3EE, sotto il segno della semplicit.
Eliminando il setter ed il costruttore di default, ci si assicurer cos che un DataManager sia sempre provvisto della sua brava DataSource.
27