2013年3月23日星期六

Google Web Toolkit 和 Google App Engine 综合教程 交互篇

前面几篇教程已经把Google Web ToolkitGoogle App Engine 两方面的代码完成了很大部分,这篇教程将让Google Web Toolkit 的客户端代码与 Google App Engine 的服务器端代码联合起来,实现客户端和服务器端的交互。

Google Web Toolkit 如何与服务器交互?

Google Web Toolkit 的程序最终会以JavaScript代码的形式在用户的浏览器上运行。所以,如果要与服务器交互,要使用JavaScript支持的方法。Google Web Toolkit 为我们提供了3种方法。

远程过程调用 (Remote Procedure Calls, GWT RPC)

如果项目的服务器端使用Java,并且为服务器端的操作都使用了各种接口,那么 GWT RPC是最好的选择。因为我们使用 Google App Engine 作为服务器端,使用Java编码,所以接下来将使用 GWT RPC来完成我们接下来的教程。
更详细的有关 Remote Procedure Calls 的介绍,请看这里

HTTP 取回 JSON

如果项目的服务器端没有使用Java,亦或是已经使用了JSON 或 XML,那么就可以通过HTTP来取得JSON来实现与服务器端的交互。
更详细的有关 JSON 的介绍,请看这里

利用 JSONP 协议

如果你对 mashup 很感兴趣,那么一定不能错过 Google Web Toolkit 提供的这种方法。
更详细的有关 JSONP 的介绍,请看这里

定义 Service Interface


RPC Service 要由一个 继承自 RemoteService 的接口来定义。这里我们在 net.kylewu.myideastorm.client.service 包下面新建一个名为 DBWorkerService 的接口。

Java代码  收藏代码
  1. package net.kylewu.idea.client.service;  
  2.   
  3. import com.google.gwt.user.client.rpc.RemoteService;  
  4. import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;  
  5.   
  6. /** 
  7.  * * The client side stub for the RPC service. 
  8.  * */  
  9. @RemoteServiceRelativePath("myidea")  
  10. public interface DBWorkerService extends RemoteService {  
  11.   
  12.     // Remove one idea  
  13.     Boolean delete(Long id);  
  14.   
  15.     String getIdeaBySubject(String subject);  
  16.   
  17.     // Read from db  
  18.     String[] getIdeas();  
  19.   
  20.     // Add a new idea  
  21.     String save(String subject, String detail, String progress);  
  22.   
  23.     // Update one idea  
  24.     String update(String id, String subject, String detail, String progress);  
  25.   
  26. }  


肯定有人注意到了,这里方法返回的值都是String,为什么不使用我们之前定义过的Idea呢。这里我只能说很抱歉了,我测试过返回Idea对 象,但是在运行时会出现 “did you forget to inherit a required module?”的错误,搜索了很久也没有搞定,希望知道的同学联系我,谢谢。

定义服务器端Service实现

