Traductions pour la plateforme Java

Développer la killer app

Article d'origine: 

Développez votre propre application de gestion de contacts avec App Engine

Résumé: 

La finalité d'une plateforme dans le cloud comme Google App Engine for Java™ est de permettre d'imaginer, développer et déployer des applications de qualité professionnelle qui passent à l'échelle - sans pour autant casser votre tirelire ou devenir fou. Dans la seconde partie de cette introduction en trois parties à Google App Engine pour Java, Rick Hightower va plus loin que les exemples prêts à l'emploi de la première partie, et vous guidera pas à pas dans l'écriture et le déploiement d'une application simple de gestion de contacts utilisant l'App Engine pour Java.

Dans la première partie de cette introduction à la construction d'applications Java passant à l'échelle avec l'App Engine, vous avez pris connaissance de l'outillage Eclipse et de l'infrastructure de la plateforme de cloud computing de Google (ou PAAS) pour les développeurs Java. Les exemples de cet article étaient préfabriqués, pour que vous puissiez vous concentrer sur l'intégration de l'App Engine dans Eclipse et passer rapidement à la pratique du développement et du déploiement de différents types d'applications - à savoir une application utilisant Google Web Toolkit (GWT) et l'autre basée sur des servlets. Cet article bâtit sur ce fondement et prépare également aux exercices de programmation plus avancés de la troisième partie de la série.

L'application de gestion de contacts que vous développerez permet à l'utilisateur de stocker des informations basiques telles que le nom, l'adresse électronique, et le numéro de téléphone. Pour créer cette application, vous utiliserez l'assistant de création de projet GWT d'Eclipse.

Commençons par simuler, puis ayons de vrais contacts (ndT: tentative désespérée pour traduire le titre humoristique de l'original)

La première étape pour construire une nouvelle application pour l'App Engine, comme vous le savez maintenant, est de lancer l'assistant de création de projet dans Eclipse. Une fois que vous y êtes, choisissez d'utiliser GWT. (La première partie de cette série donne des informations détaillées sur la création d'un projet GWT pour Google App Engine).

Pour cet exercice, vous commencerez par une application qui simulera les opérations de base sur les contacts, nous les stockerons réellement plus tard. Pour l'instant donc, utilisons un objet d'accès aux données (DAO) qui aura une implémentation simulant nos opérations, comme le montre le Listing 1:

Listing 1. Interface ContactDAO

package gaej.example.contact.server;

import java.util.List;

import gaej.example.contact.client.Contact;

public interface ContactDAO {
    void addContact(Contact contact);
    void removeContact(Contact contact);
    void updateContact(Contact contact);
    List<Contact> listContacts();
}

ContactDAO définit des méthodes pour ajouter, supprimer, mettre à jour un contact et en retourner une liste complète. C'est une interface CRUD (ndT: voici une définition succincte) très basique qui permettra de gérer les contacts. La classe Contact est votre objet métier (Listing 2):

Listing 2. Objet métier Contact (gaej.example.contact.client.Contact)

package gaej.example.contact.client;

import java.io.Serializable;

public class Contact implements Serializable {
    
    private static final long serialVersionUID = 1L;
    private String name;
    private String email;
    private String phone;

    public Contact() {
        
    }
    
    public Contact(String name, String email, String phone) {
        super();
        this.name = name;
        this.email = email;
        this.phone = phone;
    }

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getEmail() {
        return email;
    }
    public void setEmail(String email) {
        this.email = email;
    }
    public String getPhone() {
        return phone;
    }
    public void setPhone(String phone) {
        this.phone = phone;
    }
}

Pour cette première version de l'application, vous travaillerez avec un simulacre qui stocke les contacts dans une collection en mémoire, comme dans le Listing 3:

Listing 3. Classe DAO simulacre

package gaej.example.contact.server;

import gaej.example.contact.client.Contact;

import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

public class ContactDAOMock implements ContactDAO {

    Map<String, Contact> map = new LinkedHashMap<String, Contact>();
    
