MokaByte Numero  45  - Ottobre  2000
 
Applicazioni 
stand-alone in Java
Parte terza
di 
Lavinio Cerquetti
Teoria e pratica di un’applicazione stand-alone in Java

Concluso, nella parte precedente, lo studio dell’architettura della nostra applicazione non ci rimane che discutere di alcuni particolari scenari di ottimizzazione del linguaggio Java, e dare infine uno sguardo d’insieme a tutto il progetto Irmgard, accennando a quelle che ne saranno le future linee di sviluppo.

Trucchi ed escamotage: le ricette dello sviluppatore Java
L’adagio vuole che siano i dettagli a fare la differenza. Ebbene, l’esperienza maturata grazie ad Irmgard ci ha più volte mostrato l’assoluta verità di questa massima. Tanto più in una situazione in cui la performance rappresenta veramente un problema, e non si hanno a disposizione cicli macchina da sprecare, spesso ci si trova a dover apportare delle correzioni a del codice ben modellato ed implementato solo per riuscire a guadagnare quel briciolo di velocità e stabilità che può fare la differenza tra un sì ed un no, tra un cliente acquisito ed uno perso.
E proprio per evitare ai lettori di questo articolo di trovarsi con le spalle al muro a causa delle lacune prestazionali degli ambienti Java in alcune situazioni specifiche, ho raccolto in questo paragrafo un breve sommario di quel patrimonio di conoscenze, alcune teoriche, altre sorprendentemente pratiche, che si sono rivelate un importante fattore nel successo dell’esperienza Irmgard. Ognuna di queste “ricette” richiederebbe in realtà un discorso approfondito, e su molte di esse si disquisisce da lungo tempo, non solo all’interno del mio piccolo team, ma nell’intera “comunità globale” degli sviluppatori Java:
 
  1. Gli statement SQL: per semplicità di utilizzo il primo approccio di molti programmatori alla generazione di statement SQL attraverso le API JDBC passa per gli Statement; ebbene, i test da noi effettuati in diverse riprese, in condizioni di utilizzo, e con database e driver differenti, hanno dimostrato che i PreparedStatement offrono un guadagno in performance, rispetto ai semplici Statement, nell’ordine del 35-50%. Un così evidente scompenso ci ha convinti ad utilizzare in maniera preponderante i primi all’interno di Irmgard: in particolare, tutti gli statement che operano, o che ritornano, un numero limitato di righe dal database sono codificati sotto forma di PreparedStatement (inizializzati in modalità lazy al primo utilizzo e rilasciati al termine del programma). Questa regola non viene seguita per gli statement che lavorano su set di righe di dimensioni più generose, dal momento che il guadagno prestazionale viene compensato dai tempi di elaborazione del rowset. 

  2. Se a questo discorso aggiungiamo la considerazione, già fatta in precedenza, di come i PreparedStatement permettano di effettuare in maniera portabile query su colonne la cui sintassi SQL è spesso “problematica” – come le colonne CHAR, che possono contenere il carattere apostrofo (‘), e creare così problemi sintattici nella costruzione dinamica degli statement di interrogazione – o non omogenea tra i diversi database server – penso alle colonne data, data/ora e timestamp – il vantaggio di questi ultimi nei confronti degli Statement ne esce persino ingrandito, e l’opportunità di utilizzarli in maniera quasi esclusiva si fa evidente.
  1. Restando in ambito SQL, credo sia opportuno ripetere due consigli validi in generale, ed ancora di più in ambito Java. Il primo è di inserire nella clausola SELECT solo le colonne realmente necessarie – vale a dire le colonne che verranno utilizzate nel codice attraverso le chiamate getXXX() sul ResultSet, e, per maggiore portabilità, le colonne utilizzate all’interno del medesimo statement SQL nelle clausole WHERE, GROUP BY ed ORDER – evitando quindi accuratamente l’uso del carattere asterisco (*) come jolly per richiedere tutte le colonne di una tabella. Il secondo consiglio è quello di creare degli indici per velocizzare l’esecuzione delle clausole WHERE eseguite con maggiore frequenza.
  2. Studiando il framework JFC non ci si può non accorgere di come Java sembri prediligere le non-mutable class; purtroppo per quanto nulla si possa obiettare circa l’eleganza delle implementazioni basate su questo paradigma, esso provoca un significativo decadimento prestazionale, nonché un elevato frazionamento ed una eccessiva occupazione di memoria, legati all’alto numero di allocazioni e deallocazioni da esso richieste ed all’impossibilità per il garbage collector di ricuperare celermente tutta la memoria utilizzata. 

  3. Questo problema si applica indistintamente a tutte le classi, ma in misura ancora maggiore a quelle che rivestono un ruolo importante nella vita del programma: l’istanziazione ripetuta di queste ultime, infatti, appesantisce tanto di più il programma in ragione delle loro dimensioni e della complessità del costruttore. 
    Un esempio perfetto di quando detto si presentò al mio team nei form di gestione delle tabelle, e particolarmente in quelli di dettaglio: la loro creazione era talmente dispendiosa, anche per via di tutti i controlli presenti nei form stessi - molti dei quali richiedevano la consultazione di apposite tabelle di lookup - che essi sono divenuti, da non-mutable che erano, mutable, con un conseguente significativo vantaggio in termini di performance e di memoria utilizzata.
  4. Nella libreria di gestione dati di JBuilder i DataSet in modalità AS_NEEDED, i quali permettono di lavorare su rowset di grandi dimensioni caricando in memoria solo “finestre” composte da un numero limitato di righe – e risultano quindi un punto cardine nella gestione delle tabelle - presentano un bug molto grave, causa del fallimento della loro risoluzione se tutte le righe non sono ancora state caricate, aggirabile semplicemente chiudendo il DataSet prima di effettuarne la risoluzione. Dal momento che quest’operazione implica la chiusura del cursore SQL essa potrebbe, almeno in linea teorica, causare problemi ai database server meno recenti, i quali potrebbero reagire effettuando il fetch di tutte le righe non ancora scandite dal cursore, operazione chiaramente molto onerosa: ad ogni modo, in tutti i nostri test, questa situazione limite non si è mai verificata. In breve, ricordo anche come, dopo aver risolto il DataSet applicandone le modifiche, il codice dovrà comunque effettuarne il refresh per aggirare un altro bug delle sue API, le quali “nascondono” permanentemente tutte le righe non ancora estratte da un DataSet nel momento in cui questo viene risolto.
  5. Rimanendo ancora in tema, molti sviluppatori deplorano la lentezza della gestione dati implementata dalle API di JBuilder, ed i newsgroup Borland/Inprise [1] sono un vero e proprio ricettacolo di lamentele di questo genere, nonché di “ricette magiche” proposte come soluzione. 

  6. Ora, è vero che queste API non possano paragonarsi per velocità a quelle JDBC – che io consiglio di privilegiare ogniqualvolta sia possibile – ma la situazione è tutt’altro che tragica, e con un minimo di ottimizzazione è possibile utilizzare con soddisfazione i DataSet e tutti i componenti associati. I manuali di JBuilder – cui rimando i lettori per tutti gli approfondimenti del caso - spiegano chiaramente come procedere in questo senso: in sostanza, è sufficiente rendere persistenti le informazioni sullo schema di database e tabelle (i cosiddetti metadata) e richiedere che le clausole WHERE utilizzate per reperire le righe da aggiornare nel database vengano costruite sulla sola scorta delle colonne primarie delle tabelle.
  7. Della libreria dbSwing fa parte un componente molto utile, chiamato JdbNavField; esso implementa la ricerca su di una colonna di un DataSet, gestendola in modalità incrementale se si trova a lavorare su delle colonne CHAR. Tuttavia mentre il JdbNavField funziona alla perfezione con il JDK 1.2, esso risulta incompatibile con il JDK 1.2.2, scelto dal mio team come JDK “standard”, e preferito alla versione 1.2 per le molte migliorie apportate, specialmente nel rendering a video dei componenti Swing, rendering che risultava in precedenza ricco di bug ed inconsistenze. Fortunatamente, il lavoro di reimplementazione del JdbNavField ha richiesto solo poche ore.
  8. Un punto notoriamente dolente di Java è costituito dalla sua richiesta di memoria: in questo senso non è pensabile utilizzare in Java la RAM con la stessa liberalità cui si è usi nelle applicazioni native Windows. Per “aiutare” il garbage collector a rientrare in possesso della memoria inutilizzata è realmente necessario porre a null tutti i riferimenti agli oggetti non più necessari, e questo, specialmente nel caso di oggetti con riferimenti innestati come JTable o container in generale, può essere problematico. 

  9. In tali situazioni possono essere utili gli oggetti SoftReference, introdotti da Sun con il JDK 1.2, i quali implementano dei riferimenti “a bassa priorità” agli oggetti. In sostanza se gli unici riferimenti ad un oggetto sono del tipo SoftReference, il garbage collector è libero di ignorarli e di effettuare il reclaiming dell’oggetto stesso. 
    Di più, in situazioni di scarsità di memoria, il garbage collector deve - prima di ritornare un OutOfMemoryError - liberare tutti gli oggetti SoftReference, ed eventualmente gli oggetti a cui questi ultimi facevano riferimento. Accanto alla classe SoftReference, ideale per implementare dei sistemi di cache, esistono altre classi affini, tutte sottoclassi di java.lang.Reference, e che vanno studiate con attenzione: se utilizzate in maniera intelligente possono aiutare a ridurre drasticamente alcuni dei problemi legati alla gestione della memoria con cui ci si scontra quotidianamente nella programmazione in Java.
  10. Normalmente il linguaggio di Sun esegue in maniera autonoma il garbage collector allorché la memoria disponibile scende sotto una certa soglia; tuttavia, nella nostra esperienza, può essere utile eseguirlo in maniera esplicita. A questo fine si possono individuare due strategie: la prima consiste nell’eseguire il garbage collector attraverso la chiamata System.gc() in momenti topici del programma, come dopo una stampa o in seguito alla chiusura di un form particolarmente complesso e che ha richiesto l’allocazione di un alto numero di oggetti. In queste situazioni infatti l’utente è più propenso ad ignorare - o comunque considerare accettabili - quelle pause nell’esecuzione che si possono produrre in seguito a tale operazione; il limite di questa strategia è dato dal numero di invocazioni del garbage collector, che tendono a crescere molto rapidamente – quando si è in dubbio, si preferisce infilare un System.gc() nel sorgente, giusto “per sicurezza” – ed a compromettere la leggibilità del codice.

  11. Il secondo approccio, da noi utilizzato in Irmgard, consiste nel delegare l’esecuzione del garbage collector ad un thread separato, il quale esegue tale operazione ad intervalli di tempo prefissati. Questa soluzione, scelta per la sua semplicità ed eleganza implementativa, si è finora dimostrata efficace e tutt’affatto intrusiva. Per chiudere questo breve discorso sulla memoria devo ricordare come Java gestisca tutte le allocazioni ed i rilasci di oggetti all’interno di un pool di RAM allocato all’avvio della JVM ed eventualmente allargato man mano che se ne presenti la necessità, ma che deve comunque rispettare una dimensione massima. Ne consegue che quando un programma in Java “termina” la memoria a disposizione esso non ha necessariamente utilizzato tutta la RAM – fisica o virtuale – disponibile nel sistema, ma ha solo terminato la memoria disponibile all’interno del pool, al cui resizing non può più provvedere dal momento che quest’ultimo ha già raggiunto la dimensione limite ammessa. Tale dimensione è configurabile attraverso un parametro della JVM, tipicamente -Xmx<dimensione massima del pool in byte>. 
    Si noti che la documentazione del JDK 1.2 riporta 16 MB come valore di default per tale parametro mentre, dai nostri test, questo valore sembrerebbe piuttosto dipendere in maniera dinamica dalla RAM del sistema.
  12. Performance nella gestione dei dati valutari: è disponibile una riscrittura della classe BigDecimal realizzata da IBM [2], la quale dichiara di aver ottenuto interessanti miglioramenti nelle prestazioni. Se a ciò si uniscono anche le nuove funzionalità che la stessa IBM ha introdotto in questa classe, ben si comprende la nostra intenzione di valutare seriamente la possibilità di sostituirla in Irmgard all’implementazione standard di Sun.
  13. Affidabilità delle applicazioni: nell’utilizzo prolungato di applicazioni Java di una certa mole – questo discorso si applicava nella nostra esperienza anche ad Irmgard, specialmente nelle sue versioni più recenti – si potevano manifestare, solo in certe macchine, degli errori spuri che non sembravano legati a sezioni specifiche di codice. Nel caso specifico di Irmgard le nostre ricerche non erano riuscite ad identificare la natura di questi errori, che assumevano tipicamente la forma di java.lang.NullPointerException, ma che non si presentavano mai durante le sessioni di debug. Se chi mi legge dovesse un giorno trovarsi nella nostra stessa situazione sappia che tutti i tentativi di rintracciarne le cause sono probabilmente destinati a fallire, e che questi problemi sembrano essere legati in qualche modo a componenti hardware di qualità insufficiente, e che risentono dell’utilizzo che il JDK fa delle risorse della macchina. Per quanto non spiegabili nel dettaglio essi vengono tuttavia risolti aumentando la potenza della macchina in cui si presentano, in termini di CPU o di RAM.

  14. Più in generale, non tutti i PC sono uguali, e Java sembra essere in grado di mettere in risalto queste differenze. In particolare, abbiamo avuto esperienze molto negative per quanto riguarda l’esecuzione di Irmgard su di una serie di sistemi di bassa qualità, tutti della stessa marca, in cui l’esecuzione del programma veniva frequentemente interrotta da errori di protezione (“Questo programma ha eseguito un’operazione non valida.”).
  15. Come ultima astuzia, credo possa essere interessante ricordarne una che non ha, in realtà, prodotto gli esiti sperati. Durante la costante ricerca di soluzioni atte a migliorare le performance dell’applicazione, con il mio team identificammo uno dei possibili colli di bottiglia nel processo di caricamento delle classi. Nella discussione di gruppo che ne seguì (gli anglofoni la chiamerebbero brainstorming) giungemmo all’idea di velocizzare questo processo delegando il compito di caricamento delle classi ad un background thread. In sostanza ogniqualvolta l’applicazione potesse presumere di aver bisogno, nell’immediato futuro, dei servizi di un’altra classe, avrebbe chiesto a tale thread di effettuarne il caricamento. In questo modo quando il bisogno si fosse effettivamente materializzato la classe richiesta sarebbe già stata a disposizione della JVM. Un utilizzo tipico di questa ottimizzazione era per noi costituito dalla dinamica form di riepilogo/form di dettaglio. Esiste infatti una forte possibilità che, successivamente ad ogni richiamo di un form di riepilogo, l’utente dia inizio ad una procedura di inserimento o di modifica che provocherà l’attivazione del relativo form di dettaglio. Perché, allora, non giocare d’anticipo, facendo sì che nel suo costruttore sia lo stesso form di riepilogo a provvedere al caricamento della classe del form di dettaglio ? Perché – e questa è la risposta – nelle classi più significative del programma, quelle cioè caratterizzate dai più alti tempi di attivazione, il tempo necessario al caricamento della classe è una grandezza non significativa se rapportata al tempo di esecuzione del costruttore, dove in sostanza risiede la complessità della classe stessa. Alla luce di questa realtà di fatto decidemmo di abbandonare completamente l’idea del thread caricatore di classi in background: i vantaggi ottenuti erano minimi, sia nel caricamento delle classi da unità di memorizzazione locali sia nel caricamento da file server di rete.


