Google App Engine para Java: Parte 2: Criando o Aplicativo de Interrupção

Crie seu próprio aplicativo de gerenciamento de contato no App Engine

O principal objetivo de uma plataforma em nuvem do Google App Engine para Java™ é poder imaginar, criar e implementar "killer apps" escaláveis — sem precisar gastar muito ou ficar louco. Nesta segunda parte da introdução de três partes do Google App Engine para Java, Rick Hightower leva você para além dos exemplos já mostrados na Parte 1 com um guia passo a passo para gravar e implementar um aplicativo de gerenciamento de contato simples usando o App Engine para Java.

Richard Hightower, CTO, Trivera Technologies

Rick Hightower é Diretor de Tecnologia da Mammatus Inc., uma empresa de treinamento especializada em computação em nuvem e desenvolvimento em GWT, Java EE, Spring e Hibernate. Ele é co-autor do popular livro Java Tools for Extreme Programming e autor da primeira edição do Struts Live— o download número um no TheServerSide.com por muitos anos. Ele também escreveu artigos e tutoriais para o IBM developerWorks e faz parte do corpo editorial do Java Developer's Journal, como também é um frequente contribuidor dos tópicos Java e Groovy na DZone.



17/Dez/2009

Na Parte 1 dessa introdução para criar aplicativos Java escaláveis com o App Engine para Java, você aprendeu sobre o conjunto de ferramentas Eclipse e a infraestrutura da plataforma de computação em nuvem da Google (ou PAAS) para desenvolvedores Java. Os exemplos nesse artigo foram pré-desenvolvidos, para que você pudesse se concentrar na integração do App Engine para Java com o Eclipse e começasse a criar e a desenvolver rapidamente diferentes tipos de aplicativos — , ou seja, um tipo criado usando o Google Web Toolkit (GWT) e um aplicativo baseado em servlet. Esse artigo aborda esses princípios básicos e também prepara você para realizar exercícios de programação mais avançados na Parte 3 desse artigo.

O aplicativo de gerenciamento de contato que você criará permite que o usuário armazene informações básicas de contato, como nome, endereço e número de telefone. Para criar esse aplicativo, você usará o assistente de criação de projeto GWT do Eclipse.

Do CRUD para contato

A primeira etapa para criar um novo aplicativo no App Engine para Java, como você já sabe, é ativar o assistente de criação de projeto no Eclipse. A partir daí, ative o assistente iniciador de projeto para criar o projeto GWT. (A Parte 1 desse artigo mostra instruções detalhadas para criar um projeto GWT no App Engine para Java.)

Para esse exercício, você começará com um aplicativo CRUD simples e, mais tarde, incluirá um armazenamento real. Por enquanto, use o data access object (DAO) que possui uma implementação mock, como mostrado na Listagem 1:

Listagem 1. Interface para 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 inclui métodos para adicionar, remover e atualizar um contato e retornar uma lista de todos os contatos. É uma interface CRUD básica para gerenciar contatos. A classe Contato é o seu objeto de domínio mostrado na Listagem 2:

Listagem 2. Objeto de Domínio de Contato (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;
    }
        

}

Para essa primeira versão do aplicativo, você trabalhará com um objeto mock que armazena os contatos em uma coleta em memória, como mostra a Listagem 3:

Listagem 3. Classe DAO Mock
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);
    }

}

Criando Serviços Remotos

O objetivo agora é criar uma GUI GWT para poder usar o DAO. Ela deverá usar todos os métodos na interface ContactDAO . A primeira etapa é agrupar a funcionalidade da classeDAO (futuras versões, na realidade, abordarão o armazenamento de dados no lado do servidor para que esteja no servidor) em um serviço, como mostra a Listagem 4:

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

}

Note que o ContactServiceImpl implementa RemoteServiceServlet e, em seguida, definie os métodos para incluir, listar, remover e atualizar um contato. Ele designa todas essas operações no ContactDAOMock. O ContactServiceImpl é apenas um wrapper em torno de ContactDAO que expõe a funcionalidade ContactDAO à GUI GWT. O ContactServiceImpl é mapeado no arquivo web.xml para o URI /contactlist/contacts no arquivo web.xml, como mostra a Listagem 5:

Listagem 5. web.xml para 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>

Para permitir que o frontend da GUI acesse esse serviço, é necessário definir uma interface de serviço remota e uma interface de serviço remota assíncrona, como mostra a Listagem 6 e 7:

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

Note que o ContactService implementa a interface RemoteService e define um @RemoteServiceRelativePath que especifica um caminho relativo de"contacts." O caminho relativo corresponde ao caminho definido para o serviço no arquivo web.xml (eles devem corresponder). O ContactServiceAsync possui objetos de retorno de chamada para que a GUI do GWT possa receber notificação de chamadas do servidor sem interromper a atividade de outro cliente.

Recortando através do código espaguete