    {
        map.put("rhightower@mammatus.com", 
          new Contact("Rick Hightower", "rhightower@mammatus.com", "520-555-1212"));
        map.put("scott@mammatus.com", 
          new Contact("Scott Fauerbach", "scott@mammatus.com", "520-555-1213"));
        map.put("bob@mammatus.com", 
          new Contact("Bob Dean", "bob@mammatus.com", "520-555-1214"));

    }
   
    public void addContact(Contact contact) {
        String email = contact.getEmail();
        map.put(email, contact);
    }

    public List<Contact> listContacts() {
        return Collections.unmodifiableList(new ArrayList<Contact>(map.values()));
    }

    public void removeContact(Contact contact) {
        map.remove(contact.getEmail());
    }

    public void updateContact(Contact contact) {        
        map.put(contact.getEmail(), contact);
    }
}

Création des services distants

Pour l'instant, votre objectif est de créer une IHM GWT qui permettra d'utiliser le DAO. Elle devrait utiliser toutes les méthodes de l'interface ContactDAO. La première étape est d'encapsuler toutes les fonctionnalités proposées par le DAO (les versions ultérieures de l'application devront stocker les données côté serveur, donc le DAO doit se trouver sur le serveur) dans un service, comme le montre le Listing 4:

Listing 4. ContactServiceImpl

package gaej.example.contact.server;

import java.util.ArrayList;
import java.util.List;

import gaej.example.contact.client.Contact;
import gaej.example.contact.client.ContactService;

import com.google.gwt.user.server.rpc.RemoteServiceServlet;

public class ContactServiceImpl extends RemoteServiceServlet implements ContactService {
    private static final long serialVersionUID = 1L;
    private ContactDAO contactDAO = new ContactDAOMock();

    public void addContact(Contact contact) {
        contactDAO.addContact(contact);
    }

    public List<Contact> listContacts() {
        List<Contact> listContacts = contactDAO.listContacts();
        return new ArrayList<Contact> (listContacts);
    }

    public void removeContact(Contact contact) {
        contactDAO.removeContact(contact);
    
    }

    public void updateContact(Contact contact) {
        contactDAO.updateContact(contact);
    }
}

Remarquez que ContactServiceImpl étend RemoteServiceServlet et définit des méthodes pour ajouter, supprimer et mettre à jour un contact, et en obtenir une liste. Il délègue toutes ces opérations à ContactDAOMock. ContactServiceImpl encapsule simplement ContactDAO pour exposer ses fonctionnalités à l'IHM GWT. ContactServiceImpl est associée à l'URI /contactlist/contacts dans le fichier web.xml (Listing 5):

Listing 5. Section de web.xml concernant ContactService

<servlet>
    <servlet-name>contacts</servlet-name>
    <servlet-class>gaej.example.contact.server.ContactServiceImpl</servlet-class>
  </servlet>
 
  <servlet-mapping>
    <servlet-name>contacts</servlet-name>
    <url-pattern>/contactlist/contacts</url-pattern>
  </servlet-mapping>

Pour que la partie cliente puisse accéder à ce service, il faut définir à la fois une interface classique et une interface asynchrone pour le service distant, comme dans les Listings 6 et 7:

Listing 6. ContactService

package gaej.example.contact.client;

import java.util.List;

import com.google.gwt.user.client.rpc.RemoteService;
import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;

@RemoteServiceRelativePath("contacts")
public interface ContactService extends RemoteService {
    List<Contact> listContacts();
    void addContact(Contact contact);
    void removeContact(Contact contact);
    void updateContact(Contact contact);
}

Listing 7. ContactServiceAsync

package gaej.example.contact.client;

import java.util.List;

import com.google.gwt.user.client.rpc.AsyncCallback;

public interface ContactServiceAsync  {
    void listContacts(AsyncCallback<List <Contact>> callback);
    void addContact(Contact contact, AsyncCallback<Void> callback);
    void removeContact(Contact contact, AsyncCallback<Void> callback);
    void updateContact(Contact contact, AsyncCallback<Void> callback);
}

Notez que ContactService étend l'interface RemoteService et définit un RemoteServiceRelativePath qui spécifie un chemin relatif "contacts". Ce chemin relatif correspond au chemin qui a été défini pour le service dans le fichier web.xml (c'est une obligation). ContactServiceAsync prévoit des objets callback pour que l'IHM GWT puisse être notifiée des appels au serveur sans bloquer les activités du client.

