17
Atelier

Atelier gwt

Embed Size (px)

DESCRIPTION

Bon document pour s'initier avec GWT

Citation preview

Page 1: Atelier gwt

Atelier

Page 2: Atelier gwt

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

Page 3: Atelier gwt

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

Page 4: Atelier gwt

Création du projet GWT avec Maven

Page 5: Atelier gwt

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 ».

Page 6: Atelier gwt

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);

}

Page 7: Atelier gwt

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

Page 8: Atelier gwt

• 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")

Page 9: Atelier gwt

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:

Page 10: Atelier gwt

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>

Page 11: Atelier gwt

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

Page 12: Atelier gwt

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() {

Page 13: Atelier gwt

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

Page 14: Atelier gwt

/** * 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}$")) {

Page 15: Atelier gwt

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%

Page 16: Atelier gwt

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; }

Page 17: Atelier gwt