Eu não sou muito fã desse código sempre que puder evito gravá-lo. Um exemplo de código espaguete seria um grupo de classes internas anônimas cujos métodos definem classes internas anônimas. Essas classes internas, por sua vez, fazem retornos de chamadas que chamam os métodos que são, em seguida, definidos sequencialmente em uma classe interna. Nossa! Fala sério, eu não conseguiria ler nem entender um código tão complicado desse jeito, mesmo se for meu mesmo! Então, para dar uma simplificada na coisa, vamos dividir a GUI do GWT em três partes:

  • ContactListEntryPoint
  • ContactServiceDelegate
  • ContactListGUI

O ContactListEntryPoint é o ponto de entrada principal, que faz a ligação de evento da GUI. O ContactServiceDelegate agrupa a funcionalidade ContactService e oculta a ligação do retorno de chamada da classe interna. O ContactListGUI gerencia todos os componentes da GUI e manipula eventos a partir da GUI e do Serviço. O ContactListGUI usa o ContactServiceDelegate para fazer pedidos do ContactService.

O arquivo ContactList.gwt.xml (um recurso no gaej.example.contact) especifica ContactListEntryPoint como o ponto de entrada principal para o aplicativo usando o elemento entry-point mostrado na Listagem 8:

Listagem 8. ContactList.gwt.xml
<entry-point class='gaej.example.contact.client.ContactListEntryPoint'/>

A classe ContactListEntryPoint implementa a interface EntryPoint a partir do GWT (com.google.gwt.core.client.EntryPoint), significando que essa classe será chamada para inicializar a GUI. ContactListEntryPoint não faz muita coisa. Ela cria uma instância do ContactListGUI e uma instância do ContactServiceDelegatee, em seguida, permite que eles se conheçam para que possam colaborar. A classe ContactListEntryPoint faz, em seguida, a ligação de evento da GUI. ContactListEntryPoint é mostrado na Listagem 9:

Listagem 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();
                
            }});

    }
}

Note que o ContactListEntryPoint liga eventos para addButton, updateButton, contactGride o addNewButton. Ele faz isso ao registrar as classes internas anônimas que implementam a interface de listener para os eventos de widget. Essa é uma técnica muito semelhante à manipulação de eventos no Swing. Os eventos de widget são originados de widgets criados pela GUI (ContactListGUI), que falarei um pouco dele. Note que a classe GUI possui os métodos gui_eventXXX para responder aos eventos da GUI.

A ContactListGUI cria os widgets GUI e responde aos eventos a partir deles. ContactListGUI converte os eventos da GUI em ações que o usuário deseja executar no ContactsService. ContactListGUI usa o ContactServiceDelegate para chamar métodos no ContactService. O ContactServiceDelegate cria uma interface assíncrona no ContactService e utiliza-a para fazer chamadas Ajax assíncronas. O ContactServiceDelegate notifica a ContactListGUI de eventos (retornos de êxito ou falha) a partir do serviço. O ContactServiceDelegate é mostrado na Listagem 10:

Listagem 10. ContactServiceDelegatepackage gaej.example.contact.client;
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.        
    }
    
}

Note que o ContactServiceDelegate notifica a ContactListGUI de eventos de serviço através de métodos que começam no service_eventXXX. Conforme disse anteriormente, um dos meus objetivos ao gravar ContactListGUI era evitar as classes internas aninhadas e criar uma classe GUI simples relativa (uma que eu pudesse ler e trabalhar facilmente mais tarde). A ContactListGUI possui apenas 186 e é muito mais objetiva. A ContactListGUI gerencia nove widgets da GUI e também colabora com o ContactServiceDelegate para gerenciar uma listagem de CRUD, como mostra a Listagem 11:

Listagem 11. ContactListGUI em funcionamento
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;

Note que a ContactListGUI mantém o controle do contato atual carregado no formulário (currentContact) e da lista de contatos na listagem (contacts). A Figura 1 mostra como os widgets correspondem à GUI que é criada:

Figura 1. Widgets ativos na GUI de gerenciamento de contato
Widgets ativos na GUI de gerenciamento de contato

A Listagem 12 mostra como a ContactListGUI cria widgets e um formulário de contato e, depois, coloca os widgets no formulário:

Listagem 12. ContactListGUI criando e colocando widgets
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);
    }

O método ContactListGUIinit é chamado pelo método ContactListEntryPoint.onModuleLoad . O método init chama o método buildForm para criar uma nova grade de formulário e preenchê-la com campos para editar dados de contato. O método init , em seguida, chama o método placeWidgets , que coloca os widgets contactGrid, formGrid, status e addNewButton nos slots definidos na página HTML que hospeda esse aplicativo de GUI definido na Listagem 13:

Listagem 13. ContactList.html define slots para widgets
<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>

As constantes (como CONTACT_LISTING_ROOT_PANEL="contactListing") correspondem aos IDs dos elementos (como id="contactListing") definidos na página HTML. Isso permite que um designer de página tenha maior controle sobre o layout dos widgets de aplicativos.

Com o aplicativo básico criado, vamos percorrer alguns cenários de uso comuns.

Mostrando uma listagem no carregamento de página