服务器端的类要实现刚才写过的 DBWorkerService 接口,在这里实现我们具体的操作。
Java代码  收藏代码
  1. package net.kylewu.idea.server;  
  2.   
  3. import java.text.SimpleDateFormat;  
  4. import java.util.Date;  
  5. import java.util.List;  
  6.   
  7. import javax.jdo.PersistenceManager;  
  8. import javax.jdo.Query;  
  9.   
  10. import net.kylewu.idea.client.service.DBWorkerService;  
  11.   
  12. import com.google.gwt.user.server.rpc.RemoteServiceServlet;  
  13.   
  14. public class DBWorkerServiceImpl extends RemoteServiceServlet implements  
  15.         DBWorkerService {  
  16.   
  17.     private static final long serialVersionUID = 6183858098728558886L;  
  18.   
  19.     @Override  
  20.     public Boolean delete(Long id) {  
  21.         PersistenceManager pm = PMF.get().getPersistenceManager();  
  22.         Idea idea = (Idea) pm.getObjectById(Idea.class, id);  
  23.         pm.deletePersistent(idea);  
  24.         pm.close();  
  25.         return true;  
  26.     }  
  27.   
  28.     @SuppressWarnings("unchecked")  
  29.     @Override  
  30.     public String[] getIdeas() {  
  31.         PersistenceManager pm = PMF.get().getPersistenceManager();  
  32.         String query = "select from " + Idea.class.getName();  
  33.         List ideas = (List) pm.newQuery(query).execute();  
  34.         String[] rtn = new String[ideas.size()];  
  35.         for (int i = 0; i < ideas.size(); i++) {  
  36.             Idea idea = ideas.get(i);  
  37.             rtn[i] = idea.toString();  
  38.         }  
  39.   
  40.         return rtn;  
  41.     }  
  42.   
  43.     @Override  
  44.     public String save(String subject, String detail, String progress) {  
  45.         Idea idea = new Idea(subject, detail, progress,  
  46.                 progress.compareTo("100%") == 0 ? new SimpleDateFormat("yyyy-mm-dd")  
  47.                         .format(new Date()) : "null");  
  48.         PersistenceManager pm = PMF.get().getPersistenceManager();  
  49.         try {  
  50.             pm.makePersistent(idea);  
  51.         } finally {  
  52.             pm.close();  
  53.         }  
  54.         String str = getIdeaBySubject(subject);  
  55.         return str;  
  56.     }  
  57.   
  58.     @Override  
  59.     public String update(String id, String subject, String detail,  
  60.             String progress) {  
  61.         PersistenceManager pm = PMF.get().getPersistenceManager();  
  62.         Idea idea = (Idea) pm.getObjectById(Idea.class, Long.valueOf(id));  
  63.         idea.setSubject(subject);  
  64.         idea.setDetail(detail);  
  65.         idea.setProgress(progress);  
  66.         if (progress.compareTo("100%") == 0  
  67.                 && idea.getDate().compareTo("null") == 0) {  
  68.             idea.setDate(new SimpleDateFormat("yyyy-mm-dd").format(new Date()));  
  69.         }  
  70.         pm.makePersistent(idea);  
  71.   
  72.         String rtn = ((Idea) pm.getObjectById(Idea.class, Long.valueOf(id)))  
  73.                 .toString();  
  74.         pm.close();  
  75.   
  76.         return rtn;  
  77.     }  
  78.   
  79.     @SuppressWarnings("unchecked")  
  80.     @Override  
  81.     public String getIdeaBySubject(String subject) {  
  82.         PersistenceManager pm = PMF.get().getPersistenceManager();  
  83.         Query query = pm.newQuery("select from " + Idea.class.getName()  
  84.                 + " where subject == subjectParm "  
  85.                 + "parameters String subjectParm");  
  86.         List ids = (List) query.execute(subject);  
  87.         if (ids.isEmpty() == false) {  
  88.             pm.close();  
  89.             return ids.get(0).toString();  
  90.         }  
  91.         pm.close();  
  92.         return "";  
  93.     }  
  94.   
  95. }  


如上一篇教程最后部分提到的,这里通过PMF得到PersistenceManager,进而进行数据库的增删改查。

定义 RPC Interface

代码很简单,在我们的DBWorkerService接口中的方法的参数最后添加一个AsyncCallback参数,并且这些方法都是void。

Java代码  收藏代码
  1. package net.kylewu.idea.client.service;  
  2.   
  3. import com.google.gwt.user.client.rpc.AsyncCallback;  
  4.   
  5. /** 
  6.  * The async counterpart of DBWorker. 
  7.  */  
  8. public interface DBWorkerServiceAsync {  
  9.   
  10.     // Remove one idea  
  11.     void delete(Long id, AsyncCallback callback);  
  12.   
  13.     // Read from db  
  14.     void getIdeas(AsyncCallback callback);  
  15.   
  16.     // Add a new idea  
  17.     void save(String subject, String summary, String progress, AsyncCallback callback);  
  18.   
  19.     // Update one idea  
  20.     void update(String id, String summary, String subject, String progress, AsyncCallback callback);  
  21.   
  22.     void getIdeaBySubject(String subject, AsyncCallback callback);  
  23.   
  24. }  

好了,到这里,就可以调用 RPC 接口了。

调用 RPC Interface

重新打开EntryPoint,在类中添加一段如下代码,然后在需要数据库操作的地方添加代码。
private DBWorkerServiceAsync dbWorker = GWT.create(DBWorkerService.class);
这里是最后完整的Kylewuidea类。