Couper court au code spaghetti

Je ne suis pas un grand fan du code spaghetti et j'évite d'en écrire autant que possible. Un exemple de code spaghetti serait un ensemble de classes internes anonymes dont les méthodes définiraient des classes internes anonymes. Ces classes internes, à leur tour, feraient des callbacks de méthodes, qui seraient définies dans une classe interne. Beurk ! Franchement, je suis incapable de lire ou de comprendre un code si embrouillé, même si c'est le mien ! Donc, pour éclaircir un peu les choses, je suggère de découper l'IHM GWT en trois parties:

  • ContactListEntryPoint
  • ContactServiceDelegate
  • ContactListGUI

ContactListEntryPoint est le point d'entrée, il fait le cablage de l'IHM. ContactServiceDelegate encapsule les fonctionnalités de ContactService et cache le cablage des callbacks de classe interne. ContactListGUI gère tous les composants graphiques et traite les événements provenant de l'IHM et du service. ContactListGUI utilise ContactServiceDelegate pour effectuer des requêtes à ContactService.

The ContactListEntryPoint is the main entry point; it does GUI event wiring. The ContactServiceDelegate wraps the ContactService functionality and hides the inner class callback wiring. The ContactListGUI manages all of the GUI components and handles events from the GUI and the Service. The ContactListGUI uses the ContactServiceDelegate to make requests of the ContactService.

Le fichier ContactList.gwt.xml (qui se trouve dans gaej.example.contact) spécifie que ContactListEntryPoint est le point d'entrée de l'application au moyen de l'élément entry-point comme le montre le Listing 8:

Listing 8. ContactList.gwt.xml

<entry-point class='gaej.example.contact.client.ContactListEntryPoint'/>

La classe ContactListEntryPoint implémente l'interface EntryPoint de GWT (com.google.gwt.core.client.EntryPoint), ce qui signifie que cette classe sera appelée pour initialiser l'IHM. ContactListEntryPoint ne fait pas grand chose. Elle crée une instance de ContactListGUI et une instance de ContactServiceDelegate, et les met en relation afin qu'elles puissent collaborer.  Ensuite ContactListEntryPoint réalise le cablage des événements de l'IHM. Vous pouvez voir ContactListEntryPoint au Listing 9:

Listing 9. ContactListEntryPoint

package gaej.example.contact.client;

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.user.client.ui.HTMLTable.Cell;

/**
 * Entry point classes define onModuleLoad().
 */
public class ContactListEntryPoint implements EntryPoint {
    private ContactListGUI gui;
    private ContactServiceDelegate delegate;
    
    /**
     * This is the entry point method.
     */
    public void onModuleLoad() {
        
        gui = new ContactListGUI();
        delegate = new ContactServiceDelegate();
        gui.contactService = delegate;
        delegate.gui = gui;
        gui.init();
        delegate.listContacts();
        wireGUIEvents();
                
        
    }

    private void wireGUIEvents() {
        gui.contactGrid.addClickHandler(new ClickHandler(){
            public void onClick(ClickEvent event) {
                 Cell cellForEvent = gui.contactGrid.getCellForEvent(event);
                 gui.gui_eventContactGridClicked(cellForEvent);                
            }});
        
        gui.addButton.addClickHandler(new ClickHandler(){
            public void onClick(ClickEvent event) {
                gui.gui_eventAddButtonClicked();
            }});

        gui.updateButton.addClickHandler(new ClickHandler(){
            public void onClick(ClickEvent event) {
                gui.gui_eventUpdateButtonClicked();
            }});
        
        gui.addNewButton.addClickHandler(new ClickHandler(){
            public void onClick(ClickEvent event) {
                gui.gui_eventAddNewButtonClicked();
                
            }});

    }
}