In conclusione vorrei ricordare tre precetti, validi più di qualsiasi trucco o “ricetta magica”: comunicare, comunicare, comunicare. 
In un’attività collaborativa qual è oggi lo sviluppo del software, la gran parte degli errori e delle imprecisioni nelle fasi di modellazione, codifica, debug e manutenzione sono semplicemente legati a specifiche carenze di comunicazione o documentazione – la quale altro non è se non una forma specializzata della prima. Promuovere quindi un forte spirito di gruppo ed incentivare i propri collaboratori a scambiarsi idee, dubbi e pareri, sia direttamente, sia avvalendosi di strumenti informatici quali newsgroup, sistemi di messaging o web forum, arrivando infine a creare una vera e propria libreria on-line di discussioni, problemi e soluzioni, è stata la prima e la più importante ragione del successo del progetto Irmgard.
 
 
 

Epilogo e prospettive future
Immagino che i lettori, vedendo il titolo di questo paragrafo, abbiano tirato un sospiro di sollievo. E, in fondo, a buon diritto. Il materiale presentato in questo articolo è senz’altro corposo, ed è reso ancora più pesante dalla relativa novità dei temi trattati. Tuttavia, nel momento in cui scrivo, Irmgard non è ancora concluso. Diverse parti di codice vanno raffinate, ed altre aspettano di essere trasferite dal mondo un po’ asettico di UML a quello, senza dubbio più movimentato, del linguaggio Java. Infine, nel corso di questi mesi, gli scenari che circondano il nostro programma hanno subito dei mutamenti: alcune tecnologie si sono dimostrate insufficienti, mentre altre stanno rapidamente maturando e promettono interessanti possibilità di integrazione con la nostra applicazione. A mio avviso, è opportuno discuterne, pur se molto brevemente:
 

  • Motori SQL open-source: come già visto nel paragrafo “La gestione dei dati”, la fiducia con cui tutto il mio team guarda al movimento del software open-source ci ha portato a valutare con attenzione, sin dalla fase delle scelte iniziali, la possibilità di sviluppare Irmgard su di un motore SQL non commerciale. A questa possibilità abbiamo dovuto rinunciare, non offrendo in quel momento il mercato una soluzione sufficientemente affidabile e completa. Tuttavia è nostra intenzione ritornare su questo punto, in maniera da poter lasciare ai nostri clienti la scelta tra server SQL commerciali ed open-source, e la nostra attenzione in questo senso si concentra attualmente su InterBase [3]. Questo motore SQL, originariamente prodotto di Borland/Inprise, è caratterizzato da una velocità ed affidabilità estreme. Esso ha tuttavia faticato a trovare una sua nicchia di mercato ed ha avuto – al pari della stessa Borland/Inprise e dei suoi tanti prodotti tecnologicamente validi ma non supportati da un marketing di livello adeguato – una vita piuttosto travagliata. Nei primi mesi di quest’anno la casa madre ha dichiarato la sua intenzione di renderlo open-source e di fornirne un’implementazione definitiva entro la fine dell’estate (ufficialmente InterBase 6.0 dovrebbe venire rilasciato il 30 Giugno 2000).

  • Ed è di fondamentale importanza, a mio avviso, che di questo progetto sia parte integrante anche una suite di driver JDBC (InterClient) ed ODBC, senza i quali un database server è oggi praticamente inutilizzabile. Il suo sviluppo sta avanzando assai rapidamente e sono già disponibili delle beta di buon livello, sia per ambienti Unix che Windows. Purtroppo le più recenti novità introdotte nel server non sono ancora supportate dai driver JDBC, cosa che ci ha sino ad ora impedito di testare in maniera estensiva Irmgard in questo ambiente: tuttavia nel parco dei motori SQL open-source InterBase è senza dubbio l’unico progetto che possa garantire prestazioni e solidità pari ai database server più noti, caratteristica senza dubbia dovuta alle sue origini commerciali, e tutto ci spinge a considerarlo come un sicuro punto di approdo, nel prossimo futuro, per Irmgard.
  • Sviluppo sotto Linux: le superiori affidabilità e performance di questo sistema operativo ci hanno fatto propendere sin dall’inizio, almeno nelle intenzioni, verso tale piattaforma. La recente disponibilità sia di JBuilder 3.5 sia di macchine sufficientemente potenti (ricordo infatti che essendo JBuilder 3.5 implementato interamente in Java si applica ad esso il discorso già fatto per le IDE realizzate in questo linguaggio nel paragrafo “Ambiente di sviluppo”) apre, a nostro avviso, interessanti possibilità in questo senso. È nostra intenzione, entro la fine dell’estate, testare estensivamente la solidità dell’ultima versione della IDE Borland/Inprise, per proseguire eventualmente lo sviluppo di Irmgard in maniera congiunta sotto Linux e Windows. Dai primi test effettuati la potenza richiesta in termini di CPU è effettivamente significativa, ma è comunque limitata alle sole macchine di sviluppo. JBuilder 3.5 si è dimostrato veloce in macchine analoghe a quelle descritte nel suddetto paragrafo “Ambiente di sviluppo”, dotate però, anziché di un Pentium 300-350 MHz, di un Athlon 950 MHz. La distribuzione di Linux da noi utilizzata è il Red Hat (nel momento in cui scrivo arrivata alla versione 6.2). 
  • Aggiornamento del JDK: l’intenzione annunciata da IBM di rendere liberamente disponibile una completa implementazione del JDK 1.3 per tutte le piattaforme direttamente supportate potrebbe riaprire il discorso circa la reale possibilità di valutare, e scegliere, tra diverse JVM in base alle loro caratteristiche di performance e stabilità, non appena avremo avuto modo di verificare la piena compatibilità di Irmgard - e di tutti i suoi componenti - con la nuova versione del JDK. 
  • Evoluzione del sistema di stampe: assieme al deployment server, di cui abbiamo discusso in precedenza, il più importante progetto parallelo “figlio” di Irmgard è quello di un printing system centralizzato basato su XSL Formatting Objects, la sezione del linguaggio XSL [4] destinata alla formattazione di documenti XML. Questa scelta, rispetto all’attuale implementazione in PostScript, offre tutti i vantaggi derivanti dall’utilizzo di uno standard già riconosciuto e consolidato qual è XML: ad esempio la visualizzazione delle stampe, che ora richiede l’installazione su ogni client di Ghostview [5], potrebbe essere reimplementata grazie a Cocoon [6], un tool Java finalizzato alla generazione, gestione e traduzione dinamica o statica di documenti XML nei formati più vari ed alla loro integrazione con server Web, che permetterebbe agli utenti il pieno accesso al loro repository di stampe tramite un qualsiasi browser. 
  • Look & feel personalizzati: sino a pochi mesi or sono uno dei punti fermi nel valore delle interfacce grafiche era dato dalla loro omogeneità, il cui scopo era far sentire a proprio agio l’utente nel passaggio da un’applicazione all’altra rendendolo immediatamente operativo ed abbattendo i costi di training; per questo motivo ogni ambiente grafico degno di questo nome possedeva una Style Guide ufficiale – e Java non faceva eccezione [7] – in cui venivano chiarite la filosofia e lo stile da seguire nella realizzazione di una GUI, arrivando sino al punto di porre delle regole specifiche per la forma ed il contenuto dei dialog più comuni, delle tool bar, e persino nell’utilizzo dei colori e delle forme nelle icone di bottoni e programmi. 

  • Ultimamente essere in atto un’inversione di tendenza – le cui motivazioni appaiono legate in qualche modo al bisogno di una sempre maggiore “riconoscibilità” nell’odierno mercato del software, tanto più alla luce dell’espansione del fenomeno open-source - consistente nel dotare ogni programma di particolarità grafiche uniche (chiamate skin) quali lo sfondo delle finestre, la forma e l’interattività dei componenti dell’interfaccia grafica. Sia io che il mio team siamo sempre stati profondamente contrari a quella che consideriamo una vera e propria “perversione” delle interfacce grafiche e delle ragioni che hanno portato alla loro nascita ed alla loro attuale ubiquità; ci piacque tuttavia l’idea di “giocare” un po’ con Irmgard, e decidemmo così di utilizzarlo per verificare le reali possibilità di utilizzo dei look & feel personalizzati. Abbiamo quindi implementato nella nostra applicazione il supporto per la libreria open-source Skin Look and Feel [8], sperimentandola con diverse combinazioni di forme e colori liberamente disponibili su Internet, e usualmente denominate con il termine theme. I risultati si possono così riassumere:
    • Di oltre 100 skin provati solo pochissimi (meno di 5) sono esteticamente gradevoli, mentre la maggior parte di essi presenta degli accostamenti assolutamente improbabili, i quali rendono la GUI di Irmgard confusa ed inintellegibile. Realizzare un’interfaccia grafica esteticamente piacevole e funzionalmente valida si conferma essere un compito impegnativo, che non si riduce certo a combinare qualche colore ed un paio di poligoni. In tutti questi test un solo skin ci ha colpiti per la sua bellezza e praticità, ed è quello che emula l’interfaccia Aqua [9] realizzata dalla Apple per il suo nuovo Mac OS X.
    • La personalizzazione delle interfacce rallenta in maniera visibile il rendering e l’interazione dei form e di tutti i componenti in essi contenuti.
    • Nessuno skin supporta tutte le nuance rese possibili dalla ricchezza delle API Swing: ad esempio solamente il look & feel Metal – per intendersi, quello standard nei programmi in Java realizzati con tali API – è in grado di fornire una rappresentazione a video del valore della proprietà editable.
    Alla luce di queste considerazioni il meccanismo degli skin ci pare praticamente improponibile, e teoricamente non ben fondato. Tuttavia, se in futuro le problematiche di performance e di funzionalità legate a questo genere di soluzione venissero a cadere, crediamo che sarebbe giusto lasciare agli utenti l’ultima parola, distribuendo una versione di Irmgard comprendente diversi skin, in cui il look & feel Metal verrebbe in sostanza trattato come lo skin di default.


