Java Espresso, parte II
A Gioorgi.com abbiamo una sezione di ricerca e sviluppo, dove abbiamo la necessità di provare diversi tipi di ambienti in situazioni live: prova e ti riprova, abbiamo consolidato una certa esperienza in soluzioni Java Enterprise a "consumo ridotto".
In questa serie di articoli (di cui state leggendo la seconda puntata) vediamo come ottenere un'ambiente Java leggero, LAMP-like che gira in una manciata di Megabytes...
Nell'articolo precedente, abbiamo introdotto i principi che seguiremo nella ricerca del nostro "Java espresso". Abbiamo anche scartato una soluzione J2EE "pura" basata sulla specifica EJB intesa in senso "forte".
Anche una soluzione fortemente basata su jsp (alla "PHP") è stata scartata per una serie di svantaggi abbastanza evidenti.
Supponiamo ora di possedere una macchina Linux virtualizzata (per esempio con distribuzioni CentOS 5 o Ubuntu), con una quantità di memoria intorno ai 256Mb.
Il nostro obiettivo è creare una applicazione web che si serva di JDK 1.5 e Tomcat 5.5, che abbia delle caratteristiche simili ad un ambiente PHP+MySQL.
Dopo aver configurato una serie di servizi (tra cui MySql 4.1, Apache 2.x, sendmail ecc) ci siamo ritrovati con circa 150MB liberi.
Per effettuare delle misurazioni, abbiamo monitorato il sistema servendoci di Webmin, che consente di creare piccole sonde sugli aspetti di carico della macchina.
Abbiamo anche configurato Java con opzioni come "-verbose:gc" per ottenere una tracciatura puntuale delle allocazioni di memoria, e stringere il nostro chicco di caffé intorno ad una bel corpetto contenitivo.
Architettura
La strada che si prospetta è quella di uno sviluppo a 2-tier (tipicamente front end e back end).
Per realizzare questa architettura abbiamo scelto come libreria fondante Spring 2.5 (molto modulare e quindi segmentabile).
Spring può essere usato in modo "leggero", servendosi dei suoi automatismi spinti: l'autowire per l'agganciamento automatico dei componenti architetturali, e l'integrazione transazionale via AOP.
Spring: separare per riunire
E' importante osservare come Spring permetta di sviluppare in modo nuovo e più efficace: vediamo perché. L'idea di Spring è di separare lo sviluppo e i componenti.
Lo sviluppo intorno alla fine degli anni '90 era basato sull'uso di factory di oggetti e gerarchie di classi.
Purtroppo l'uso delle sottoclassi ha una serie di problemi nel lungo periodo. Il problema della classe di base fragile ne è un esempio abbastanza conosciuto, tanto che il creatore di Java (James Gosling) con una battuta ha affermato che se potesse tornare indietro toglierebbe la parola chiave "extends" da Java:
[...] During the [...] Q&A session, someone asked him [James Gosling]: "If you could do Java over again, what would you change?" "I'd leave out classes," he replied. After the laughter died down, he explained that the real problem wasn't classes per se, but rather implementation inheritance (the extends relationship). Interface inheritance (the implements relationship) is preferable. You should avoid implementation inheritance whenever possible.Inoltre una volta che il codice è sviluppato in modo esplicito intorno a factory e classi, la sua manutenzione è sì semplice ma non è possibile "riadattarlo" a concetti diversi. Le classi cioé non sono più entità indipendenti, ma assomigliano più a dei micro lavorati la cui riusabilità si riduce.
L'approccio della Dependency Injection su cui si basa Spring è leggermente diverso.
L'idea è quella di sviluppare semplici oggetti, che collaborano tra loro servendosi di incapsulamento leggero piuttosto che del subclassing. Le classi cioé possono svilupparsi autonomamente, e poi si interfacciano tra loro in modo isolato.
Se "Person" ha bisogno di una "Car", esporrà dei metodi tipo setCar/getCat con una segnatura tipizzata. Se Car e Person sono interfacce, il livello di accoppiamento è molto leggero e facile da ri-configurare.
La responsabilità di "collegare" i vari componenti viene demandata ad un container (come Spring), che viene guidato da una serie di direttive dichiarative...
Per fare un paragone, è come passare dall'accesso manuale ai dati all'uso di SQL.
Per cui le dipendenze vengono "configurate dichiarativamente". Allo stesso modo, la transazionalità diventa una "coperta" da applicare attraverso l'Aspect Oriented Programming. E' quindi possibile sviluppare applicazioni complesse aggregando semplici bean: per un esempio fate riferimento al modello MVC Web di Spring.
Unit Testing semplificato
L'altro aspetto importante dell'uso della Dependency Injection, è che è possibile sviluppare test di unità semplicemente "riaggregando" le varie parti in esame in modo differente.
Un pizzico di sale e pepe
Come tool di persistenza vi sono diverse scelte: si può andare da Hibernate a iBatis, che offrono livelli diversi di mappatura e automatismo.
Hibernate è concettualmente pesante, ma garantisce una libertà dal modello E-R molto ampia. E' superiore ai vari prodotti di mapping relazionale che potete trovare in altri linguaggi, e i suoi file di configurazione XML sono sintetici.
Cosa evitare
Struts 1.x invece va valutato con molta cautela: richiede molto lavoro di configurazione, e non è possibile integrare velocemente test di non regressione nel suo modello a componenti.
Nella prossima parte vedremo comemettere tutto assieme...
Riferimenti