Notez que ContactListEntryPoint cable les événements pour addButton, updateButton, contactGrid, and et addNewButton. Pour  ce faire, elle enregistre des classes internes annoymes qui implémentent les interfaces écouteur des événements provenant des composants graphiques. C'est une technique très similaire à la gestion des événements dans Swing. Les événéments proviennent des composants graphiques créés par l'IHM (ContactListGUI), dont nous discuterons plus loin. Notez que la classe représentant l'IHM possèdes des méthodes gui_eventXXX pour répondre aux événements.

ContactListGUI crée les composants graphiques de l'IHM et répond aux événements qu'ils génèrent. Elle les traduit en actions que l'utilisateur veut effectuer sur ContactsService. ContactListGUI utilise ContactServiceDelegate pour invoquer les méthodes de ContactService. ContactServiceDelegate crée une interface asynchrone vers ContactService et l'utilise pour faire des appels Ajax asynchrones. ContactServiceDelegate notifie ContactListGUI des événements (succès ou échec) provenant du service. Vous pouvez voir ContactServiceDelegate dans le Listing 10:

Listing 10. ContactServiceDelegate

import java.util.List;

import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.rpc.AsyncCallback;

public class ContactServiceDelegate {
    private ContactServiceAsync contactService = GWT.create(ContactService.class);
    ContactListGUI gui;
    
    void listContacts() {
        contactService.listContacts(new AsyncCallback<List<Contact>> () {
                    public void onFailure(Throwable caught) {
                        gui.service_eventListContactsFailed(caught);
                    }
        
                    public void onSuccess(List<Contact> result) {
                        gui.service_eventListRetrievedFromService(result);
                        
                    }
        }//end of inner class
        );//end of listContacts method call.
    }
    
    void addContact(final Contact contact) {
        contactService.addContact(contact, new AsyncCallback<Void> () {
            public void onFailure(Throwable caught) {
                gui.service_eventAddContactFailed(caught);
            }

            public void onSuccess(Void result) {
                gui.service_eventAddContactSuccessful();
            }
        }//end of inner class
        );//end of addContact method call.        
    }

    void updateContact(final Contact contact) {
        contactService.updateContact(contact, new AsyncCallback<Void> () {
            public void onFailure(Throwable caught) {
                gui.service_eventUpdateContactFailed(caught);
            }

            public void onSuccess(Void result) {
                gui.service_eventUpdateSuccessful();
            }
        }//end of inner class
        );//end of updateContact method call.        
    }

    void removeContact(final Contact contact) {
        contactService.removeContact(contact, new AsyncCallback<Void> () {
            public void onFailure(Throwable caught) {
                gui.service_eventRemoveContactFailed(caught);
            }

            public void onSuccess(Void result) {
                gui.service_eventRemoveContactSuccessful();
            }
        }//end of inner class
        );//end of updateContact method call.        
    }
}

Remarquez que ContactServiceDelegate notifie ContactListGUI des événements de service au moyen de méthodes qui commencent par service_eventXXX. Comme mentionné précédemment, un de mes objectifs en écrivant ContactListGUI était d'éviter les classes internes imbriquées et de créer une classe IHM relativement plate (qui soit facile à lire et à utiliser par la suite). ContactListGUI fait seulement 186 lignes et est plutôt simple. Elle gère neuf composants graphiques et collabore avec ContactServiceDelegate pour les opérations CRUD, comme le montre le Listing 11:

Listing 11. ContactListGUI en action

package gaej.example.contact.client;

import java.util.List;

import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.Hyperlink;
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.HTMLTable.Cell;