Finalmente, è arrivato il momento di tirare le somme.
Io credo di avere dimostrato, con il lavoro fatto da me e dal mio team in questi mesi, che realizzare applicazioni stand-alone in Java è realmente possibile, e spero – con questo articolo – di aver contribuito a chiarire come sia possibile farlo. Certo, esistono ancora strumenti più veloci ed immediati per la realizzazione di applicazioni legate ad un particolare sistema operativo, ma – non appena si aggiunge sul piatto della bilancia la portabilità garantita da Java - ecco che i rapporti di forze cambiano, e la scelta di questo linguaggio appare naturale.
Per inciso, sul problema del reale supporto multipiattaforma da parte del linguaggio di Sun molto si è detto e scritto; a lungo considerato una chimera, l’incessante evoluzione di questa tecnologia ha radicalmente cambiato, e proprio negli ultimi mesi, lo stato delle cose, ed è con una brevissima storia a tale riguardo che desidero chiudere il mio articolo.

È la storia di un gruppo di persone che ha lavorato per mesi ad un progetto che si diceva essere impossibile, realizzando un programma che avrebbe dovuto in teoria essere compatibile con chissà quanti sistemi operativi, ma che faticava a funzionare anche solamente sotto Windows, che pure era non solo l’ambiente in cui veniva realizzato, ma anche l’unico scenario di test disponibile.
Senza nessun preavviso, negli ultimi giorni del 1999 apparve sui siti di Borland/Inprise e di Sun la prima beta stabile del JDK 1.2.2 per Linux: finalmente, dopo tanta attesa e speranza, consumate cercando quasi quotidianamente novità circa il porting del JDK 1.2 sotto Linux nel sito di Blackdown [10] (l’organizzazione non commerciale formatasi per seguire questo progetto), quelle persone avevano la possibilità di toccare con mano la realtà della più grande promessa di Sun. Ma quel programma, con tutte quelle decine di migliaia di righe di codice, scritte avendo davanti a sé solamente delle workstation Windows, avrebbe davvero funzionato sotto Linux ? Possibile che non ci fosse una, una sola riga, che alla fine li avrebbe traditi ?
Il codice del programma venne immediatamente portato su di una macchina Linux, in cui si installò in tutta fretta il JDK. Quello stesso programma, poi, utilizzava diverse altre librerie: un driver JDBC per l’accesso ad un database SQL - di cui nulla si sapeva circa le sue reali possibilità di funzionamento in quell’ambiente - e, soprattutto, un framework di gestione dati realizzato sì in Java, ma intimamente legato ad un ambiente di sviluppo disponibile solamente sotto Windows. Mentre si copiavano anche queste librerie nella macchina Linux, e si avviava l’applicazione, sviluppatori ed analisti scandagliavano freneticamente la propria memoria e tentavano di applicare ogni loro conoscenza alla ricerca di tutte le possibili dipendenze nascoste in quelle 200,000 righe di codice.
Cos’è ? Dove sarà l’inghippo ? Cosa ci saremo scordati ?