Java代码  收藏代码
  1. package net.kylewu.idea.client;  
  2.   
  3. import java.util.ArrayList;  
  4. import java.util.HashMap;  
  5. import java.util.Map;  
  6.   
  7. import net.kylewu.idea.client.service.DBWorkerService;  
  8. import net.kylewu.idea.client.service.DBWorkerServiceAsync;  
  9.   
  10. import com.google.gwt.core.client.EntryPoint;  
  11. import com.google.gwt.core.client.GWT;  
  12. import com.google.gwt.event.dom.client.ClickEvent;  
  13. import com.google.gwt.event.dom.client.ClickHandler;  
  14. import com.google.gwt.user.client.rpc.AsyncCallback;  
  15. import com.google.gwt.user.client.ui.Button;  
  16. import com.google.gwt.user.client.ui.DialogBox;  
  17. import com.google.gwt.user.client.ui.FlexTable;  
  18. import com.google.gwt.user.client.ui.HasHorizontalAlignment;  
  19. import com.google.gwt.user.client.ui.HasVerticalAlignment;  
  20. import com.google.gwt.user.client.ui.HorizontalPanel;  
  21. import com.google.gwt.user.client.ui.ListBox;  
  22. import com.google.gwt.user.client.ui.Panel;  
  23. import com.google.gwt.user.client.ui.RootPanel;  
  24. import com.google.gwt.user.client.ui.TextArea;  
  25. import com.google.gwt.user.client.ui.TextBox;  
  26. import com.google.gwt.user.client.ui.VerticalPanel;  
  27.   
  28. /** 
  29.  * Entry point classes define onModuleLoad(). 
  30.  */  
  31. public class Kylewuidea implements EntryPoint {  
  32.   
  33.     private final int COL_ID = 0;  
  34.     private final int COL_SUBJECT = 1;  
  35.     private final int COL_DETAIL = 2;  
  36.     private final int COL_PROGRESS = 3;  
  37.     private final int COL_TIME = 4;  
  38.     private final int COL_OPERATION = 5;  
  39.   
  40.     private FlexTable table = new FlexTable();  
  41.   
  42.     private ArrayList subjectList = new ArrayList();  
  43.   
  44.     private Map mapStrToInt = new HashMap();  
  45.     private Map mapIntToStr = new HashMap();  
  46.   
  47.     private DBWorkerServiceAsync dbWorker = GWT.create(DBWorkerService.class);  
  48.   
  49.     /** 
  50.      * This is the entry point method. 
  51.      */  
  52.     public void onModuleLoad() {  
  53.   
  54.         // Initial all items.  
  55.         init();  
  56.         // Add table to html page.  
  57.         RootPanel.get("ideastorm").add(createBasePanel());  
  58.         // Initial table.  
  59.         importFromDatabase();  
  60.   
  61.     }  
  62.   
  63.     private void init() {  
  64.   
  65.         mapStrToInt.put("0%"0);  
  66.         mapStrToInt.put("25%"1);  
  67.         mapStrToInt.put("50%"2);  
  68.         mapStrToInt.put("75%"3);  
  69.         mapStrToInt.put("100%"4);  
  70.         mapIntToStr.put(0"0%");  
  71.         mapIntToStr.put(1"25%");  
  72.         mapIntToStr.put(2"50%");  
  73.         mapIntToStr.put(3"75");  
  74.         mapIntToStr.put(4"100%");  
  75.   
  76.         // Initial table structure.  
  77.         table.setText(0, COL_ID, "ID");  
  78.         table.setText(0, COL_SUBJECT, "Subject");  
  79.         table.setText(0, COL_DETAIL, "Detail");  
  80.         table.setText(0, COL_PROGRESS, "Progress");  
  81.         table.setText(0, COL_OPERATION, "Operation");  
  82.         table.setText(0, COL_TIME, "Time");  
  83.   
  84.         // Set table attribute.  
  85.         table.setCellPadding(5);  
  86.         table.getColumnFormatter().setWidth(0"10");  
  87.         table.getColumnFormatter().setWidth(1"200");  
  88.         table.getColumnFormatter().setWidth(2"400");  
  89.         table.getColumnFormatter().setWidth(3"150");  
  90.         table.getColumnFormatter().setWidth(4"100");  
  91.     }  
  92.   
  93.     /** 
  94.      * Initial table data 
  95.      */  
  96.     private void importFromDatabase() {  
  97.         // Get exist ideas from db  
  98.         dbWorker.getIdeas(new AsyncCallback() {  
  99.   
  100.             @Override  
  101.             public void onFailure(Throwable caught) {  
  102.                 // TODO Exception  
  103.   
  104.             }  
  105.   
  106.             @Override  
  107.             public void onSuccess(String[] result) {  
  108.                 for (String idea : result) {  
  109.                     String[] parts = idea.split("\\|");  
  110.                     insertIdeaIntoTable(-1, parts[0], parts[1], parts[2],  
  111.                             parts[3], parts[4]);  
  112.   
  113.                 }  
  114.             }  
  115.   
  116.         });  
  117.   
  118.     }  
  119.   
  120.     /** 
  121.      * Create base panel 
  122.      * 
  123.      * @return 
  124.      */  
  125.     private Panel createBasePanel() {  
  126.         // Base Panel of this project.  
  127.         VerticalPanel mainPanel = new VerticalPanel();  
  128.         Button btnAdd = new Button("Add Idea");  
  129.   
  130.         // Add click handler to add button.  
  131.         btnAdd.addClickHandler(new ClickHandler() {  
  132.             public void onClick(ClickEvent event) {  
  133.                 // Show Add Idea Dialog  
  134.                 showIdeaEditDialog(true, -1);  
  135.             }  
  136.         });  
  137.   
  138.         // Assemble the panel.  
  139.         mainPanel.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_CENTER);  
  140.         mainPanel.add(table);  
  141.         mainPanel.add(btnAdd);  
  142.   
  143.         return mainPanel;  
  144.     }  
  145.   
  146.     /** 
  147.      * Show Add Idea Dialog 
  148.      */  
  149.     private void showIdeaEditDialog(final boolean isNew, final int index) {  
  150.         // Initial Add Idea Dialog.  
  151.         final DialogBox dialog = new DialogBox();  
  152.         final TextBox txtBoxSubject = new TextBox();  
  153.         final TextArea txtAreaDetail = new TextArea();  
  154.         final ListBox listBox = new ListBox();  
  155.         VerticalPanel dialogPanel = new VerticalPanel();  
  156.         HorizontalPanel itemPanel = new HorizontalPanel();  
  157.         Button btnInsert = new Button();  
  158.         Button btnClose = new Button("Close");  
  159.   
  160.         // Set attribute.  
  161.         dialog.setText("Input your idea");  
  162.         dialog.setAnimationEnabled(true);  
  163.         txtAreaDetail.setSize("300""380");  
  164.         listBox.clear();  
  165.         listBox.addItem("0%");  
  166.         listBox.addItem("25%");  
  167.         listBox.addItem("50%");  
  168.         listBox.addItem("75%");  
  169.         listBox.addItem("100%");  
  170.         listBox.setVisibleItemCount(5);  
  171.   
  172.         if (isNew) {  
  173.             btnInsert.setText("Insert");  
  174.             txtBoxSubject.setText("Input your indea");  
  175.             listBox.setSelectedIndex(0);  
  176.         } else {  
  177.             btnInsert.setText("Update");  
  178.             txtBoxSubject.setText(table.getText(index, COL_SUBJECT));  
  179.             txtAreaDetail.setText(table.getText(index, COL_DETAIL));  
  180.             listBox.setSelectedIndex(mapStrToInt.get(table.getText(index,  
  181.                     COL_PROGRESS)));  
  182.             if (table.getText(index, COL_PROGRESS).compareTo("100%") == 0  )  
  183.                 listBox.setEnabled(false);  
  184.         }  
  185.   
  186.         // Add ClickHandler to Insert button  
  187.         btnInsert.addClickHandler(new ClickHandler() {  
  188.             public void onClick(ClickEvent event) {  
  189.                 // Check empty  
  190.                 if (txtBoxSubject.getText().length() == 0  
  191.                         || txtAreaDetail.getText().length() == 0)  
  192.                     return;  
  193.                 // Check exist  
  194.                 if (subjectList.contains(txtBoxSubject.getText()) == true  
  195.                         && isNew) {  
  196.                     return;  
  197.                 }  
  198.   
  199.                 insert(index, txtBoxSubject.getText(), txtAreaDetail.getText(),  
  200.                         mapIntToStr.get(listBox.getSelectedIndex()));  
  201.                 dialog.hide();  
  202.             }  
  203.   
  204.         });  
  205.   
  206.         // Add ClickHandler to Close button  
  207.         btnClose.addClickHandler(new ClickHandler() {  
  208.             public void onClick(ClickEvent event) {  
  209.                 dialog.hide();  
  210.             }  
  211.         });  
  212.   
  213.         // Assemble dialog panel.  
  214.         itemPanel.setWidth("100%");  
  215.         itemPanel.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE);  
  216.         itemPanel.add(listBox);  
  217.         itemPanel.add(btnInsert);  
  218.         itemPanel.add(btnClose);  
  219.         dialogPanel.add(txtBoxSubject);  
  220.         dialogPanel.add(txtAreaDetail);  
  221.         dialogPanel.add(itemPanel);  
  222.   
  223.         // Associate the dialog with the panel.  
  224.         dialog.setWidget(dialogPanel);  
  225.   
  226.         // Show dialog.  
  227.         dialog.center();  
  228.     }  
  229.   
  230.     private void insert(final int index, final String subject,  
  231.             final String detail, final String progress) {  
  232.         if (index == -1) {  
  233.             // new idea  
  234.             dbWorker.save(subject, detail, progress,  
  235.                     new AsyncCallback() {  
  236.   
  237.                         @Override  
  238.                         public void onFailure(Throwable caught) {  
  239.                             // TODO Auto-generated method stub  
  240.                         }  
  241.   
  242.                         @Override  
  243.                         public void onSuccess(String idea) {  
  244.                             if (idea.length() != 0) {  
  245.                                 String[] parts = idea.split("\\|");  
  246.                                 insertIdeaIntoTable(index, parts[0], parts[1],  
  247.                                         parts[2], parts[3],  
  248.                                         parts[4] == "null" ? "" : parts[4]);  
  249.                             }  
  250.                         }  
  251.   
  252.                     });  
  253.         } else {  
  254.             dbWorker.update(table.getText(index, COL_ID), subject, detail,  
  255.                     progress, new AsyncCallback() {  
  256.   
  257.                         @Override  
  258.                         public void onFailure(Throwable caught) {  
  259.                             // TODO Auto-generated method stub  
  260.   
  261.                         }  
  262.   
  263.                         @Override  
  264.                         public void onSuccess(String idea) {  
  265.                             if (idea.length() != 0) {  
  266.                                 String[] parts = idea.split("\\|");  
  267.                                 insertIdeaIntoTable(index, parts[0], parts[1],  
  268.                                         parts[2], parts[3],  
  269.                                         parts[4].compareTo("null")==0 ? "" : parts[4]);  
  270.                             }  
  271.                         }  
  272.   
  273.                     });  
  274.         }  
  275.     }  
  276.   
  277.     private void insertIdeaIntoTable(int index, final String id,  
  278.             final String subject, String detail, String progress, String date) {  
  279.         //  
  280.         if (index == -1) {  
  281.             index = table.getRowCount();  
  282.             subjectList.add(subject);  
  283.         } else {  
  284.             subjectList.set(index - 1, subject);  
  285.         }  
  286.   
  287.         HorizontalPanel panel = new HorizontalPanel();  
  288.         Button btnUpdate = new Button("Update");  
  289.         Button btnRemove = new Button("Remove");  
  290.   
  291.         // Add handler to buttons  
  292.         btnUpdate.addClickHandler(new ClickHandler() {  
  293.   
  294.             @Override  
  295.             public void onClick(ClickEvent event) {  
  296.                 int i = subjectList.indexOf(subject);  
  297.                 showIdeaEditDialog(false, i + 1);  
  298.             }  
  299.   
  300.         });  
  301.   
  302.         btnRemove.addClickHandler(new ClickHandler() {  
  303.   
  304.             @Override  
  305.             public void onClick(ClickEvent event) {  
  306.                 dbWorker.delete(Long.valueOf(id), new AsyncCallback() {  
  307.   
  308.                     @Override  
  309.                     public void onFailure(Throwable caught) {  
  310.                         // TODO Auto-generated method stub  
  311.   
  312.                     }  
  313.   
  314.                     @Override  
  315.                     public void onSuccess(Boolean result) {  
  316.                         int i = subjectList.indexOf(subject);  
  317.                         table.removeRow(i + 1);  
  318.                         subjectList.remove(subject);  
  319.   
  320.                     }  
  321.                 });  
  322.   
  323.             }  
  324.   
  325.         });  
  326.   
  327.         panel.add(btnUpdate);  
  328.         panel.add(btnRemove);  
  329.   
  330.         table.setWidget(index, COL_OPERATION, panel);  
  331.         table.setText(index, COL_ID, id);  
  332.         table.setText(index, COL_SUBJECT, subject);  
  333.         table.setText(index, COL_DETAIL, detail);  
  334.         table.setText(index, COL_PROGRESS, progress);  
  335.         if (progress.compareTo("100%") == 0 && table.getText(index, COL_TIME).length() == 0) {  
  336.             table.setText(index, COL_TIME, date);  
  337.         }  
  338.   
  339.     }  
  340. }  


Well done,来运行一下看效果吧。点击Add弹出对话框,填入新的idea,这样在table中就出现了这个idea,同时,会把idea插入服务器端的数据库。重新刷新一下界面,会再次从数据库读取信息,显示在table中。
演示地址 :http://kylewuidea.appspot.com

总结

好了,就写到这里了,稍微回顾一下,在熟悉了Google Web Toolkit 和 Google App Engine 后,我们先制作了界面,然后实现了服务器端的数据存储,今天把前后台结合起来。难度很低,因为是一个入门教程,也是自我学习的一个记录。还有很多东西没有 去实现,比如现在任何人都可以Add Idea ( 这个其实只要在server端判断一下user就可以了 ),对重复的idea没有进行检查,样式上几乎没做任何操作……
希望诸位同学多多指导,大家都来做些好的应用。

没有评论:

发表评论