Quando a página do aplicativo de gerenciamento de contato é carregado pela primeira vez, ela chama o método onModuleLoad do ContactListEntryPoint . onModuleLoad chama o método listContacts do ContactServiceDelegate , que chama o método listContact do serviço assincronamente. Quando o método listContact é retornado, uma classe interna anônima definida no ContactServiceDelegate chama o método de manipulador de evento de serviço chamado service_eventListRetrievedFromService, mostrado na Listagem 14:

Listagem 14. Chamando o manipulador de eventos 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 ++;
        }
    }

O método de manipulador de eventos service_eventListRetrievedFromService armazena a lista de contatos enviada pelo servidor. Ele desmarca o contactGrid que exibe a lista de contatos. Ele especifica novamente o número de linhas para corresponder ao tamanho da lista de contatos retornada do servidor. Em seguida, ele interage com a lista de contatos, colocando dados de nome,telefone e de e-mail em cada contato nas primeiras três colunas de cada linha. Ele também fornece um link Editar e um link Remover para cada contato, permitindo que os usuários editem ou removam facilmente os contatos.

Usuário edita um contato existente

Quando um usuário clica em um link Editar na lista de contatos, o gui_eventContactGridClicked é chamado, como mostra a Listagem 15:

Listagem 15. ContactListGUI's gui_eventContactGridClicked event handler method
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());
    }

O método gui_eventContactGridClicked deve determinar se o link Editar ou o link Remover foi clicado. Isso é feito ao descobrir qual coluna foi clicado. Em seguida, ele oculta o addNewButton e o addButton e torna o updateButton visível. O updateButton é exibido no formGrid e permite que o usuário envie as informações de atualização de volta para ContactService. Ele também torna o emailField somente leitura para que o usuário não possa editar o campo de e-mail. Em seguida, o gui_eventContactGridClicked chama o loadForm (mostrado na Listagem 15), que define o formGrid para visível, configura o contato que está sendo editado e, em seguida, copia as propriedades de contato para os widgets emailField, phoneField e nameField .

Quando o usuário clica no updateButton, o método de manipulador de eventos gui_eventUpdateButtonClicked é chamado, como mostra a Listagem 16.Esse método torna o addNewButton visível (para que o usuário possa incluir novos contatos) e oculta aformGrid. Em seguida, ele chama o copyFieldDateToContact, que copia o texto dos widgets emailField, phoneField e nameField de volta para as propriedades do currentContact. Em seguida, ele chama o método updateContactContactServiceDelegate para transmitir o contato atualizado mais recentemente de volta para o serviço.

Listagem 16. ContactListGUI's gui_eventUpdateButtonClicked event-handler method
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());
    }

Esses dois cenários dão uma ideia de como o aplicativo funciona, além de explorar a infraestrutura fornecida pelo App Engine para Java. O código completo para a ContactListGUI é mostrado na Listagem 17:

Listagem 17. Código completo para 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");
        
    }

}

Conclusão

Essa segunda parte de uma introdução de três partes para o Google App Engine para Java mostrou o processo para criar um aplicativo GWT customizado usando as ferramentas de plug-in do Eclipse para o App Engine para Java. Na construção do aplicativo de gerenciamento de contato simples, você aprendeu a:

  • Criar serviços remotos que trabalham de forma assíncrona.
  • Organizar o código da GUI para evitar declarações de classes internas aninhadas.
  • Utilizar o GWT para implementar a funcionalidade para dois casos de uso principais

Siga a Parte 3 deste artigo, onde você refinará o aplicativo de gerenciamento de contato e incluirá suporte para a persistir os objetos de Contato com os recursos de armazenamento de dados da App Engine para Java.

Recursos

Aprender

Obter produtos e tecnologias

Discutir

Comentários

developerWorks: Conecte-se

Los campos obligatorios están marcados con un asterisco (*).


Precisa de um ID IBM?
Esqueceu seu ID IBM?


Esqueceu sua senha?
Alterar sua senha

Ao clicar em Enviar, você concorda com os termos e condições do developerWorks.

 


A primeira vez que você entrar no developerWorks, um perfil é criado para você. Informações no seu perfil (seu nome, país / região, e nome da empresa) é apresentado ao público e vai acompanhar qualquer conteúdo que você postar, a menos que você opte por esconder o nome da empresa. Você pode atualizar sua conta IBM a qualquer momento.

Todas as informações enviadas são seguras.

Elija su nombre para mostrar



Ao se conectar ao developerWorks pela primeira vez, é criado um perfil para você e é necessário selecionar um nome de exibição. O nome de exibição acompanhará o conteúdo que você postar no developerWorks.

Escolha um nome de exibição de 3 - 31 caracteres. Seu nome de exibição deve ser exclusivo na comunidade do developerWorks e não deve ser o seu endereço de email por motivo de privacidade.

Los campos obligatorios están marcados con un asterisco (*).

(Escolha um nome de exibição de 3 - 31 caracteres.)

Ao clicar em Enviar, você concorda com os termos e condições do developerWorks.

 


Todas as informações enviadas são seguras.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=80
Zone=Tecnologia Java, Software livre, Cloud computing
ArticleID=457482
ArticleTitle=Google App Engine para Java: Parte 2: Criando o Aplicativo de Interrupção
publish-date=12172009