Uso il noi perché l’applicazione al centro di questa storia è Irmgard, e questo gruppo di persone è il mio team. E, se non l’avete ancora capito, Irmgard funzionò veramente sotto Linux, al primo tentativo, comportandosi esattamente come sotto Windows.
Irmgard, pensato e realizzato addirittura prima che si rendesse disponibile un JDK per Linux su cui testarlo, Irmgard, basato su librerie sì in Java, ma di cui era completamente sconosciuta l’effettiva portabilità al di fuori di Windows, funzionò. E funzionò bene.
 

Bibliografia
[1] - Newsgroup Borland/Inprise: news://forums.inprise.com
[2] - BigDecimal IBM: http://www.ibm.com/java/jdk/decimal
[3] - InterBase: http://www.interbase.com
[4] - XSL: http://www.w3.org/TR/xsl
[5] - Ghostview: http://www.cs.wisc.edu/~ghost/ghostview
[6] - Cocoon: http://xml.apache.org/cocoon
[7] - Java Look and Feel Design Guidelines: http://java.sun.com/products/jlf/guidelines.html
[8] - Skin Look and Feel: http://l2fprod.hypermart.net/software/skinlf
[9] - Aqua: http://www.arstechnica.com/reviews/1q00/macos-x-gui/macos-x-gui-5.html
[10] – Blackdown: http://www.blackdown.org
 

Lavinio Cerquetti si occupa di modellazione e sviluppo del software (prevalentemente in Java, C/C++ e Delphi) in ambienti Windows e Unix, concentrandosi sulla programmazione distribuita e di rete. Può essere contattato all’indirizzo di e-mail lavinio@poboxes.com
 

Chi volesse mettersi in contatto con la redazione può farlo scrivendo a mokainfo@mokabyte.it