首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 软件管理 > 软件架构设计 >

Google App Engine for Java: 第 二 部分:构建杀手级应用程序

2012-10-26 
Google App Engine for Java: 第 2 部分:构建杀手级应用程序【原文地址】:http://www.ibm.com/developerwork

Google App Engine for Java: 第 2 部分:构建杀手级应用程序
【原文地址】:http://www.ibm.com/developerworks/cn/java/j-gaej2/#main
诸如 Google App Engine for Java? 这样的云平台的关键在于能够设计、构建和部署专业级的应用程序 —— 可以非常容易地进行伸缩。在这个包含三部分的 Google App Engine for Java 系列文章第二篇中,Rick Hightower 将通过一个分步指南,使用 Google App Engine for Java 来编写和部署一个简单的联系人管理应用程序,从而超越 第 1 部分 中提供的现成示例。
在介绍使用 App Engine for Java 构建可伸缩 Java 应用程序的 第 1 部分 中,您了解了 Google 云计算平台(即 PAAS)为 Java 开发人员提供的 Eclipse 工具和基础设施。该文章中的示例都是预先准备的,这样您可以将精力集中到 App Engine for Java 与 Eclipse 的集成中,并快速构建和部署不同类型的应用程序 — 即使用 Google Web Toolkit (GWT) 构建的应用程序和基于 servlet 的应用程序。本文将在此基础上展开,并且在本系列第 3 部分中提供了更加高级的编程实践。
您将构建的联系人管理应用程序允许用户存储基本的联系人信息,比如名称、电子邮件地址和电话号码。要创建这个应用程序,将需要使用 Eclipse GWT 项目创建向导。

云计算空间
您是否希望随时获取最新的云计算消息?是否想得到云计算相关的技术知识?developerWorks 云计算空间就是这样一个云计算信息资源的门户,在这里您可以了解来自 IBM 和业界其他媒体的最新信息,并且得到如何在云环境中使用 IBM 软件的入门知识。
IBM 在 Amazon EC2 云计算环境中提供了 DB2、Informix、Lotus、WebSphere 等方面的 AMI 镜像资源。您只需按使用量支付少量费用,就可以使用到云上的数据、门户、Web 内容管理、情景应用等服务。欢迎您随时访问 云计算空间,获取更多信息。
从 CRUD 到联系人应用程序
正如目前您已经了解到的一样,在 App Engine for Java 中构建新应用程序的第一步就是在 Eclipse 启动项目创建向导。之后,您可以打开 GWT 项目启动向导来创建 GWT 项目(本文 第 1 部分 给出了在 App Engine for Java 中创建 GWT 项目的详细说明)。
对于这个练习,您将启动一个简单的 CRUD 应用程序,并稍后添加实际的存储。我们将使用一个具有模拟实现的数据访问对象(DAO),如清单 1 所示:

清单 1. 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 添加了各种方法,可以添加联系人、删除联系人、更新联系人,并返回一个所有联系人的列表。它是一个非常基本的 CRUD 接口,可以管理联系人。Contact 类是您的域对象,如清单 2 所示:

清单 2. 联系人域对象(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;
    }
       

}

对于这个应用程序的第一个版本,您将使用一个模拟对象将联系人存储在一个内存集合中,如清单 3 所示:

清单 3. Mock DAO 类


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

}

创建远程服务
您现在的目标是创建一个允许您使用 DAO 的 GWT GUI。将使用 ContactDAO 接口上的所有方法。第一步是将 DAP 类(未来版本将直接与服务器端的数据存储通信,因此必须位于服务器中)的功能封装到一个服务中,如清单 4 所示:

清单 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);
    }
   

}

注意,ContactServiceImpl 实现了 RemoteServiceServlet,随后定义方法来添加联系人、列出联系人、删除联系人,以及更新联系人。它将所有这些操作委托给 ContactDAOMock。ContactServiceImpl 不过是一个围绕 ContactDAO 的包装器,后者将 ContactDAO 功能公开给 GWT GUI。ContactServiceImpl 在 web.xml 文件中被映射到 URI /contactlist/contacts,如清单 5 所示:

清单 5. web.xml 中的 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>

要使 GUI 前端访问该服务,需要定义一个远程服务接口和一个异步远程服务接口,如清单 6 和 7 所示:

清单 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);
}


清单 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);
}

注意,ContactService 实现了 RemoteService 接口并定义了一个 @RemoteServiceRelativePath,指定了 “联系人” 的相对路径。相对路径与您在 web.xml 文件中为服务定义的路径是对应的(必须匹配)。ContactServiceAsync 包含回调对象,因此 GWT GUI 可以收到来自服务器的调用的通知,而不会阻塞其他客户机行为。
避免编写杂乱的代码
我并不喜欢编写杂乱的代码,因此在可能的情况下会尽量避免编写这类代码。这类代码的一个例子就是一组匿名内部类,这些类的方法定义匿名内部类。这些内部类反过来执行回调,调用在某个内部类中以内联方式定义的方法。坦白说,我无法阅读或是理解这些纠缠在一起的代码,即使是我自己编写的!因此,为了将代码稍微简单化,我建议将 GWT GUI 分解为三个部分:
ContactListEntryPoint
ContactServiceDelegate
ContactListGUI
ContactListEntryPoint 是主要的入口点;它执行 GUI 事件连接。ContactServiceDelegate 封装 ContactService 功能并隐藏内部类回调连接。ContactListGUI 管理所有 GUI 组件并处理来自 GUI 和 Service 的事件。ContactListGUI 使用 ContactServiceDelegate 发出 ContactService 请求。
ContactList.gwt.xml 文件(位于 gaej.example.contact 下的一个资源)使用 entry-point 元素将 ContactListEntryPoint 指定为应用程序的主要入口点,如清单 8 所示:

清单 8. ContactList.gwt.xml

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

ContactListEntryPoint 类实现了 GWT 的 EntryPoint 接口(com.google.gwt.core.client.EntryPoint),并指定将调用该类来初始化 GUI。ContactListEntryPoint 所做的工作并不多。它创建一个 ContactListGUI 实例和一个 ContactServiceDelegate 实例,然后让它们彼此了解对方,这样就可以展开协作。ContactListEntryPoint 然后执行 GUI 事件连接。ContactListEntryPoint 如清单 9 所示:

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

    }
}

注意,ContactListEntryPoint 为 addButton、updateButton、contactGrid 和 addNewButton 连接事件。具体做法是注册为小部件事件实现侦听器接口的匿名内部类。这与 Swing 中的事件处理非常相似。这些小部件事件来自由 GUI 创建的小部件(ContactListGUI),我将稍后进行讨论。注意,GUI 类包含 gui_eventXXX 方法来响应 GUI 事件。
ContactListGUI 创建了 GUI 小部件并响应来自它们的事件。ContactListGUI 将 GUI 事件转换为用户希望对 ContactsService 执行的操作。ContactListGUI 使用 ContactServiceDelegate 对 ContactService 调用方法。ContactServiceDelegate 对 ContactService 创建一个异步接口并使用它发出异步 Ajax 调用。ContactServiceDelegate 向 ContactListGUI 通知来自服务的事件(成功或失败)。ContactServiceDelegate 如清单 10 所示:

清单 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.       
    }
   
}

注意,ContactServiceDelegate 通过以 service_eventXXX 开头的方法向 ContactListGUI 发出有关服务事件的通知。如前所述,我编写 ContactListGUI 的目标之一就是避免嵌套的内部类并创建一个相对扁平的 GUI 类(我可以非常方便地阅读和理解的类)。ContactListGUI 只有 186 行,因此非常简单。ContactListGUI 管理 9 个 GUI 小部件并与 ContactServiceDelegate 协作来管理一个 CRUD 清单,如清单 11 所示:

清单 11. 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;

注意,ContactListGUI 跟踪表单中加载的当前联系人(currentContact)和清单中的联系人列表(contacts)。图 1 展示了小部件如何对应于创建的 GUI:

图 1. 联系人管理 GUI 中活动的小部件

清单 12 展示了 ContactListGUI 如何创建小部件和联系人表单,并将小部件放到表单中:

清单 12. ContactListGUI 创建并放置小部件

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

ContactListGUI init 方法由 ContactListEntryPoint.onModuleLoad 方法创建。init 方法调用 buildForm 方法来创建新的表单网格并使用字段填充,以编辑联系人数据。init 方法随后调用 placeWidgets 方法,随后将 contactGrid、formGrid、status 和 addNewButton 小部件放到 HTML 页面中定义的插槽中,这个 HTML 页面托管了清单 13 中定义的 GUI 应用程序:

清单 13. ContactList.html 定义了用于小部件的插槽


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

常量(比如 CONTACT_LISTING_ROOT_PANEL="contactListing")对应于 HTML 页面中定义的元素的 ID(类似 id="contactListing")。这允许页面设计师进一步控制应用程序小部件的布局。
对于基本的应用程序构建,让我们了解几个常见的使用场景。
展示一个有关页面加载的链接
当联系人管理应用程序的页面首次加载时,它将调用 ContactListEntryPoint 的 onModuleLoad 方法。onModuleLoad 调用 ContactServiceDelegate 的 listContacts 方法,后者异步调用服务的 listContact 方法。当 listContact 方法返回时,ContactServiceDelegate 中定义的匿名内部类将调用名为 service_eventListRetrievedFromService 的服务事件处理器方法,如清单 14 所示:

清单 14. 调用 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 ++;
        }
    }

service_eventListRetrievedFromService 事件处理器方法存储由服务器发送的联系人列表。然后它将清空显示联系人列表的 contactGrid。它将重新调整行数,以匹配服务器返回的联系人列表的大小。随后遍历联系人列表,将每个联系人的姓名、电话、电子邮件数据放到每一行的前三个列中。它还为每个联系人提供了 Edit 链接和一个 Remove 链接,使用户能够轻松地删除和编辑联系人。
用户编辑现有的联系人
当用户单击联系人列表中的 Edit 链接时,gui_eventContactGridClicked 将得到调用,如清单 15 所示:

清单 15. ContactListGUI 的 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());
    }


gui_eventContactGridClicked 方法必须确定 Edit 链接或 Remove 链接是否被单击。具体做法是找到那个列被单击。随后隐藏 addNewButton 和 addButton,并使 updateButton 可见。updateButton 显示在 formGrid 中,允许用户将更新信息发送回 ContactService。它还使 emailField 变为只读,这样用户就不能编辑电子邮件字段。接下来,gui_eventContactGridClicked 调用 loadForm(如 清单 15 所示),后者将 formGrid 设置为可见,设置正在被编辑的联系人,然后将联系人属性复制到 emailField、phoneField 和 nameField 小部件中。
当用户单击 updateButton 时,gui_eventUpdateButtonClicked 事件处理器方法被调用,如清单 16 所示。这个方法使 addNewButton 变为可见(这样用户就可以编辑新的联系人)并隐藏了 formGrid。它随后调用 copyFieldDateToContact,后者将来自 emailField、phoneField 和 nameField 小部件的文本复制回 currentContact 的属性。随后调用 ContactServiceDelegate updateContact 方法来将新更新的联系人传递回服务。

清单 16. ContactListGUI 的 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());
    }

这两个场景应当使您了解到应用程序是如何工作的,以及它如何依赖于 App Engine for Java 提供的基础设施。ContactListGUI 的完整代码如清单 17 所示:

清单 17. 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");
       
    }

}

热点排行