public class ContactListGUI {
    /* Constantes. */
    private static final String CONTACT_LISTING_ROOT_PANEL = "contactListing";
    private static final String CONTACT_FORM_ROOT_PANEL = "contactForm";
    private static final String CONTACT_STATUS_ROOT_PANEL = "contactStatus";
    private static final String CONTACT_TOOL_BAR_ROOT_PANEL = "contactToolBar";
    private static final int EDIT_LINK = 3;
    private static final int REMOVE_LINK = 4;

    /* Composants graphiques */
    protected Button addButton;
    protected Button updateButton;
    protected Button addNewButton;
    protected TextBox nameField;
    protected TextBox emailField;
    protected TextBox phoneField;
    protected Label status;
    protected Grid contactGrid;
    protected Grid formGrid;
    
    /* Modèle de données */
    private List<Contact> contacts;
    private Contact currentContact;
    protected ContactServiceDelegate contactService;

 

Notez que ContactListGUI garde en mémoire le contact en cours d'affichage dans le formulaire (currentContact) et la liste des contacts (contacts). La Figure 1 montre la correspondance entre les composants graphiques et l'IHM:

Figure 1. Les composants graphiques de l'IHM de gestion de contacts

Les composants graphiques de l'IHM de gestion de contacts

Le Listing 12 montre comment ContactListGUI crée les composants graphiques et un formulaire de contact, puis place les composants dans le formulaire:

Listing 12. ContactListGUI crée et positionne les composants graphiques

public class ContactListGUI {
    /* Constants. */
    private static final String CONTACT_LISTING_ROOT_PANEL = "contactListing";
    private static final String CONTACT_FORM_ROOT_PANEL = "contactForm";
    private static final String CONTACT_STATUS_ROOT_PANEL = "contactStatus";
    private static final String CONTACT_TOOL_BAR_ROOT_PANEL = "contactToolBar";
    ...
    public void init() {
        addButton = new Button("Add new contact");
        addNewButton = new Button("Add new contact");
        updateButton = new Button("Update contact");
        nameField = new TextBox();
        emailField = new TextBox();
        phoneField = new TextBox();
        status = new Label();
        contactGrid = new Grid(2,5);

        buildForm();
        placeWidgets();
    }
    
    private void buildForm() {
        formGrid = new Grid(4,3);
        formGrid.setVisible(false);
        
        formGrid.setWidget(0, 0, new Label("Name"));
        formGrid.setWidget(0, 1, nameField);

        formGrid.setWidget(1, 0, new Label("email"));
        formGrid.setWidget(1, 1, emailField);
        
        formGrid.setWidget(2, 0, new Label("phone"));
        formGrid.setWidget(2, 1, phoneField);
        
        formGrid.setWidget(3, 0, updateButton);
        formGrid.setWidget(3, 1, addButton);
        
    }

    private void placeWidgets() {
        RootPanel.get(CONTACT_LISTING_ROOT_PANEL).add(contactGrid);
        RootPanel.get(CONTACT_FORM_ROOT_PANEL).add(formGrid);
        RootPanel.get(CONTACT_STATUS_ROOT_PANEL).add(status);
        RootPanel.get(CONTACT_TOOL_BAR_ROOT_PANEL).add(addNewButton);
    }

La méthode init de ContactListGUI est appelée par la méthode ContactListEntryPoint.onModuleLoad. La méthode init appelle la méthode buildForm pour créer une nouvelle grille de formulaire et la remplir avec les champs nécessaires à l'édition des données des contacts. La méthode init appelle ensuite la méthode placeWidgets, qui place les composants contactGrid, formGrid, status, et addNewButton aux endroits définis dans la page HTML qui héberge l'IHM de l'application, et que vous voyez dans le Listing 13:

Listing 13. ContactList.html définit l'affichage des composants graphiques

<h1>Contact List Example</h1>

