Le attuali applicazioni Web (2.0 si intende), sono ricche di funzioni lato client e relativo codice JavaScript da scrivere. L'aumento di complessità fa aumentare anche la probabilità di errore e può rallentare la produttività degli sviluppatori. Inoltre bisogna sempre tener conto della compatibilità cross-browser. Fortunatamente esistono alcuni framework che ci permettono di aggirare molti di questi problemi.
Il Google Web Toolkit (GWT), è un framework open source, sviluppato dalla Google, che permette di creare applicazioni Web con AJAX, scrivendo le proprie pagine esclusivamente in linguaggio JAVA. Sarà compito della libreria GWT tradurre il codice Java e produrre le pagine HTML e JavaScript corrispondenti.
Possiamo scaricare il toolkit dal sito ufficiale del progetto dove, per rendere il tutto ancora più semplice, Google ha messo a disposizione anche plugin per Eclipse.
In questo articolo, installeremo il plugin e realizzeremo la classica applicazione "Hello World" per iniziare a prendere familiarità con il framework.
Installare il plugin su Eclipse
L'installazione del plugin è molto semplice. Dal menu Help
di Eclipse (Ganymede), selezioniamo la voce Software Updates...
.
Nella finestra che appare, selezioniamo la tab Available Software
e clicchiamo sul pulsante Add Site
. Quindi aggiungiamo l'URL:
URL del Google Web Toolkit per Eclipse 3.4 (Ganymede)
http://dl.google.com/eclipse/plugin/3.4
È disponibile anche la versione del plugin per Eclipse 3.3 (Europa) all'URL:
URL del Google Web Toolkit per Eclipse 3.3 (Europa)
http://dl.google.com/eclipse/plugin/3.3
Una volta aggiunto il sito, possiamo selezionare il plugin e l'SDK nell'elenco degli aggiornamenti disponibili e cliccare su Install
per dare il via all'installazione. È consigliato scaricare, oltre al plugin e all'SDK, anche l'application engine. Questo componente non è fondamentale, ma torna utile per testare l'applicazione.
Una volta scaricato ed installato il tutto, è necessario riavviare l'ambiente per rendere attive le modifiche. Al riavvio, noteremo un nuovo gruppo di icone nella barra degli strumenti.
Creare un progetto
Per creare il nostro primo progetto GWT, clicchiamo sulla prima dell nuove icone (New Web Application Project
).
Assegniamo un nome al progetto (HtmlExample
) e stabiliamo un package di riferimento (it.html
). La sezione Google SDKs, permette di selezionare la versione GWT da utilizzare e l'application Engine con il quale testare il nostro applicativo. Lasciamo invariate tali opzioni e confermiamo con il tasto Finish.
GWT genererà la struttura del nostro progetto, tralasciamo per un attimo i dettagli e vediamo come testare l'applicazione appena creata. Nella barra degli strumenti dobbiamo ancora esplorare le due icone:
- GWT Compile, che permette di compilare l'applicazione e generare il codice HTML+JavaScript corrispondente
- Deploy to App Engine, che permette di effettuare il deploy dell'applicativo all'interno dell'application Engine di Google
Le applicazioni sviluppate mediante Google Web Toolkit possono essere eseguite in due modalità differenti: Hosted mode e Web mode.
Mediante la modalità Hosted mode, l'applicazione viene lanciata come bytecode Java all'interno della Java Virtual Machine. In questa modalità, sono utilizzabili tutti gli strumenti di debug disponibili nell'ambiente di sviluppo. Come supporto alla modalità Hosted Mode, GWT fornisce uno speciale Browser Web.
Mediante la modalità Web mode, invece, l'applicazione viene eseguita come una classica applicazione Web. Il compilatore GWT (Java-to-JavaScript) genera il codice HTML e JavaScript, interpretabile dal browser Web.
Per eseguire la nostra applicazione in hosted mode, clicchiamo col tasto destro sul nome del progetto, poi sulla voce Run As > Web Application
.
Eclipse visualizzerà due nuove finestre: la prima rappresenta la console dell'application engine di google (l'analogo di Tomcat o Jboss), mentre la seconda è il browser di Google nel quale possiamo ammirare la nostra Web application, costituita da una classica pagina contenente un semplicissimo campo di testo. Inserendo il proprio nome e cliccando sul tastino Send
, viene visualizzato un semplice messaggio di notifica.
Ora che abbiamo visto il risultato finale, facciamo un passo indietro, e analizziamo le singole classi che hanno prodotto questo risultato.
Uno sguardo al codice
Il progetto è suddiviso in due package: it.html.client
e it.html.server
. Il package client
contiene tre classi: HtmlExample
, GreetingService
e GreetingServiceAsync
. Il package server
contiene un'unica classe: GreetingServiceImpl
.
La classe principale è it.html.client.HtmlExample
che implementa l'interfaccia com.google.gwt.core.client.EntryPoint
. L'unico metodo definito dall'interfaccia, è onModuleLoad. Questo metodo viene richiamato dal framework nel momento in cui la pagina viene caricata. In questo metodo, con una filosofia simile a quella che si utilizza per le applicazioni swing, viene creata la struttura di quella che sarà la pagina HTML corrispondente.
Analizziamo il metodo riga per riga:
package it.html.client; import com.google.gwt.core.client.EntryPoint; /* saltiamo gli altri 'import' */ public class HtmlExample implements EntryPoint { private static final String SERVER_ERROR = "An error occurred ... bla bla"; private final GreetingServiceAsync greetingService = GWT.create(GreetingService.class); public void onModuleLoad() { // crea il bottone Send e gli assegna uno stile final Button sendButton = new Button("Send"); sendButton.addStyleName("sendButton"); // crea un campo di testo e ne inizializza il contenuto final TextBox nameField = new TextBox(); nameField.setText("GWT User"); // aggiunge i due elementi precedenti alla pagina RootPanel.get("nameFieldContainer").add(nameField); RootPanel.get("sendButtonContainer").add(sendButton); // assegna il focus al campo di testo nameField.setFocus(true); nameField.selectAll(); // crea una finestra di dialogo final DialogBox dialogBox = new DialogBox(); dialogBox.setText("Remote Procedure Call"); // la finestra viene resa trascinabile dialogBox.setAnimationEnabled(true); // crea un bottone Close e gli assengna un id final Button closeButton = new Button("Close"); closeButton.getElement().setId("closeButton"); // crea una label final Label textToServerLabel = new Label(); // crea una label contenente una stringa HTML final HTML serverResponseLabel = new HTML(); // creazione di un pannello al quale, oltre ad una stringa di testo, // vengono aggiunte le due label create precedentemente VerticalPanel dialogVPanel = new VerticalPanel(); dialogVPanel.addStyleName("dialogVPanel"); dialogVPanel.add(new HTML("<strong>Sending name to the server:</strong>")); dialogVPanel.add(textToServerLabel); dialogVPanel.add(new HTML("<br /><strong>Server replies:</strong>")); dialogVPanel.add(serverResponseLabel); dialogVPanel.setHorizontalAlignment(VerticalPanel.ALIGN_RIGHT); // il bottone viene aggiunto al pannello dialogVPanel.add(closeButton); // il pannello viene aggiunto alla finestra di dialogo dialogBox.setWidget(dialogVPanel); // viene assegnato un handler di tipo click al bottone Close closeButton.addClickHandler(new ClickHandler() { public void onClick(ClickEvent event) { //nasconde la finestra di dialogo dialogBox.hide(); //abilita il bottone Send e gli viene assegnato il focus sendButton.setEnabled(true); sendButton.setFocus(true); } }); // creazione di un handler di tipo click o keyUp class MyHandler implements ClickHandler, KeyUpHandler { // metodo richiamato in seguito ad un evento di tipo click public void onClick(ClickEvent event) { sendNameToServer(); } // metodo richiamato in seguito ad un evento di tipo key up public void onKeyUp(KeyUpEvent event) { // soltanto in caso in cui il comando inviato è di tipo enter if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER) { sendNameToServer(); } } private void sendNameToServer() { // il bottone Send viene disabilitato sendButton.setEnabled(false); // viene letto il valore del campo di testo String textToServer = nameField.getText(); // assegnazione del valore alla label textToServerLabel.setText(textToServer); serverResponseLabel.setText(""); greetingService.greetServer(textToServer, new AsyncCallback<String>() { // questo metodo viene richiamato nel caso in cui la chiamata va in errore public void onFailure(Throwable caught) { // viene abilitata la finestra di dialogo precedentemente definita dialogBox.setText("Remote Procedure Call - Failure"); serverResponseLabel.addStyleName("serverResponseLabelError"); // nella finestra di dialogo viene stampato il messaggio di errore serverResponseLabel.setHTML(SERVER_ERROR); dialogBox.center(); closeButton.setFocus(true); } // questo metodo viene richiamato nel caso in cui la chiamata va a buon fine public void onSuccess(String result) { // viene abilitata la finestra di dialogo precedentemente definita dialogBox.setText("Remote Procedure Call"); serverResponseLabel.removeStyleName("serverResponseLabelError"); // nella finestra di dialogo viene stampato un messaggio contenente // il valore inserito dall'utente nel campo di testo serverResponseLabel.setHTML(result); dialogBox.center(); closeButton.setFocus(true); } }); } } // viene associato al bottone send e al comando ENTER l'handler precedentemente definito MyHandler handler = new MyHandler(); sendButton.addClickHandler(handler); nameField.addKeyUpHandler(handler); } }
Il passaggio principale, sul quale vale la pena soffermarci, è costituito dalla chiamata:
greetingService.greetServer(...)
Per capire meglio il funzionamento, però, è necessario analizzare prima le altre classi e interfacce necessarie all'applicazione, ad esempio il servizio che consumiamo:
public interface GreetingService extends RemoteService { String greetServer(String name); } public interface GreetingServiceAsync { void greetServer(String input, AsyncCallback<String> callback); } public class GreetingServiceImpl extends RemoteServiceServlet implements GreetingService { public String greetServer(String input) { String serverInfo = getServletContext().getServerInfo(); String userAgent = getThreadLocalRequest().getHeader("User-Agent"); return "Hello, " + input + "!<br /><br />I am running " + serverInfo + ".<br /><br />It looks like you are using:<br />" + userAgent; } }
L'interfaccia GreetingService
estende l'interfaccia RemoteService
, e contiene il metodo utilizzato per inviare la richiesta al server. L'unico parametro di ingresso è rappresentato dalla stringa inserita nel campo di testo dall'utente.
L'interfaccia GreetingServiceAsync
definisce un metodo analogo al precedente ma, ha come secondo parametro di ingresso, una classe di tipo AsyncCallback<String>
che rappresenta la funzione di ritorno.
La classe GreetingServiceImpl
estende la classe RemoteServiceServlet
ed implementa l'interfaccia GreetingService
.
Il metodo greetServer
di questa classe, riceve il testo inserito nel campo di testo dall'utente, e restituisce un'altra stringa contenente alcune informazioni sul server e sul browser utilizzato. Inoltre viene aggiunto il classico messaggio di benvenuto con il nome dell'utente.
Facciamo un passo indietro ed analizziamo la seguente riga di codice del metodo onModuleLoad
:
greetingService.greetServer(textToServer, new AsyncCallback<String>()...
greetingService
è una variabile d‘istanza della classe HtmlExample
che viene inizializzata nel seguente modo:
private final GreetingServiceAsync greetingService = GWT.create(GreetingService.class);
L'unico metodo a nostra disposizione è, naturalmente, quello definito nell'interfaccia GreetingServiceAsync
. Questo metodo riceve in ingresso, come detto in precedenza, una stringa e un'istanza della classe AsyncCallback
.
Effettivamente AsyncCallback
è un'interfaccia nella quale sono definiti i due seguenti metodi:
void onFailure (java.lang.Throwable caught)
che viene invocato nel caso in cui la chiamata asincrona falliscevoid onSuccess (T result)
che viene invocato nel caso in cui l'elaborazione va a buon fine. La variabile result rappresenta l'oggetto di ritorno della chiamata effettuata. Nel nostro caso, la variabile result, è la stringa contenente il messaggio di benvenuto
Compilare il progetto
Nella prima parte dell'articolo abbiamo realizzato il nostro applicativo e lo abbiamo testato in modalità hosted mode. Ora vediamo come compilare il progetto utilizzando il compilatore GWT.
Clicchiamo sull'icona rossa presente nella toolbar di eclipse.
L'IDE dovrebbe aver impostato già tutti i parametri per la compilazione, ma possiamo comunqu selezionare, attraverso il classico tastino Browse (sfoglia), il progetto da compilare, il livello di log da utilizzare e lo stile di output del log (Obfuscated
, Pretty
oppure Detailed
).
Durante la fase di compilazione, il compilatore GWT traduce il codice Java in HTML e JavaScript, compatibile con tutti i browser attualmente disponibili.
Le applicazioni scritte in GWT, risultano sicuramente più performanti, rispetto ad un'applicazione Ajax sviluppata manualmente, poiché il compilatore GWT, durante la fase di generazione del codice JavaScript, sfrutta al massimo le funzionalità che ogni singolo browser implementa nativamente.
Il risultato della compilazione è disponibile nella cartella war
del nostro progetto. In questa cartella troviamo tutti i file necessari per il deploy dell'applicazione, in un qualsiasi Web server Java:
- il descrittore
web.xml
- le librerie gwt
- il file di configurazione log4j
- tutti i file HTML e JavaScript
Unica cosa da osservare, per il momento, è il file XML, nel quale viene definita la Servlet greetServlet. Questa Servlet verrà interrogata dalle funzioni JavSscript generate da GWT, mediante richieste asincrone, tipiche di Ajax.
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd"> <web-app> <welcome-file-list> <welcome-file>HtmlExample.html</welcome-file> </welcome-file-list> <servlet> <servlet-name>greetServlet</servlet-name> <servlet-class> it.html.server.GreetingServiceImpl </servlet-class> </servlet> <servlet-mapping> <servlet-name>greetServlet</servlet-name> <url-pattern>/htmlexample/greet</url-pattern> </servlet-mapping> </web-app>
Tralasciamo il resto del contenuto della cartella war
, rinominiamola in HtmlExample
, e copiamola nella cartella webapps
di Tomcat. Avviamo il nostro Web server e carichiamo l'applicazione all'indirizzo http://localhost:8080/HtmlExample
.
Come possiamo vedere, l'applicazione è praticamente identica a quella che abbiamo eseguita in modalità hosted mode. Adesso però, stiamo eseguendo la nostra applicazione direttamente nel browser, quindi stiamo eseguendo esclusivamente codice HTML e JavaScript.
Traduciamo la nostra applicazione in termini tecnici. Nel momento in cui l'utente connesso clicca sul tastino Send, viene invocata una funzione JavaScript che invoca, in modo asincrono, la greetServlet
. Il messaggio di ritorno viene intercettato e stampato in una classica finestra di output. Quando l'utente clicca sul tasto close di questa finestra, viene invocata un'altra funzione javascript che nasconde la finestra.
Scrivere un'applicazione utilizzando il framework Google Web Toolkit, è molto semplice. Non occorre avere particolari conoscenze di HTML o JavaScript ma esclusivamente codice java. Questo rende il tutto molto semplice e manutenibile. Naturalmente gli strumenti messi a disposizione del tool sono diversi. In questo articolo ci siamo limitati a mostrare esclusivamente il risultato finale e cercare di far capire la potenzialità del framewoork.