Il programmatore e le sue API

XX parte: realizzare la GUI web con GWTdi

Il Google Web Toolkit è un framework con cui è possibile realizzare applicazioni AJAX in modo rivoluzionario: il programmatore implementa l‘interfaccia grafica in linguaggio Java utilizzando un set di componenti forniti dal kit di sviluppo. Solo in fase di compilazione verrà creato il codice JavaScript che sarà eseguito nel runtime del browser.

Introduzione

Dopo aver speso anni a imparare nel profondo i vari dettagli della programmazione a oggetti, ci troviamo di fronte a una piccola rivoluzione che potrebbe vanificare i nostri sforzi (almeno per quanto riguarda lo sviluppo di applicazioni web AJAX). Scrivere migliaia di righe di codice JS non è allettante per nessuno. Per fortuna GWT pare aver risolto brillantemente questo problema.

GWT

Il Google Web Toolkit è un framework introdotto dalla casa americana che gli dà il nome. Con GWT è possibile realizzare applicazioni AJAX in maniera completamente rivoluzionaria: il programmatore infatti implementa la interfaccia grafica in linguaggio Java utilizzando un set di componenti forniti dal kit di sviluppo e solo in fase di compilazione questo dà luogo al corrispondente codice JS che verrà eseguito all'interno del runtime del browser.

Il programmatore non vede alcun dettaglio del codice JavaScript prodotto, ma si limita a sviluppare una applicazione grafica simile a Swing o SWT o AWT.

La cosa interessante è che trattandosi di codice che verrà eseguito all'interno di un browser (sotto forma di script JS) l'approccio è proprio quello di sviluppare una applicazione client side che per comunicare con lo strato di backend deve far uso di un apposito framework di comunicazione remota (GWT RPC), basato su un interessante e potente meccanismo di invocazione asincrona: se infatti il client vuole invocare un qualsiasi metodo del server, deve creare un apposito oggetto AsnycCallback in grado di poter gestire la risposta che arriverà in un secondo momento. GWT è infatti un framework che non solo traduce Java in JS, ma in cui la tecnologia sottostante è in tutto e per tutto AJAX, ovvero HTTP asincrono.

