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:
-
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.
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.
-
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.
-
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.
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.
-
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.
-
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.
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.
-
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.
-
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.
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.
-
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.
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.
-
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.
-
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.
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.”).
-
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
|