Introduzione
Lo scorso mese un articolo di Sandro Pedrazzini ha introdotto
la tecnologia basata sul concetto di "rich thin client"
(RTC, [3]). Con questo articolo si intende continuare idealmente
il discorso, mostrando un esempio pratico di utilizzo.
Tra i prodotti elencati nell'articolo di Pedrazzini, figurava
anche Canoo ULC, di cui è stata riportata in modo sommario
l'architettura, perché ritenuta particolarmente interessante.
Questa permette infatti a chi sviluppa un'applicazione di
utilizzare oggetti in rete in modo completamente trasparente,
senza doversi cioè preoccupare del fatto che alcuni
elementi dell'applicazione (widget, nel caso specifico) si
trovino sul client e altri sul server.
Il design pattern utilizzato in ULC per rendere trasparente
la comunicazione è il pattern Half-Object, anche noto
in letteratura come HOPP (Half-Object Plus Protocol ([2]).
In questo articolo ci preme soprattutto mostrare gli effetti
di questo pattern sulla libreria RTC in questione. Lasciamo
ad altri, in un eventuale prossimo articolo, l'incombenza
di entrare nei dettagli dell'architettura e di mostrare in
modo più specifico l'implementazione del pattern.
Motivazione:
libreria RTC
L'articolo dello scorso mese ha spiegato come con l'aumentare
della popolarità e della diffusione del paradigma Web
molte nuove applicazioni siano state scritte con interfaccia
Web e molte vecchie siano state adattate per fornire un'interfaccia
Web di tipo Html.
In molti casi, ci si è però accorti che questo
approccio crea problemi di sviluppo e di manutenzione. Il
motivo principale è determinato dal fatto che Html
non è pensato per realizzare e gestire interfacce utente
(GUIs) altamente interattive. Per risolvere i limiti di Html,
gli sviluppatori di applicazioni Web sono così costretti
ad integrare numerose tecnologie, come applet Java, Html,
JSP, JavaScript, ActionScript, istanze XML proprietarie, ecc.,
che creano miscugli di codice e non fanno altro che complicare
il lavoro di sviluppo e, soprattutto, di manutenzione.
Una possibile soluzione a questo problema è naturalmente
quella di tornare all'idea di rich client, utilizzando tecnologie
come Swing o SWT, che offrono maggiore interattività,
con sistemi di eventi avanzati e widgets preconfezionati a
disposizione. Il prezzo da pagare in questo caso è
però la presenza di codice applicativo sul client (fat
client), con conseguenze sia nello sviluppo (separazione client/server)
che nel deployment (l'applicazione non risiede più
interamente nel server, perciò ogni modifica all'applicazione
comporta un aggiornamento del client).
La soluzione mostrata era quella di una libreria RTC (rich
thin client) che abbini i vantaggi di entrambi i paradigmi,
permettendo l'utilizzo di interfacce grafiche avanzate (rich),
senza dover installare sul client codice applicativo. Abbiamo
anche visto come Java, grazie alla sua onnipresenza, abbia
giocato e giochi tuttora un ruolo determinante nello sviluppo
e nella diffusione di queste librerie.
Trasparenza
nella comunicazione
Come viene realizzata in ULC ([4]) l'idea di gestire l'applicazione
interamente sul server, pur permettendo il suo utilizzo con
architettura client/server, in cui il client rappresenta la
GUI dell'applicazione?
Il concetto è relativamente semplice e viene reso possibile
proprio dall'utilizzo del pattern Half-Object.
Questo pattern permette a ULC di offrire una API per Swing
a livello server. Chi sviluppa utilizza questa API direttamente
nel server (che corrisponde all'applicazione), senza nemmeno
specificare la separazione client/server. È come se
i widgets della GUI venissero utilizzati nel server. L'effettiva
separazione tra client e server avviene solo durante l'esecuzione.
In runtime, ogni oggetto si suddivide in due: una parte client
(client half object) e una parte server (faceless server half
object), che interagiscono tra di loro, come mostrato in fig.
1.
La gestione sul client dei relativi mezzi oggetti avviene
tramite il cosiddetto UI Engine, un motore generico di interfaccia,
non più grande di 300 KB. La sua funzione è
paragonabile a quella del browser per le applicazioni Web.
Figura 1
Il
pattern permette di nascondere l'elemento di comunicazione
tra client e server, in modo tale che lo sviluppatore possa
utilizzare i widgets di Swing come se questi si trovassero
sul server.
L'implementazione del pattern prevede un proxy per ogni componente
Swing. Il proxy rappresenta quindi le componenti Swing e viene
suddiviso in due metà oggetti (half-object): la metà
sul client interagisce effettivamente con Swing, mentre la
metà sul server mette a disposizione la API per lo
sviluppatore.
Considerando la presenza di uno standard JRE su client e su
server, questo approccio permette di lavorare con una libreria
relativamente ridotta e un design pulito.
Modello object-oriented
La funzionalità richiesta dalla GUI viene realizzata
con codice Java object-oriented, al posto di miscugli di codice
e tecnologie eterogenee. Il vantaggio per lo sviluppatore
è evidente: può lavorare con un insieme minimo
di utensili e un modello di programmazione semplice. In realtà
sviluppa un'applicazione come se questa fosse stand-alone,
diventando client/server unicamente in fase di esecuzione.
Come detto, affinchè questo sia possibile il client
deve possedere il motore di presentazione (UIEngine). Questo
motore, esattamente come un browser per Html, non contiene
codice specifico di un'applicazione e può quindi essere
utilizzato con ogni tipo di programma ULC. Siccome lo UIEngine
è realizzato completamente in Java, può essere
eseguito come applet (quindi senza necessità di installazione),
come applicazione stand-alone, oppure in ambiente JNLP, cioè
con il meccanismo di Java Web Start a disposizione del linguaggio
Java.
Scaricamento dei dati "lazy"
La trasparenza nella comunicazione ha parecchi effetti interessanti.
Tra questi la possibilità di gestire il traffico server-client
dei dati in modo "lazy". Cosa significa? Significa
che unicamente gli elementi effettivamente visualizzati vengono
scaricati sul client. Gli altri rimangono sul server e vengono
inviati al client unicamente in caso di bisogno. Facciamo
un esempio: un utente sta visionando nella sua interfaccia
grafica i dati di una lista con migliaia di elementi. Solo
i dati che vede nella sua finestra sono stati effettivamente
scaricati (10, 20, a dipendenza della dimensione del frame).
Gli altri non si sono mossi dal server. Si sposteranno verso
il client solo se l'utente effettuerà un'operazione
di scrolling. Questo aiuta a spiegare come mai un'applicazione
sviluppata sul modello RTC genera in media molto meno traffico
di una tradizionale applicazione Web con interfaccia Html,
in cui ad ogni interazione ci sono paginate di codice che
vengono inviate al client.
Utilizzo
L'utilizzo di widget di una libreria RTC implementata con
il pattern half-object può essere resa simile all'utilizzo
di semplici elementi di interfaccia Swing, mantenendo nascosta
la separazione client/server il fatto che la metà oggetto
sul client in realtà delega la funzionalità
grafica ad un elemento Swing, attraverso il pattern Adapter
([1]).
Nel caso di ULC, per ogni widget Swing (classi con il nome
che inizia con "J", ad esempio JButton), esiste
una corrispondente classe che inizia con "ULC" (ad
esempio ULCButton), con la cui API lo sviluppatore deve interagire.
(In realtà non sempre è possibile questa conversione,
sia perché è stata data maggiore attenzione
all'ortogonalità delle singole API, sia perché
essendo diverso il contesto di programmazione, ci possono
essere alcune operazioni particolari che hanno senso unicamente
in un contesto client/server. Si tratta però di una
percentuale relativamente ridotta, rispetto a quanto può
essere convertito direttamente).
Entriamo nel dettaglio di alcuni semplici esempi.
Confronto con Swing
Per fare un confronto a livello di codice con l'utilizzo di
Swing, consideriamo l'esempio di una finestra contenente un
pulsante.
Ecco la versione Swing della finestra, realizzata con una
nuova classe che modelli la finestra stessa ereditando da
JFrame.
public
class JButtonWindow extends JFrame{
JButtonWindow(String title){
super(title);
JButton button = new JButton("First
Appearance");
getContentPane().add(button);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
}
}
Questo
diventa il codice necessario per instanziare un oggetto e
visualizzare la finestra:
JFrame
frame = new JButtonWindow("JButtonWindow");
frame.setVisible(true);
Come
detto, se per ogni classe Swing che rappresenta un widget,
ne esiste una corrispondente ULC, questo significa che nel
nostro caso avremo almeno ULCFrame e ULCButton a disposizione.
Ecco quindi il codice corrispondente in ULC:
public
class ULCButtonWindow extends ULCFrame{
ULCButtonWindow(String title){
super(title);
ULCButton button = new ULCButton("First
Appearance");
add(button);
setDefaultCloseOperation(ULCFrame.TERMINATE_ON_CLOSE);
}
}
Le
differenze sono dovute a scelte di semplificazione (add()
chiamata direttamente sul frame, invece che sul content pane),
o perché, basandosi ULC su Swing, ci sono operazioni
che sono implicite (chiamata pack()).
Per il resto non ci sono grosse differenze. Da notare: il
fatto che l'applicazione giri completamente sul server e che
in runtime invii messaggi al client per mostrare la GUI non
è visibile nel codice, e nemmeno lo sviluppatore deve
preoccuparsene.
Questo è il codice di chiamata
ULCFrame
frame = new ULCButtonWindow("ULCButtonWindow");
frame.setVisible(true);
Aggiunta
di eventi
Vediamo un ulteriore aspetto che dimostri quanto la trasparenza
riguardo l'architettura client/server sia totale. Aggiungiamo
un'azione al pulsante appena creato. Facciamo in modo che
il testo del pulsante venga modificato al primo click.
Ecco la versione Swing:
class
ChangeTextAction implements ActionListener{
public
void actionPerformed(ActionEvent event){
JButton
source = (JButton)event.getSource();
source.setText("Changed...");
}
}
Ed
ecco la versione ULC:
class
ChangeTextAction implements IActionListener{
public
void actionPerformed(ActionEvent event){
ULCButton
source = (ULCButton)event.getSource();
source.setText("Changed...");
}
}
Se
consideriamo che nello scenario di deployment più tipico
l'azione viene eseguita sul server, può sorprendere
come questo fatto non abbia nessun impatto a livello di codice.
L'azione viene eseguita sul server e le modifiche nella metà
del widget che riguardano l'interfaccia grafica vengono inviate
all'engine grafico del client.
ULC mette a disposizione dello sviluppatore degli elementi
di simulazione della comunicazione client/server. Questo significa
che lo sviluppo avviene su un'unica macchina, esattamente
come se si trattasse di un'applicazione stand-alone. Il codice
sopra dimostra perciò come una libreria RTC potrebbe
essere facilmente utilizzata al posto di Swing anche per applicazioni
stand-alone. Solo nel caso in cui, per regioni di scalabilità,
si desiderasse introdurre un'architettura distribuita, si
passerebbe ad un deployment client/server, senza toccare una
sola linea di codice.
Esempio applicativo
I produttori di librerie RTC forniscono vari esempi di applicazioni
implementate con la loro tecnologia.
Restando in un ambito conosciuto, mostriamo l'interfaccia
grafica di un negozio virtuale.
Figura 2
(clicca sull'immagine per ingrandire)
L'applicazione
in questione è messa a disposizione come servlet e
la comunicazione con il client avviene via http (o https).
Da un punto di vista architetturale si tratta quindi di un'applicazione
Web a tutti gli effetti, che però, invece di un browser,
necessita dell'engine grafico di 300 KB.
L'applicazione, con una serie di altri esempi, può
essere facilmente provata all'indirizzo http://www.canoo.com/ulc/demos/onlineshop.html
Da notare come l'inserimento di un elemento nel carrello della
spesa possa essere eseguito con una semplice operazione di
drag-and-drop.
Conclusioni
In questo articolo abbiamo ripreso il discorso delle architetture
rich thin client, proposte in un articolo di Pedrazzini del
mese scorso [3]. Abbiamo parlato del pattern Half-Object,
anche conosciuto come pattern HOPP, senza entrare nel dettaglio
della sua architettura, ma osservando il suo beneficio maggiore:
la trasparenza della comunicazione tra elementi distribuiti.
Questo pattern sta alla base dell'architettura di Canoo ULC,
menzionata, assieme ad altre, nell'articolo dello scorso mese
a proposito di tecnologie rich thin client.
Con questo modello è possibile rendere trasparente
il fatto che un oggetto esista e venga utilizzato in più
spazi di indirizzamento. Grazie a questo pattern una libreria
RTC può utilizzare widget grafici nell'applicazione
lato server, senza preoccuparsi del fatto che poi questi in
runtime abbiano una loro rappresentazione grafica sul client.
Bibliografia
[1] Gamma et al.: Design Patterns, Elements of Reusable Object-Oriented
Software, Addison Wesley, 1995.
[2] Meszaros G.: Pattern: Half-Object+Protocol Pattern, in
Coplien, Schmidt: "Patterns Languages of Program Design",
Addison-Wesley, Reading 1995.
[3] Pedrazzini S.: Java: la piattaforma ideale per architetture
"rich thin client".
[4] UltraLightClient, Canoo ULC: http://www.canoo.com/ulc
|