Il lavoro fatto dal gruppo di sviluppo di Google è veramente impressionante e molto concreto. Essendo lo scopo di questi articoli non quello di spiegare nei dettagli come funzioni GWT ma piuttosto fornire un esempio concreto di integrazione del framework all'interno della nostra applicazione che stiamo presentando ai fini metodologici di questa lunga serie, mostreremo come con poca fatica, e dopo aver letto qualche tutorial, si è in grado di realizzare la versione GWT della sua interfaccia grafica. Per chi fosse interessato ad approfondire l'argomento, raccomandiamo la lettura dell'articolo pubblicato qualche tempo fa su MokaByte [MOKA] oltre all'efficace tutorial (anche se un po' ristretto) pubblicato sul sito ufficiale (vedi [GWT]).

Un esempio semplice e chiaro

Per capire in modo efficace e rapido la semplicità e la potenza di questo nuovo framework, partiamo subito con un esempio molto semplice che simuli la realizzazione di una parte dell'interfaccia grafica del nostro applicativo Beelog, dedicato alla gestione delle attività di apicoltura.

Si parte dalla definizione, come da specifica, del cosiddetto entrypoint (derivando dalla classe com.google.gwt.core.client.EntryPoint) e del metodo onLoad() che viene eseguito automaticamente al momento della istanziazione della GUI dell'entry point. Il codice evidenzia i passaggi fondamentali per poter creare la GUI in GWT, eseguire l'invocazione del lato di backend e popolare una tabella con il risultato. In questo caso si simula la gestione di una maschera di ricerca tramite la quale interrogare il back end (in cui la logica di ricerca è incapsulata da un session bean esposto sulla frontiera del layer tramite un session facade [SF]) per ricavare un elenco di apiari (rappresentati da una lista di oggetti ApiaryList). Si noti come la comunicazione avvenga tramite l'invocazione di una GWT-RPC ovvero tramite invocazione asincrona di un oggetto remoto. La GUI, dopo aver invocato il server non attende la risposta ma ritorna subito alla interattività con l'utente; la risposta viene assegnata a un oggetto AsyncCallback che ingloba al suo interno l'oggetto restituito dal server, in questo caso una lista di ApiaryVO.

L'oggetto che viene invocato con una chiamata RPC è il BeelogServiceAsync che espone un solo metodo. Il codice riportato è sufficientemente commentato per permetterne una corretta compensione:

public class Beelog implements EntryPoint {
    // ricava il reference all'oggetto remoto per
    // le chiamate GWT-RPC con la controparte
    // server side
    private final BeelogServiceAsync beelogService = GWT.create(BeelogService.class);
    // il pannello verticale servirà come pannello principale
    // su cui inserire tutti i vari widget
    private VerticalPanel verticalPanel = new VerticalPanel();
    private FlexTable apiaryListFlexTable = new FlexTable();
    private HorizontalPanel searchPanel = new HorizontalPanel();
    private TextBox searchTextBox = new TextBox();
    private Button searchButton = new Button("Cerca");
    private Label systemMessageLabel = new Label();
    
    // il metodo che provvede al caricamento e inizializzazione
    // della GUI da mostrare all'utente
    public void onModuleLoad() {
        // Crea la tabella con i nomi delle colonne
        // in questo caso la griglia verrà popolata
        // in modo rudimentale associando cella per cella
        // ai valori che risulteranno dalla ricerca server
        // side
        apiaryListFlexTable.setText(0, 0, "Nome");
        apiaryListFlexTable.setText(0, 1, "Location");
        apiaryListFlexTable.setText(0, 2, "Note");
        apiaryListFlexTable.setText(0, 3, "View");
        // aggiunge la gestione dell'evento click
        // sul pulsante "cerca"
        searchButton.addClickHandler(new ClickHandler() {
            public void onClick(ClickEvent event) {
                searchApiaries();
            }
        });
        // aggiunge il pannello della ricerca al
        // pannello principale
        verticalPanel.add(searchPanel);
        // Assembla il panel con i componenti per la
        // effettuare la ricerca
        searchPanel.add(searchTextBox);
        searchPanel.add(searchButton);

        // Assembla il main panel
        verticalPanel.add(apiaryListFlexTable);
        verticalPanel.add(systemMessageLabel);
        // inserisce il pannello nel punto della pagina HTML
        // identificato dal div con id="apiaryList"
        RootPanel rootPanel = RootPanel.get("apiaryList");
        rootPanel.add(verticalPanel, 50, 25);
        verticalPanel.setSize("203px", "32px");
    }

    // metodo di invocazione asincrona dello strato di back end
    // notare la definizione inner della classe AsyncCallback
    // i due metodi onFailure e onSuccess che verranno invocati
    // in caso si risposta negativa o positiva del back end
    private void searchApiaries() {
        // questo oggetto permette di ricevere una risposta
        // differita nel tempo da parte del back end.
        // verrà passato al metodo RPC
        AsyncCallback callback = new AsyncCallback() {
            // qui si deve gestire l'errore con un messaggio da inviare
            // utente es. con un pop, oltre ad eseguire un
            // adeguato log dell'evento.
            public void onFailure(Throwable caught) {
                ....
                ....
            }
            // se la risposta dal server è positiva
            // si esegue aggiornamento della tabella
            // con i valori relativi alla ricerca.
            // notare che il valore ritornato viene
            // inserito all'interno dell'oggetto di
            // callback secondo il pattern Template
            public void onSuccess(List apiaries) {
                updateTable(apiaries);
            }
        };

        // per eseguire la ricerca di back end si potrebbe ricavare
        // il valore di un campo testo da passare al metodo
        // remoto: in questo caso si ipotizza di cercare tutti gli
        // apiari associati a un determinato apicoltore
        final String beekeeperName = searchTextBox.getText().toUpperCase().trim();
        searchTextBox.setFocus(true);
        // il metodi di callback RPC per specifica deve ricevere i parametri di
        // invocazione seguiti obbligatoriamente dall'oggetto
        // callback che verrà usato per la risposta asincrona
        beelogService.getApiaryList(beekeeperName, callback);
    }

    // questo metodo permette di aggiornare la tabella con un
    // elenco (list) di oggetti Value Object
    // in questo caso il widget grafico usato per visualizzare la
    // tabella viene popolato in modo rudimentale cella per cella
    // Utilizzando un approccio più moderno con un componente
    // databound migliora per certo la stesura del codice
    void updateTable(List apiaries) {
        ApiaryVO apiaryVO;
        List listApiaries = (List) apiaries;
        for (int i = 0; i < apiaries.size(); i++) {
            apiaryVO = listApiaries.get(i);
            apiaryListFlexTable.setText(i, 0, apiaryVO.getName());
            apiaryListFlexTable.setText(i, 1, apiaryVO.getLocation());
            apiaryListFlexTable.setText(i, 2, ""+apiaryVO.getSize());
        }
    }
}

Il codice riportato dovrebbe essere piuttosto chiaro e grazie ai commenti la sua comprensione piuttosto semplice. Per completezza riportiamo il codice relativo alle classi di backend. Partiamo dalla classe che implementa il lato backend; al suo interno troviamo il codice che implementa la chiamata (qui solo simulata) verso il componente che a sua volta implementa la logica di business e inserito nel business logic layer. In pratica, al posto dei puntini si dovrebbe inserire l'oggetto Business Delegate il quale a sua volta lavora con un session facade remoto (schema ampiamente descritto nelle puntate precedenti di questa serie).

public class BeelogServiceImpl extends RemoteServiceServlet implements BeelogService {
    public List getApiaryList() {
        ArrayList apiaryList = new ArrayList();
        // popola la lista tramite una
        // ricerca sullo strato di persistenza
        // utilizzando un EJB session facade
        ...
        return apiaryList;
    }
}

Si noti come, in base alla specifica, per poter realizzare un oggetto remoto è necessario implementare un servizio remoto ed esporre i metodi che si vogliono pubblicare per l'invocazione GWT-RPC: tutto questo si ottiene con la definizione delle seguenti interfacce (implementate dalla classe appena vista):

public interface BeelogServiceAsync {
    void getApiaryList(AsyncCallback callback);
}

 

public interface BeelogService extends RemoteService {
    List getApiaryList();
}

Anche in questo caso si noti la presenza dell'oggetto AsyncCallback nella firma dei metodi. Per maggiori dettagli sulla tecnica di funzionamento di RPC e sulla sua implementazione, si faccia riferimento al tutorial presente sul sito di Google (vedi [GWT]).

Utilizzare un super-framework: Ext-GWT, GWT-Ext, SmartGWT

Un fattore piuttosto interessante legato a GWT è la presenza di una community molto attiva che sta rilasciando in modo open source, gratuitamente o a pagamento, alcuni progetti che si aggiungono a quanto proposto da Google. In tal senso molto interessanti sono i vari framework che si vanno a sovrapporre al kit base e che permettono di usare set di componenti molto versatili e potenti.

Nell'ambito di una consulenza svolta proprio di recente, ho potuto fare alcune valutazioni per identificare lo strumento migliore con cui procedere poi allo sviluppo di una piccola applicazione prototipale. In tale contesto si sono fatte delle prove piuttosto dettagliate con alcuni di questi super-framework (il termine "super" non indica le funzionalità eccezionali ma piuttosto il fatto che sono strumenti che si agganciano "sopra" GWT che è a sua volta un framework, in una parola sono "sopra-framework" non "framework super"...), ricchi di componenti grafici avanzati e dotati di una intelligente gestione di effetti grafici e di connessione con i dati (i cosiddetti oggetti databound).

Lo scenario attuale propone tre prodotti: GWT-EXT basato sulle librerie JavaScript JS-EXT (e che per problemi di licenza e sviluppo legati a questa libreria verrà presto messo fuori produzione), EXT-GWT (noto anche come GXT che, nonostante il nome, ha poco a che fare con il precedente) e infine il recente SmartGWT. Personalmente ho nutrito una compulsiva affinità con quest'ultimo anche perche' nato su iniziativa dell'ex-capo progetto di GWT-EXT, il quale ha abbandonato il progetto precedente per i problemi di cui sopra.

Per quanto concerne GXT invece possiamo dire che si tratta del progetto al momento più maturo e ricco di componenti e può essere utilizzato con l'unico plugin visuale degno di nota attualmente disponibile sul mercato (vedi oltre). GXT offre anche un meccanismo molto potente e raffinato di gestione del modello MVC unitamente a un sistema molto efficace e flessibile di gestione dei messaggio e degli ascoltatori dei medesimi (pattern Observer).

Nel caso siate interessati a sviluppare una applicazione ricca di componenti grafici, con un buon supporto per componenti databound e magari con un plugin visuale (o almeno quasi visuale), con la possibilità di disporre di un MVC integrato, consiglio di passare quanto prima alla adozione di GXT o SmartGWT; il primo è sicuramente più stabile e ricco.

Proprio mentre stavo ultimando questo articolo è stato annunciato GWT 2.0 che pare innovare non poco l'architettura sottostante di GWT: purtroppo i vari GXT, SmartGWT non hanno ancora dato nessuna notizia circa un eventuale allineamento dei relativi prodotti con questa nuova release.

I plugin per GWT

Fintantoche' si parla di sviluppo per AJAX, GWT pare essere veramente avanti come idea e come soluzione proposta. Purtroppo, quando poi si passa alla messa in pratica, iniziano le note dolenti: nonostante che GWT sia sul campo ormai da diverso tempo, non possiamo certo dire che siano disponibili strumenti di sviluppo adeguati alle potenzialità del framework. Gli ambienti di sviluppo più utilizzati in questo momento sono Eclipse e NetBeans i quali di base non offrono alcun supporto ne' per GWT ne' tantomeno per uno dei super-framework elencati poco sopra; si deve ricorrere ai plugin.

In questo momento il plugin più famoso e più utilizzato è il GooglePlugin (alla faccia della fantasia...) che si installa su Eclipse e fornisce poi una serie di wizard per la creazione di un progetto GWT e per il build/run in modo semplificato. Si tratta di uno strumento sicuramente utile anche se non offre alcune funzionalità visuali. Il corrispondente per NetBeans (gwt4nb) offre più o meno le stesse funzioni anche se ho buttato tutto dalla finestra quando, al terzo tentativo di far partire il progetto, ottenevo un errore poco comprensibile in console; certamente il problema era nella piattaforma/configurazione della macchina su sui ho fatto le prove, ma la mia pazienza con i plugin alle soglie del 2010 (quando certi dettagli legati alla piattaforma di sviluppo dovrebbero essere ormai superati) è veramente al minimo storico.

Se, infine, vi interessa un plugin visuale che offra un minimo di funzionalità WYSIWYG, ho provato Window Builder - GWTDesginer (vedi [WBuild]) plugin per Eclipse che permette di disegnare le GUI riducendo i tempi di sviluppo: il tool non è eccezionale ma presenta anzi alcune pecche di stabilità nel 2-way-programming; ma, vista la totale assenza di alternative, possiamo adottarlo come strumento di lavoro principe.

Non sono ancora convinto che per lavorare in GWT sia necessario un tool visuale: se si organizza bene l'applicazione e la struttura delle varie componenti di UI, si può correre il rischio di lavorare direttamente sul codice. Ma lascio al lettore la scelta dell'approccio preferito.

GWT in definitiva

Dovendo sintetizzare in poche parole cosa sia GWT e come lo possiamo valutare, direi che si tratta sicuramente di una tecnologia molto interessante; per certi versi la si potrebbe considerare come la risposta più semplice a un problema comune (e complesso) se visto dal punto di vista dello sviluppatore, vale a dire poter realizzare applicazioni AJAX senza dover impazzire con tonnellate di JavaScript e di altre diavolerie, ma rimanendo in ambito Java. Mi spingerei quasi a considerare GWT come un "pattern", che per definizione è una soluzione ottimizzata a un problema ricorrente. Il fatto è che ancora non sappiamo quanto tale soluzione sia effettivamente ottimizzata.

Il nome alle spalle è di quelli che possono imporre trend e direzioni al web e al mondo dell'IT in genere, fattore questo da non sottovalutare nel momento in cui si deve fare una scelta circa l'adozione di una soluzione di tecnologia o di strumento. Al momento trovo molto interessante la presenza dei progetti di super-framework (descritti al paragrafo precedente), anche se colgo il rischio di una possibile complicazione dello scenario derivante dalla mancanza di un coordinamento unico dei vari progetti. Se attualmente GXT (EXT-GWT) pare essere lo standard de facto, la presenza di nomi illustri nel team di SmartGWT dovrebbe colmare il gap attuale in breve tempo.

Non sono invece ancora soddisfatto della platea dei tool di sviluppo o meglio dei vari plugin disponibili per i due IDE più usati al momento: voto insufficiente per il plugin di NetBeans (almeno quello da me provato anche se, ad onor del vero, non di recente) mentre trovo piuttosto macchinosi quelli per Eclipse (non parlo del tool di sviluppo visuale, ma proprio dei vari plugin necessari per creare lo scheletro di progetto GWT). Non è impossibile lavorare ma certo non sono del tutto chiare le procedure per abilitare un progetto a divenire un GWT project (magari supportando anche GXT).

In definitiva, nel caso di GWT per una volta voglio sbilanciarmi e dare una votazione ampiamente positiva, magari togliendo qualche punto a JSF che non mi appassiona più di tanto.

Riferimenti

[MOKA] Ivan Diana, "Introduzione a GWT", MokaByte 139, Aprile 2009

http://www2.mokabyte.it/cms/article.run?articleId=ZRN-948-15E-2M5_7f000001_23998236_9088b3a8

[SF] Giovanni Puliti, "Il programmatore e le sue api - VI parte: L'architettura del sistema", MokaByte 132, Settembre 2008

http://www2.mokabyte.it/cms/article.run?articleId=95B-78Q-PVE-8CQ_7f000001_10911033_55ab6d7f

[GWT] Google Web Toolkit

http://code.google.com/webtoolkit/

[GXT] Ext-GWT: Rich Internet Application Framework for GWT

http://www.extjs.com/products/gxt/

[GWT-Ext] GWT-Ext Widget Library

http://code.google.com/p/gwt-ext/

[SmartGWT] Smart GWT: GWT API's for SmartClient

http://code.google.com/p/smartgwt/

[WBuild] WindowBuilder Pro

http://www.instantiations.com/windowbuilder/pro/index.html

 

 

 

 

Condividi

Pubblicato nel numero
146 dicembre 2009
Giovanni Puliti lavora come consulente nel settore dell’IT da oltre 20 anni. Nel 1996, insieme ad altri collaboratori crea MokaByte, la prima rivista italiana web dedicata a Java. Da allora ha svolto attività di formazione e consulenza su tecnologie JavaEE. Autore di numerosi articoli pubblicate sia su MokaByte.it che su…
Articoli nella stessa serie
Ti potrebbe interessare anche