    <table align="center">
      <tr>
        <td id="contactStatus"></td> <td id="contactToolBar"></td>
      </tr>
      <tr>
        <td id="contactForm"></td>
      </tr>
      <tr>
        <td id="contactListing"></td>
      </tr>
    </table>

 

Les constantes (comme CONTACT_LISTING_ROOT_PANEL="contactListing") correspondents aux IDs des éléments (comme id="contactListing") définis dans la page HTML. Ceci permet au concepteur de la page d'avoir un meilleur contrôle sur l'affichage des composants de l'application.

Maintenant que les bases de l'application sont posées, déroulons quelques cas d'utilisation courants.

Afficher une liste au chargement de la page

Quand la page de l'application de gestion des contacts se charge pour la première fois, elle appelle la méthode onModuleLoad de ContactListEntryPoint. onModuleLoad appelle la méthode ContactServiceDelegate.listContacts, qui effectue un appel asynchrone au service listContact. Quand la méthode listContact s'achève, une classe interne anonyme définie dans ContactServiceDelegate appelle la méthode de gestion de l'événement en provenance du service nommée service_eventListRetrievedFromService, que vous voyez dans le Listing 14:

Listing 14. Gestion d'événement au retour de listContact

public class ContactListGUI { 
  ...
  public void service_eventListRetrievedFromService(List<Contact> result) {
        status.setText("Retrieved contact list");
        this.contacts = result;
        this.contactGrid.clear();
        this.contactGrid.resizeRows(this.contacts.size());
        int row = 0;
        
        for (Contact contact : result) {
            this.contactGrid.setWidget(row, 0, new Label(contact.getName()));
            this.contactGrid.setWidget(row, 1, new Label (contact.getPhone()));
            this.contactGrid.setWidget(row, 2, new Label (contact.getEmail()));
            this.contactGrid.setWidget(row, EDIT_LINK, new Hyperlink("Edit", null));
            this.contactGrid.setWidget(row, REMOVE_LINK, new Hyperlink("Remove", null));
            row ++;
        }
    }

La méthode de gestion d'événement service_eventListRetrievedFromServic stocke la liste de contacts envoyée par le serveur. Puis elle nettoie contactGrid qui affiche cette liste. Elle met à jour le nombre de lignes nécessaires pour correspondre à la taille de la liste retournée par le serveur. Ensuite elle itère sur cette liste de contacts, plaçant le nom, le téléphone et l'adresse électronique de chaque contact dans les trois première colonnes de chaque ligne. Elle fournit également des liens d'édition et de suppression pour chaque contact, permettant ainsi aux utilisateurs de modifier et de supprimer facilement des contacts.

Edition d'un contact existant

Quand un utilisateur clique sur le lien d'édition de la liste des contacts, gui_eventContactGridClicked est appelée, comme le montre le Listing 15:

Listing 15. Méthode de gestion d'événement gui_eventContactGridClicked

public class ContactListGUI { 
  ...

