Download - Atelier gwt
Atelier
Objectif
Création une application basic avec GWT pour familiarise avec le Framework
Description
Dans ce TP on vas crée une application StockWatcher les principes
fonctionnements de l’application sont :
Permet aux utilisateurs la possibilité d'ajouter des actions
Afficher les informations suivantes pour chaque stock: symbole, le cours,
le changement depuis le dernier rafraîchissement
Fournir aux utilisateurs la possibilité de supprimer un support dans la liste
Actualiser le prix des actions
Calculer la variation depuis la dernière actualisation à la fois comme un
nombre et un pourcentage
Afficher un horodatage indiquant la dernière mise à jour
Les éléments d'interface utilisateur:
une table pour contenir les données de stock
deux boutons, l'un pour ajouter des actions et un pour les supprimer
une zone de saisie pour entrer le code de stock
un timestamp pour afficher l'heure et la date de la dernière actualisation
Les étapes de créations de l’application
Creation d’un nouveau projet
création d’interface utilisateur
manager les événements
debug l’application
application d’un style (css)
complies l’application
client-serveur communication (RPC)
Ajout du plugin mojo
Création du projet GWT avec Maven
Client serveur communication RPC
Principe de fonctionnement
Code GWT
Interface asynchrone
Interface synchrone
Code Serveur
Créer un service Coté client, définir une interface dite synchrone qui étend RemoteService et qui liste
toutes les méthodes RPC
Coté serveur, implémenter l’interface et étendre RemoteServiceServlet
Coté client, définir une interface asynchrone basée sur la première interface avec le
même nombre de méthodes que la première interface
On demande un service mais on n’attend pas la réponse Celle-ci viendra en son temps grâce à un « objet de rappel ».
Interface synchrone coté client Spécification de vos services
Import com.google.gwt.user.client.rpc.RemoteService;
public interface MyService extends RemoteService {
public T myMethod(T1 p1, T2 p2, T3 p3);
}
Implémentation coté serveur
Implémentation de vos services
public class MyServiceImpl extends RemoteServiceServlet
implements MyService { public T myMethod(T1 p1, T2 p2, T2 p3) {
// …
return r;
}
}
Interface asynchrone coté client Implémentation de vos services
interface MyServiceAsync {
public void myMethod(T1 p1, T2 p2,T2 p3,
AsyncCallback<T> callback);
}
Appel de méthodes asynchrones o Un appel de méthode asynchrone nécessite de passer un objet de
rappel (callback object)
o L’objet de rappel est notifié lorsque l’appel asynchrone est
terminé
o En attendant l’appelant continu son activité
o Remarquez que les méthodes asynchrones ont un type résultat
void
o Conventions de nommage o Nom des classes et interfaces
– MyService interface synchrone
– MyServiceImpl implementation
– MyServiceAsync interface asynchrone
o Chaque méthode dans l’interface synchrone doit avoir une
méthode correspondante dans l’interface asynchrone avec un
paramêtre extra en dernier argument de type AsyncCallback
o Méthode de l’interface synchrone
public Integer[ ] myMethod(String s, int i)
o Méthode de l’interface asynchrone
public void myMethod(String s,int i, AsyncCallback<Integer[ ]>
callback)
Utilisation de services
• Créer une instance de service MonServiceAsync monService = GWT.create(MonService.class);
• Appel de méthode à distance monService.maMethode(p1,p2, new AsyncCallBack<T>() {
public onFailure(Trowable t) {...}
public onSuccess(T result) {...}
})
Chemin d’accès à un service
• Le chemin d’accès à un service est composé de deux parties
– /nomDuModule/nomDuService
• Le nom du service peut être défini de deux manières
– Statiquement par annotation dans l’interface synchrone
@RemoteServiceRelativePath("service")
– Dynamiquement ServiceAsync service = GWT.create(Service.class);
ServiceDefTarget test = (ServiceDefTarget) service
test.setServiceEntryPoint("/module/service");
• Les déclarations de path pour la servlet doivent être cohérentes
– Dans le descripteur de module <servlet class= "MaServlet" path="/module/service"/>
– Dans le descripteur web.xml
<url-pattern>/module/service</urlpattern>
Application
Dans ce mini tutorial nous allons creer une petite application qui implémente le
service propose par le framework GWT .Ceer un nouveau projet et suivre les
diffférentes etapes decritent .
I Configuration
Afin de définir et configurer une interface RPC asynchrone, vous aurez besoin
de créer trois nouveaux fichiers:
1. MonService
Une interface implémentant RemoteService dans laquelle vous définirez les
méthodes que vous voudrez appeler. Vous définirez grâce à
l’annotation @RemoteServiceRelativePath(String path), le chemin grâce auquel
vous accéderez à votre service:Cette classe doit etre créée dans le package client
1 //MonService.java
2 @RemoteServiceRelativePath("monservice")
3 public interface MonService extends RemoteService {
4 String exempleMethode();
5 }
Toutes vos méthodes asynchrones doivent avoir un retour de
type Serializable !
2. MonServiceImpl
Une classe située dans le package server, étendant RemoteServiceServlet et
implémentant MonService, dont vous implémenterez les méthodes :
1 //MonServiceImpl.java
2 public class MonServiceImpl extends RemoteServiceServlet implements Mon
Service {
3
4 @Override
5 public String exempleMethode() {
6 return "Réponse du Serveur";
7 }
8
9 }
3. MonServiceAsync
Une interface devant être dans le même package que MonService et dont le nom
devra être le même que celle-ci suivi de Async (e.g :MonServiceAsync). Dans
cette interface, vous définirez les mêmes méthodes que dans MonService, mis a
part quelques détails:
1 //MonServiceAsync.java
2 public interface MonServiceAsync {
3
4 void exempleMethode(AsyncCallback<String> callback);
5
6 }
Comme vous pouvez le voir, la méthode dans cette interface retourne void, donc
rien. En effet, toutes vos méthodes dans cette interface auront un type de
retour void. Cependant, le type de retour de vos méthodes définies
dans MonService se retrouvent dans l’argument supplémentaire de votre
méthode : AsyncCallback<String> callback. Toutes vos méthodes
définies dans MonService devront donc être ainsi transposées
dansMonServiceAsync.
Maintenant, afin de pouvoir accéder à notre code côté serveur, nous allons avoir
besoin de configurer une servlet qui contiendra l’implémentation
de MonService dans le web.xml. Rien de différent ici avec la définition
habituelle des servlets en Java. Vous préciserez donc la classe de la servlet, ainsi
que son chemin (celui définit dans MonService).
01 <!--MonServiceAsync.java-->
02 <servlet>
0
3 <servlet-name>monServiceImpl</servlet-name>
0
4
<servlet-
class>com.gwtfrance.mapremiereapplication.server.MonServiceImpl</servlet
-class>
05 </servlet>
06
07 <servlet-mapping>
08 <servlet-name>monServiceImpl</servlet-name>
09 <url-pattern>/ma_premiere_application/monservice</url-pattern>
10 </servlet-mapping>
Voilà, votre application est prête à utiliser les appels asynchrones. Si vous le
désirez, maintenant que vous savez à quoi servent les
fichiersGreetingService, GreetingServiceAsync, et GreetingServiceImpl, vous
pouvez les supprimer, ainsi que la configuration de la
servlet greetingServicedans le web.xml.
II. Utilisation
GWT-RPC étant configuré, nous allons pouvoir effectuer nos appels
asynchrones. Pour ceci, vous aurez besoin de créer une instance du proxy de
service :Créer une classe dans le package client qui implément le
entryPoint :explement :Ma_Premiere_Application.java
1 //Ma_Premiere_Application.java
2 final MonServiceAsync monService = GWT.create(MonService.class);
Je vous conseille fortement de faire de cette instance un Singleton, étant inutile
de réinstancier systématiquement ce proxy.
Afin de pouvoir gérer les retour de notre appel asynchrone, nous aurons besoin
d’un objet AsyncCallback.
01 //Ma_Premiere_Application.java
02 final AsyncCallback<String> callback = new AsyncCallback<String>() {
04 @Override
05 public void onFailure(Throwable caught) {
06 Window.alert("Erreur lors de l'appel serveur");
07
08 }
09
10 @Override
11 public void onSuccess(String result) {
12 Window.alert(result);
13
14 }
15 };
Comme vous pouvez le voir, lors de l’instanciation de notre objet callback, nous
devons implémenter deux méthode:
La méthode onFailure(Throwable caught) est appelée lorsque une erreur se
produit côté serveur lors de votre appel. Grâce à l’objetThrowable, vous pourrez
récupérer les exceptions lancées côté serveur et les traiter comme vous
l’entendez.
La méthode onSuccess(String result) est elle appelée lorsque le traitement
serveur s’est passé sans problème. L’objet retourné correspond à l’objet retourné
par votre implémentation côté serveur.
Notre objet callback étant maintenant défini, nous pouvons donc effectuer notre
appel asynchrone. Ici, nous remplaçons le code de la méthodeonClick(Event event) par notre appel:
1 //Ma_Premiere_Application.java
2 monBouton.addClickHandler(new ClickHandler() {
3
4 @Override
5 public void onClick(ClickEvent event) {
6 monService.exempleMethode(callback);
7
8 }
9 });
StockWatcher.java
package com.google.gwt.sample.stockwatcher.client; import java.util.ArrayList; import java.util.Date; import com.google.gwt.core.client.EntryPoint; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.event.dom.client.KeyCodes; import com.google.gwt.event.dom.client.KeyPressEvent; import com.google.gwt.event.dom.client.KeyPressHandler; import com.google.gwt.i18n.client.DateTimeFormat; import com.google.gwt.i18n.client.NumberFormat; import com.google.gwt.user.client.Random; import com.google.gwt.user.client.Timer; import com.google.gwt.user.client.Window; import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.FlexTable; import com.google.gwt.user.client.ui.HorizontalPanel; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.RootPanel; import com.google.gwt.user.client.ui.TextBox; import com.google.gwt.user.client.ui.VerticalPanel; public class StockWatcher implements EntryPoint { private VerticalPanel mainPanel = new VerticalPanel(); private FlexTable stocksFlexTable = new FlexTable(); private HorizontalPanel addPanel = new HorizontalPanel(); private TextBox newSymbolTextBox = new TextBox(); private Button addStockButton = new Button("Add"); private Label lastUpdatedLabel = new Label(); private ArrayList<String> stocks = new ArrayList<String>(); private static final int REFRESH_INTERVAL = 5000; // ms
/** * Entry point method. */ public void onModuleLoad() { // Create table for stock data. stocksFlexTable.setText(0, 0, "Symbol"); stocksFlexTable.setText(0, 1, "Price"); stocksFlexTable.setText(0, 2, "Change"); stocksFlexTable.setText(0, 3, "Remove"); // Add styles to elements in the stock list table. stocksFlexTable.getRowFormatter().addStyleName(0, "watchListHeader"); stocksFlexTable.addStyleName("watchList"); stocksFlexTable.getCellFormatter().addStyleName(0, 1, "watchListNumericColumn"); stocksFlexTable.getCellFormatter().addStyleName(0, 2, "watchListNumericColumn"); stocksFlexTable.getCellFormatter().addStyleName(0, 3, "watchListRemoveColumn"); // Assemble Add Stock panel. addPanel.add(newSymbolTextBox); addPanel.add(addStockButton); addPanel.addStyleName("addPanel"); // Assemble Main panel. mainPanel.add(stocksFlexTable); mainPanel.add(addPanel); mainPanel.add(lastUpdatedLabel); // Associate the Main panel with the HTML host page. RootPanel.get("stockList").add(mainPanel); // Move cursor focus to the input box. newSymbolTextBox.setFocus(true); // Setup timer to refresh list automatically. Timer refreshTimer = new Timer() { @Override public void run() { refreshWatchList(); } }; refreshTimer.scheduleRepeating(REFRESH_INTERVAL); //Listen for mouse events on the Add button. newSymbolTextBox.addKeyPressHandler(new KeyPressHandler() { public void onKeyPress(KeyPressEvent event) { // if (event.getCharCode() == KeyCodes.KEY_ENTER) { addStock(); // } } }); } /** * Add stock to FlexTable. Executed when the user clicks the addStockButton or * presses enter in the newSymbolTextBox. */ private void addStock() { final String symbol = newSymbolTextBox.getText().toUpperCase().trim(); newSymbolTextBox.setFocus(true); // Stock code must be between 1 and 10 chars that are numbers, letters, or dots. if (!symbol.matches("^[0-9A-Z\\.]{1,10}$")) {
Window.alert("'" + symbol + "' is not a valid symbol."); newSymbolTextBox.selectAll(); return; } newSymbolTextBox.setText(""); // Don't add the stock if it's already in the table. if (stocks.contains(symbol)) return; // Add the stock to the table. int row = stocksFlexTable.getRowCount(); stocks.add(symbol); stocksFlexTable.setText(row, 0, symbol); stocksFlexTable.getCellFormatter().addStyleName(row, 1, "watchListNumericColumn"); stocksFlexTable.getCellFormatter().addStyleName(row, 2, "watchListNumericColumn"); stocksFlexTable.getCellFormatter().addStyleName(row, 3, "watchListRemoveColumn"); // Add a button to remove this stock from the table. Button removeStockButton = new Button("x"); removeStockButton.addClickHandler(new ClickHandler() { public void onClick(ClickEvent event) { int removedIndex = stocks.indexOf(symbol); stocks.remove(removedIndex); stocksFlexTable.removeRow(removedIndex + 1); } }); stocksFlexTable.setWidget(row, 3, removeStockButton); // Get the stock price. refreshWatchList(); } /** * Update a single row in the stock table. * * @param price Stock data for a single row. */ private void updateTable(StockPrice price) { // Make sure the stock is still in the stock table. if (!stocks.contains(price.getSymbol())) { return; } int row = stocks.indexOf(price.getSymbol()) + 1; // Format the data in the Price and Change fields. String priceText = NumberFormat.getFormat("#,##0.00").format( price.getPrice()); NumberFormat changeFormat = NumberFormat.getFormat("+#,##0.00;-#,##0.00"); String changeText = changeFormat.format(price.getChange()); String changePercentText = changeFormat.format(price.getChangePercent()); // Populate the Price and Change fields with new data. stocksFlexTable.setText(row, 1, priceText); stocksFlexTable.setText(row, 2, changeText + " (" + changePercentText + "%)"); } private void refreshWatchList() { final double MAX_PRICE = 100.0; // $100.00 final double MAX_PRICE_CHANGE = 0.02; // +/- 2%
StockPrice[] prices = new StockPrice[stocks.size()]; for (int i = 0; i < stocks.size(); i++) { double price = Random.nextDouble() * MAX_PRICE; double change = price * MAX_PRICE_CHANGE * (Random.nextDouble() * 2.0 - 1.0); prices[i] = new StockPrice(stocks.get(i), price, change); } updateTable(prices); } private void updateTable(StockPrice[] prices) { for (int i = 0; i < prices.length; i++) { updateTable(prices[i]); } // Display timestamp showing last refresh. lastUpdatedLabel.setText("Last update : " + DateTimeFormat.getMediumDateTimeFormat().format(new Date())); } }
StockWatcher.css
/* Formatting specific to the StockWatcher application */ body { padding: 10px; } /* stock list header row */ .watchListHeader { background-color: #2062B8; color: white; font-style: italic; } /* stock list flex table */ .watchList { border: 1px solid silver; padding: 2px; margin-bottom:6px; } /* stock list Price and Change fields */ .watchListNumericColumn { text-align: right; width:8em; } /* stock list Remove column */ .watchListRemoveColumn { text-align: center; } /* Add Stock panel */ .addPanel { margin: 10px 0px 15px 0px; }