    public void gui_eventContactGridClicked(Cell cellClicked) {
         int row = cellClicked.getRowIndex();
         int col = cellClicked.getCellIndex();
        
         Contact contact = this.contacts.get(row);
         this.status.setText("Name was " + contact.getName() + " clicked ");
        
         if (col==EDIT_LINK) {
             this.addNewButton.setVisible(false);
             this.updateButton.setVisible(true);
             this.addButton.setVisible(false);
             this.emailField.setReadOnly(true);
             loadForm(contact);
         } else if (col==REMOVE_LINK) {
             this.contactService.removeContact(contact);
         }
    }
   ...
    private void loadForm(Contact contact) {
        this.formGrid.setVisible(true);
        currentContact = contact;
        this.emailField.setText(contact.getEmail());
        this.phoneField.setText(contact.getPhone());
        this.nameField.setText(contact.getName());
    }

La méthode gui_eventContactGridClicked doit déterminer si c'est le lien d'édition ou de suppression qui a été cliqué, ce qu'elle fait en trouvant quelle colonne a été cliquée. Elle cache ensuite addNewButton et addButton et rend visible updateButton. updateButton s'affiche dans formGrid et permet à l'utilisateur d'envoyer l'information mise à jour à ContactService. Elle met aussi emailField en lecture seule pour que l'utilisateur ne puisse pas l'éditer. Ensuite, gui_eventContactGridClicked appelle loadForm qui rend visible, positionne le contact qui doit être édité puis copie les propriétés du contact dans les composants emailField, phoneField, et nameField.

Quand l'utilisateur clique updateButton, la méthode de gestion d'événement gui_eventUpdateButtonClicked est appelée, comme le montre le Listing 16. Cette méthode rend addNewButton visible (pour que l'utilisateur puisse ajouter des contacts) et cache formGrid. Elle appelle ensuite copyFieldDateToContact, qui copie le texte des composants emailField, phoneField, et nameField dans les propriétés de currentContact. Elle appelle ensuite la méthode ContactServiceDelegate.updateContact en passant le contact modifié en paramètre.

Listing 16. Méthode de gestion d'événement gui_eventUpdateButtonClicked

public class ContactListGUI { 
  ...

    public void gui_eventUpdateButtonClicked() {
        addNewButton.setVisible(true);
        formGrid.setVisible(false);
        copyFieldDateToContact();
        this.contactService.updateContact(currentContact);
    }
    private void copyFieldDateToContact() {
        currentContact.setEmail(emailField.getText());
        currentContact.setName(nameField.getText());
        currentContact.setPhone(phoneField.getText());
    }

 

Ces deux scénarions devraient vous donner une idée du fonctionnement de l'application, et de la manière dont il s'appuie sur l'infrastructure fournie par l'App Engine pour Java. Le code complet de ContactListGUI se trouve au Listing 17:

Listing 17. Code complet de ContactListGUI

package gaej.example.contact.client;

import java.util.List;

import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.Hyperlink;
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.HTMLTable.Cell;

public class ContactListGUI {
    /* Constants. */
    private static final String CONTACT_LISTING_ROOT_PANEL = "contactListing";
    private static final String CONTACT_FORM_ROOT_PANEL = "contactForm";
    private static final String CONTACT_STATUS_ROOT_PANEL = "contactStatus";
    private static final String CONTACT_TOOL_BAR_ROOT_PANEL = "contactToolBar";
    private static final int EDIT_LINK = 3;
    private static final int REMOVE_LINK = 4;

    /* GUI Widgets */
    protected Button addButton;
    protected Button updateButton;
    protected Button addNewButton;
    protected TextBox nameField;
    protected TextBox emailField;
    protected TextBox phoneField;
    protected Label status;
    protected Grid contactGrid;
    protected Grid formGrid;
    
    /* Data model */
    private List<Contact> contacts;
    private Contact currentContact;
    protected ContactServiceDelegate contactService;
        
    public void init() {
        addButton = new Button("Add new contact");
        addNewButton = new Button("Add new contact");
        updateButton = new Button("Update contact");
        nameField = new TextBox();
        emailField = new TextBox();
        phoneField = new TextBox();
        status = new Label();
        contactGrid = new Grid(2,5);

        buildForm();
        placeWidgets();
    }
    
    private void buildForm() {
        formGrid = new Grid(4,3);
        formGrid.setVisible(false);
        
        formGrid.setWidget(0, 0, new Label("Name"));
        formGrid.setWidget(0, 1, nameField);

        formGrid.setWidget(1, 0, new Label("email"));
        formGrid.setWidget(1, 1, emailField);
        
        formGrid.setWidget(2, 0, new Label("phone"));
        formGrid.setWidget(2, 1, phoneField);
        
        formGrid.setWidget(3, 0, updateButton);
        formGrid.setWidget(3, 1, addButton);
        
    }

    private void placeWidgets() {
        RootPanel.get(CONTACT_LISTING_ROOT_PANEL).add(contactGrid);
        RootPanel.get(CONTACT_FORM_ROOT_PANEL).add(formGrid);
        RootPanel.get(CONTACT_STATUS_ROOT_PANEL).add(status);
        RootPanel.get(CONTACT_TOOL_BAR_ROOT_PANEL).add(addNewButton);
    }

    private void loadForm(Contact contact) {
        this.formGrid.setVisible(true);
        currentContact = contact;
        this.emailField.setText(contact.getEmail());
        this.phoneField.setText(contact.getPhone());
        this.nameField.setText(contact.getName());
    }


    private void copyFieldDateToContact() {
        currentContact.setEmail(emailField.getText());
        currentContact.setName(nameField.getText());
        currentContact.setPhone(phoneField.getText());
    }

    public void gui_eventContactGridClicked(Cell cellClicked) {
         int row = cellClicked.getRowIndex();
         int col = cellClicked.getCellIndex();
        
         Contact contact = this.contacts.get(row);
         this.status.setText("Name was " + contact.getName() + " clicked ");
        
         if (col==EDIT_LINK) {
             this.addNewButton.setVisible(false);
             this.updateButton.setVisible(true);
             this.addButton.setVisible(false);
             this.emailField.setReadOnly(true);
             loadForm(contact);
         } else if (col==REMOVE_LINK) {
             this.contactService.removeContact(contact);
         }
    }


    public void gui_eventAddButtonClicked() {
        addNewButton.setVisible(true);
        formGrid.setVisible(false);
        copyFieldDateToContact();
        this.phoneField.getText();
        this.contactService.addContact(currentContact);
    }

    public void gui_eventUpdateButtonClicked() {
        addNewButton.setVisible(true);
        formGrid.setVisible(false);
        copyFieldDateToContact();
        this.contactService.updateContact(currentContact);
    }

    public void gui_eventAddNewButtonClicked() {
        this.addNewButton.setVisible(false);
        this.updateButton.setVisible(false);
        this.addButton.setVisible(true);
        this.emailField.setReadOnly(false);
        loadForm(new Contact());
    }


    public void service_eventListRetrievedFromService(List<Contact> result) {
        status.setText("Retrieved contact list");
        this.contacts = result;
        this.contactGrid.clear();
        this.contactGrid.resizeRows(this.contacts.size());
        int row = 0;
        
        for (Contact contact : result) {
            this.contactGrid.setWidget(row, 0, new Label(contact.getName()));
            this.contactGrid.setWidget(row, 1, new Label (contact.getPhone()));
            this.contactGrid.setWidget(row, 2, new Label (contact.getEmail()));
            this.contactGrid.setWidget(row, EDIT_LINK, new Hyperlink("Edit", null));
            this.contactGrid.setWidget(row, REMOVE_LINK, new Hyperlink("Remove", null));
            row ++;
        }
    }

    public void service_eventAddContactSuccessful() {
        status.setText("Contact was successfully added");
        this.contactService.listContacts();
    }

    public void service_eventUpdateSuccessful() {
        status.setText("Contact was successfully updated");
        this.contactService.listContacts();
    }
    public void service_eventRemoveContactSuccessful() {
        status.setText("Contact was removed");
        this.contactService.listContacts();
        
    }

    public void service_eventUpdateContactFailed(Throwable caught) {
        status.setText("Update contact failed");
    }

    public void service_eventAddContactFailed(Throwable caught) {
        status.setText("Unable to update contact");
    }

    public void service_eventRemoveContactFailed(Throwable caught) {
        status.setText("Remove contact failed");
    }

    public void service_eventListContactsFailed(Throwable caught) {
        status.setText("Unable to get contact list");
        
    }

}

 

Conclusion

Cette seconde partie de l'introduction en trois volets à Google App Engine pour Java vous à initier à la façon de créer une application GWT en utilisant le plugin Eclipse pour l'App Engine. En construisant une application simple de gestion des contacts, vous avez appris comment:

  • Construire des services distants pouvant être invoqués de manière asynchrone
  • Organiser le code de l'IHM pour éviter les déclarations de classes internes imbriquées
  • Utiliser GWT pour implémenter les fonctionnalités de deux cas d'utilisation clés

Restez à l'écoute pour le troisième volet, dans lequel vous apporterez des raffinements à l'application de gestion des contacts et ajouterez le support de la persistence avec les fonctionnalités offertes par le magasin de données de l'App Engine.