周一的專(zhuān)欄

          Java架構(gòu)專(zhuān)題

          Google App Engine for Java: 第 1 部分:運(yùn)轉(zhuǎn)起來(lái)!

          Google App Engine 曾經(jīng)一度是 Python 開(kāi)發(fā)人員 的專(zhuān)利。那是一段黑暗的歲月。Google Inc. 在 2009 年 4 月向 Java™ 開(kāi)發(fā)人員開(kāi)放了其云計(jì)算平臺(tái)。在這個(gè)共分三部分的系列文章中,Java 技術(shù)作家兼培訓(xùn)師 Rick Hightower 將帶領(lǐng)您了解這個(gè)可靠、健壯、有趣的平臺(tái),并將它用于基于 Java 的開(kāi)發(fā)。在本文中,您將了解到為什么 Google App Engine for Java 將成為您構(gòu)建高度可伸縮的殺手級(jí)應(yīng)用程序的開(kāi)發(fā)平臺(tái),然后開(kāi)始使用 Google Plugin for Eclipse 構(gòu)建兩個(gè)示例應(yīng)用程序:一個(gè)基于 Google Web Toolkit (GWT),另一個(gè)基于 Java Servlet API。您將了解到 Google App Engine for Java 帶來(lái)的巨大改變,包括從頭構(gòu)建應(yīng)用程序以及將它部署到高達(dá) 5 百萬(wàn)個(gè)視圖。(這僅僅是免費(fèi)版提供的功能)。

          頭腦里出現(xiàn)的想法就好像是被蚊蟲(chóng)叮了一樣:您需要抓癢癢,這樣做才感覺(jué)舒服一些。作為軟件開(kāi)發(fā)人員,我們花了大量時(shí)間來(lái)為各種應(yīng)用程序捕捉想法。很有趣,不是嗎?困難的部分在于想出如何使一個(gè)軟件產(chǎn)品獲得成功。需要構(gòu)想出一些東西并隨后 實(shí)現(xiàn)它。考慮其他的問(wèn)題(即沒(méi)有被抓過(guò)的癢處)只會(huì)讓人灰心。

          許多應(yīng)用程序從未獲得進(jìn)展的一個(gè)原因就是無(wú)法滿足對(duì)基礎(chǔ)設(shè)施的需求。一個(gè)得到良好維護(hù)的基礎(chǔ)設(shè)施常常需要一個(gè)由系統(tǒng)管理員、DBA 和網(wǎng)絡(luò)工程師組成的團(tuán)隊(duì),到目前為止,這一直是企業(yè)獲得成功的主因。即使雇用第三方來(lái)托管您的應(yīng)用程序也絕不簡(jiǎn)單:如果應(yīng)用程序大受歡迎并且突然之間獲得很高的點(diǎn)擊率,會(huì)發(fā)生什么?所謂的 Slashdot 效應(yīng) 可以幫助獲得一個(gè)好的想法,僅僅因?yàn)楹茈y預(yù)測(cè)加載峰值。


          但是,眾所周知,事物是不斷變化的。Web 服務(wù)的基礎(chǔ)在不斷演變,如今它為我們帶來(lái)了許多新方式,通過(guò)云計(jì)算和強(qiáng)大的平臺(tái)即服務(wù)/PAAS 更輕松地構(gòu)建、部署和發(fā)布應(yīng)用程序。現(xiàn)在,在編寫(xiě)下一個(gè) Twitter 并將其部署到云平臺(tái)上時(shí),它將不斷擴(kuò)展。哇,感覺(jué)很棒!

          在這份共分三部分的系列文章中,您將了解到為什么云計(jì)算/PAAS 對(duì)于軟件開(kāi)發(fā)來(lái)說(shuō)是如此重要的一個(gè)演變,同時(shí)開(kāi)始使用一種令人振奮的新平臺(tái)進(jìn)行 Java 開(kāi)發(fā):Google App Engine for Java,目前可以使用它的預(yù)覽版。我將首先對(duì) App Engine for Java 進(jìn)行概述,包括它所提供的應(yīng)用程序服務(wù)的類(lèi)型。之后將直接查看第一個(gè)應(yīng)用程序示例(共兩個(gè)),它使用 App Engine for Java Google Plugin for Eclipse。第一個(gè)應(yīng)用程序示例將利用 App Engine for Java 對(duì) Java Servlet API 的支持,第二個(gè)示例將利用對(duì) GWT 的支持。在 第 2 部分 中,您將利用 App Engine for Java 對(duì) servlets 和 GWT 提供的支持創(chuàng)建一個(gè)小型的聯(lián)系人管理應(yīng)用程序。在第 3 部分中,將使用自己構(gòu)建的應(yīng)用程序來(lái)利用 App Engine for Java 的基于 Java 的持久性支持,這種支持的基礎(chǔ)是 Java Data Objects (JDO) 和 Java Persistence API (JPA)。

          好的,不說(shuō)廢話了:讓我們開(kāi)始吧!


          關(guān)于 Google App Engine for Java

          Google(同時(shí)也是一些搜索引擎的創(chuàng)建者)于 2008 年 4 月首度發(fā)布了 Google App Engine。令許多 Java 開(kāi)發(fā)人員失望的是,初始版完全只服務(wù)于 Python 程序員 — 那些認(rèn)為應(yīng)該大塊使用空白的人!(我曾經(jīng)撰寫(xiě)過(guò)一本有關(guān) Python 的書(shū),因此我想我應(yīng)該知道)。Google 響應(yīng)了用戶的普遍要求,于 2009 年 4 月發(fā)布了 Google App Engine for Java。

          Google App Engine for Java 為企業(yè) Java 開(kāi)發(fā)提供了一個(gè)端到端解決方案:一個(gè)易于使用的基于瀏覽器的 Ajax GUI、Eclipse 工具支持以及后端的 Google App Engine。易于使用和工具支持是 Google App Engine for Java 優(yōu)于其他云計(jì)算解決方案的兩大優(yōu)勢(shì)。

          App Engine for Java 中的應(yīng)用程序開(kāi)發(fā)意味著使用 Google 的資源存儲(chǔ)和檢索 Java 對(duì)象。數(shù)據(jù)存儲(chǔ)的基礎(chǔ)是 BigTable,但是使用的是 JDO 和 JPA 接口,這些接口允許您編寫(xiě)沒(méi)有直接綁定到 BigTable 的代碼。事實(shí)上,Google 為許多 API 提供了基于標(biāo)準(zhǔn)的支持,這樣就可以編寫(xiě)沒(méi)有全部綁定到 App Engine for Java 的代碼。

          App Engine for Java 依賴(lài)以下標(biāo)準(zhǔn) Java API:

          • java.net.URL,檢索服務(wù)(通過(guò)使用 HTTP 和 HTTPS 協(xié)議與其他主機(jī)通信)
          • JavaMail,發(fā)送郵件消息
          • 一個(gè)通向 Memcache 的 JCache (JSR 107) 接口,提供快速、臨時(shí)的分布式存儲(chǔ),用于緩存查詢(xún)和計(jì)算

          此外,App Engine for Java 為以下應(yīng)用程序服務(wù)提供了支持:

          • 用戶身份驗(yàn)證和授權(quán)
          • CRON
          • 數(shù)據(jù)導(dǎo)入/導(dǎo)出
          • 訪問(wèn)防火墻數(shù)據(jù)

          對(duì)于將數(shù)據(jù)從其他來(lái)源移動(dòng)到您的 App Engine for Java 應(yīng)用程序,數(shù)據(jù)導(dǎo)入/導(dǎo)出十分重要。這也不需要綁定到 App Engine for Java。Google 的 CRON 支持基于對(duì)某個(gè)調(diào)度的內(nèi)部 URL 命中率,從而使它成為不需要綁定 App Engine for Java 的出色服務(wù)。用戶身份驗(yàn)證和授權(quán)機(jī)制 特定于 App Engine for Java 的,但是您可以編寫(xiě)一個(gè) ServletFilter、aspect 或 Spring Security 插件,以便最小化這種緊密耦合。

          創(chuàng)建 App Engine for Java 應(yīng)用程序

          如果您已經(jīng)閱讀了前面的內(nèi)容,那么已經(jīng)準(zhǔn)備好開(kāi)始構(gòu)建第一個(gè) App Engine for Java 應(yīng)用程序。首先需要 安裝 Google Plugin for Eclipse for App Engine for Java;安裝之后,您就有了好的起點(diǎn)。

          打開(kāi) Eclipse IDE,您將看到在 Eclipse IDE 中,Printer 按鈕旁邊出現(xiàn)了三個(gè)新按鈕:一個(gè) G 顯示在藍(lán)色小球中,另一個(gè) G 顯示在紅色工具箱中,還有一個(gè) App Engine for Java 迷你噴氣式飛機(jī),如圖 1 所示:


          圖 1. Eclipse IDE 中的新按鈕
          圖 1. Eclipse IDE 中的新按鈕

          下面列出了這些按鈕的功能:

          • 藍(lán)色小球讓您能夠訪問(wèn) App Engine for Java 項(xiàng)目創(chuàng)建向?qū)А?
          • 紅色工具箱讓您編譯一個(gè) GWT 項(xiàng)目。
          • 迷你噴氣式飛機(jī)圖標(biāo)讓您能夠部署一個(gè) App Engine 項(xiàng)目。

          您將使用項(xiàng)目創(chuàng)建向?qū)?chuàng)建兩個(gè)新項(xiàng)目:一個(gè)基于 servlets,另一個(gè)使用 GWT 構(gòu)建。將使用工具箱功能編譯 GWT 項(xiàng)目。當(dāng)您準(zhǔn)備好部署 App Engine 項(xiàng)目時(shí),將啟動(dòng)迷你噴氣式分機(jī),激活項(xiàng)目。

          首先創(chuàng)建一個(gè) App Engine for Java 項(xiàng)目。第一步,單擊藍(lán)色小球以訪問(wèn)項(xiàng)目創(chuàng)建向?qū)АH缓笫褂妹Q(chēng)為 gaej.example 的包創(chuàng)建名為 SimpleServletApp 的應(yīng)用程序,如圖 2 所示:


          圖 2. 開(kāi)始一個(gè)新項(xiàng)目
          圖 2. 開(kāi)始一個(gè)新項(xiàng)目

          注意,對(duì)于這第一個(gè)簡(jiǎn)單示例,沒(méi)有選擇 GWT 支持。完成了這一步后,項(xiàng)目創(chuàng)建向?qū)?chuàng)建一個(gè)簡(jiǎn)單的基于 servlet 的應(yīng)用程序,提供了一個(gè) Hello World 類(lèi)型的 servlet。圖 3 展示了這個(gè)項(xiàng)目的屏幕截圖。


          圖 3. SimpleServletApp 項(xiàng)目
          圖 3. SimpleServletApp 項(xiàng)目

          注意,這個(gè)新的基于 servlet 的項(xiàng)目自動(dòng)包含了 JAR 文件:

          • datanucleus-*.jar:用于使用標(biāo)準(zhǔn) JDO 或低級(jí) DataNucleus API訪問(wèn) App Engine for Java 數(shù)據(jù)庫(kù)
          • appengine-api-sdk.1.2.0.jar:用于使用非標(biāo)準(zhǔn) App Engine for Java 應(yīng)用程序服務(wù),比如 App Engine for Java Security
          • geronimo-*.jar:用于使用標(biāo)準(zhǔn) Java API,比如 Java Transaction Management API (JTA) 和 JPA
          • jdo2-api-2.3-SNAPSHOT.jar:用于使用 JDO API

          在本系列 第 2 部分 中,您將了解到如何使用 App Engine for Java 的持久化 API 和 App Engine for Java 的一些應(yīng)用程序服務(wù)。

          還需注意用于為 Google App Engine 配置運(yùn)行時(shí)容器的文件,名為 appengine.xml。在本例中,appengine.xml 用于配置 logging.properties 文件,以使用 App Engine for Java 完成登錄。

          App Engine for Java servlet 應(yīng)用程序初探

          在項(xiàng)目創(chuàng)建向?qū)е型瓿伤信渲煤螅珹pp Engine for Java 將向您顯示一個(gè) Hello World 風(fēng)格的 servlet 應(yīng)用程序的骨架。查看代碼并看看如何使用 App Engine for Java Eclipse 工具運(yùn)行應(yīng)用程序。該應(yīng)用程序的主要入口點(diǎn)為 SimpleServletAppServlet,如清單 1 所示:


          清單 1. SimpleServletAppServlet
                      package gaej.example;
                      import java.io.IOException;
                      import javax.servlet.http.*;
                      @SuppressWarnings("serial")
                      public class SimpleServletAppServlet extends HttpServlet {
                      public void doGet(HttpServletRequest req, HttpServletResponse resp)
                      throws IOException {
                      resp.setContentType("text/plain");
                      resp.getWriter().println("Hello, world");
                      }
                      }
                      

          servlet 在 web.xml 中的 URI /simpleservletapp 下完成映射,如清單 2 所示:


          清單 2. web.xml
                      <?xml version="1.0" encoding="utf-8"?>
                      <!DOCTYPE web-app PUBLIC
                      "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
                      "http://java.sun.com/dtd/web-app_2_3.dtd">
                      <web-app xmlns="http://java.sun.com/xml/ns/javaee" version="2.5">
                      <servlet>
                      <servlet-name>simpleservletapp</servlet-name>
                      <servlet-class>gaej.example.SimpleServletAppServlet</servlet-class>
                      </servlet>
                      <servlet-mapping>
                      <servlet-name>simpleservletapp</servlet-name>
                      <url-pattern>/simpleservletapp</url-pattern>
                      </servlet-mapping>
                      <welcome-file-list>
                      <welcome-file>index.html</welcome-file>
                      </welcome-file-list>
                      </web-app>
                      

          項(xiàng)目創(chuàng)建還提供了一個(gè) index.html 文件,包含了連接新 servlet 的鏈接,如清單 3 所示:


          清單 3. 項(xiàng)目創(chuàng)建向?qū)傻?index.html
                      <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
                      <!-- The HTML 4.01 Transitional DOCTYPE declaration-->
                      <!-- above set at the top of the file will set     -->
                      <!-- the browser's rendering engine into           -->
                      <!-- "Quirks Mode". Replacing this declaration     -->
                      <!-- with a "Standards Mode" doctype is supported, -->
                      <!-- but may lead to some differences in layout.   -->
                      <html>
                      <head>
                      <meta http-equiv="content-type" content="text/html; charset=UTF-8">
                      <!--                                           -->
                      <!-- Any title is fine                         -->
                      <!--                                           -->
                      <title>Hello App Engine</title>
                      </head>
                      <!--                                           -->
                      <!-- The body can have arbitrary html, or      -->
                      <!-- you can leave the body empty if you want  -->
                      <!-- to create a completely dynamic UI.        -->
                      <!--                                           -->
                      <body>
                      <h1>Hello App Engine!</h1>
                      <table>
                      <tr>
                      <td colspan="2" style="font-weight:bold;">Available Servlets:</td>
                      </tr>
                      <tr>
                      <td><a href="simpleservletapp"/>SimpleServletAppServlet</td>
                      </tr>
                      </table>
                      </body>
                      </html>
                      


          您現(xiàn)在已經(jīng)使用了一些 Java API 構(gòu)建了一個(gè)簡(jiǎn)單的 servlet 應(yīng)用程序。并且重點(diǎn)在于:App Engine for Java 使用標(biāo)準(zhǔn) Java API 封裝 App Engine 功能,使 App Engine 能夠支持面向 Java 平臺(tái)的豐富框架。

          部署應(yīng)用程序

          要使用 App Engine for Java Eclipse 工具運(yùn)行我們的基于 servlet 的應(yīng)用程序,首先右鍵單擊該項(xiàng)目并選擇 Run As 菜單,然后選擇旁邊有一個(gè)藍(lán)色小球的 “Web Application”,如圖 4 所示:


          圖 4. 運(yùn)行 App Engine for Java 部署服務(wù)器
          圖 4. 運(yùn)行 App Engine for Java 部署服務(wù)器

          現(xiàn)在您應(yīng)當(dāng)能夠在瀏覽器中導(dǎo)航到 http://localhost:8080/simpleservletapp 并看到應(yīng)用程序顯示出 Hello World 消息。


          創(chuàng)建 App Engine for Java/GWT 應(yīng)用程序

          您已經(jīng)了解了一個(gè)簡(jiǎn)單 App Engine for Java servlet 應(yīng)用程序的工作原理,那么接下來(lái)讓我們探討一下面向 GWT 應(yīng)用程序的 App Engine for Java Eclipse 工具。首先單擊 Eclipse IDE 工具欄中的藍(lán)色小球來(lái)激活 Google 項(xiàng)目創(chuàng)建向?qū)А_@一次,選擇 GWT 支持,如圖 5 所示:


          圖 5. 使用 App Engine for Java 項(xiàng)目創(chuàng)建向?qū)?chuàng)建一個(gè)簡(jiǎn)單的 GWT 應(yīng)用程序
          圖 5. 使用 App Engine for Java 項(xiàng)目創(chuàng)建向?qū)?chuàng)建一個(gè)簡(jiǎn)單的 GWT 應(yīng)用程序

          如圖 6 所示,與簡(jiǎn)單的基于 servlet 的應(yīng)用程序相比,App Engine for Java 為 GWT 應(yīng)用程序提供了更多的代碼工件。示例應(yīng)用程序是一個(gè)在 GWT 執(zhí)行的 GUI,它可以與一個(gè)問(wèn)候(greeting)服務(wù)應(yīng)用程序通信。



          圖 6. 為 GWT 應(yīng)用程序提供的代碼工件
          圖 6. 為 GWT 應(yīng)用程序提供的代碼工件

          為 GWT 應(yīng)用程序提供的一個(gè)額外 JAR 對(duì)于基于 servlet 的應(yīng)用程序并不是必須的,這個(gè) JAR 文件就是 gwt-servlet.jar。

          其他工件包括:

          • src/gaej/example:SimpleGWTApp.gwt.xml:GWT 模塊描述符
          • src/gaej.example.server:GreetingServiceImpl.java:?jiǎn)柡蚍?wù)的實(shí)現(xiàn)
          • src/gaej.example.client:GreetingService.java:?jiǎn)柡蚍?wù)的同步 API
          • src/gaej.example.client:GreetingServiceAsync.java:?jiǎn)柡蚍?wù)的異步 API
          • src/gaej.example.client:SimpleGWTApp.java:構(gòu)建啟動(dòng) GUI 的主要入口點(diǎn)
          • war/WEB-INF:web.xml:配置 GreetingServiceImpl 的部署描述符
          • war:SimpleGWTApp.html:顯示 GWT GUI 的 HTML 頁(yè)面
          • war:SimpleGWTApp.css:GWT GUI 的樣式表

          在深入研究應(yīng)用程序的架構(gòu)和源代碼之前,看看運(yùn)行它的時(shí)候會(huì)發(fā)生什么狀況。要運(yùn)行應(yīng)用程序,單擊工具欄中的紅色工具箱,然后單擊 Compile 按鈕。現(xiàn)在右鍵單擊項(xiàng)目并像剛才一樣選擇 Run As—> Web Application 菜單項(xiàng)。這一次,由于您處理的是一個(gè) GWT 應(yīng)用程序,將顯示一個(gè) GWT Hosted Mode Console 和瀏覽器。繼續(xù)并使用 Web 應(yīng)用程序輸入您的名字并觀察響應(yīng)。我收到如圖 7 所示的響應(yīng):


          圖 7. 運(yùn)行樣例 GWT 應(yīng)用程序
          圖 7. 運(yùn)行樣例 GWT 應(yīng)用程序

          在下一小節(jié)中,我將帶領(lǐng)您遍歷樣例 GWT 應(yīng)用程序。如果希望了解更多關(guān)于 GWT 的信息(或者獲得 GWT 教程),請(qǐng)參見(jiàn) 參考資料

          詳細(xì)了解 GWT 應(yīng)用程序

          根據(jù)已經(jīng)提供的配置,Eclipse 的 GWT 工具創(chuàng)建了一個(gè)啟動(dòng)應(yīng)用程序,包含一個(gè) HTML 前端(SimpleGWTApp.html,如 清單 10 所示),可以加載 simplegwtapp.js 和 simplegwtapp.nocache.js。這是由 GWT 從 Java 代碼中生成的 JavaScript 代碼;也就是說(shuō),位于 gaej.example.client 包中的 src 目錄下的代碼(參見(jiàn)清單 678)。

          創(chuàng)建 GUI 的主要入口點(diǎn)是 gaej.example.client.SimpleGWTApp,如 清單 8 所示。該類(lèi)創(chuàng)建 GWT GUI 元素并將它們與 SimpleGWTApp.html 中的 HTML DOM 元素關(guān)聯(lián)起來(lái)(參見(jiàn) 清單 10)。SimpleGWTApp.html 定義了兩個(gè) DOM 元素,分別命名為 nameFieldContainersendButtonContainer(表中的兩列)。SimpleGWTApp 類(lèi)使用 RootPanel.get("nameFieldContainer") 訪問(wèn)與這些 DOM 元素關(guān)聯(lián)的面板并使用 GUI 元素替換它們。SimpleGWTApp 類(lèi)隨后定義一個(gè)文本框和按鈕,可以使用它們輸入某人的名字并發(fā)送一句問(wèn)候語(yǔ)(參見(jiàn) 清單 10)。

          GWT 知道 SimpleGWTApp 類(lèi)是應(yīng)用程序的主要入口點(diǎn),因?yàn)?SimpleGWTApp.gwt.xml 使用入口點(diǎn)元素進(jìn)行了指定。

          SimpleGWTApp 封裝了名為 sendButton 的按鈕,這樣當(dāng)它被單擊時(shí),SimpleGWTApp 將對(duì) GreetingService 調(diào)用 greetServer 方法。GreetingService 接口在 src/gaej.example.client.GreetingService.java 中被定義(參見(jiàn) 清單 6)。

          由于 Ajax 天生就具有異步性,因此 GWT 定義了一個(gè)異步接口來(lái)訪問(wèn)遠(yuǎn)程服務(wù)。SimpleGWTApp 使用 src/gaej.example.client.GreetingServiceAsync.java 中定義的異步接口(參見(jiàn) 清單 7)。GreetingServiceImpl(src/gaej.example.server.GreetingServiceImpl.java)實(shí)現(xiàn)了 GreetingService 中定義的 greetServer方法(參見(jiàn) 清單 5)。GreetingServiceImpl.greetServer 方法返回一條問(wèn)候消息 String,SimpleGWTApp 用它在所創(chuàng)建的對(duì)話框中顯示問(wèn)候消息。

          GWT 模塊描述符聲明了 GUI 應(yīng)用程序的主要入口點(diǎn),即 gaej.example.client.SimpleGWTApp,如清單 4 所示:


          清單 4. GWT 模塊描述符(src/gaej/example/SimpleGWTApp.gwt.xml)
                      <?xml version="1.0" encoding="UTF-8"?>
                      <!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit 1.6.4//EN"
                      "http://google-web-toolkit.googlecode.com/svn/tags/1.6.4/
                      distro-source/core/src/gwt-module.dtd">
                      <module rename-to='simplegwtapp'>
                      <!-- Inherit the core Web Toolkit stuff.                        -->
                      <inherits name='com.google.gwt.user.User'/>
                      <!-- Inherit the default GWT style sheet.  You can change       -->
                      <!-- the theme of your GWT application by uncommenting          -->
                      <!-- any one of the following lines.                            -->
                      <inherits name='com.google.gwt.user.theme.standard.Standard'/>
                      <!-- <inherits name='com.google.gwt.user.theme.chrome.Chrome'/> -->
                      <!-- <inherits name='com.google.gwt.user.theme.dark.Dark'/>     -->
                      <!-- Other module inherits                                      -->
                      <!-- Specify the app entry point class.                         -->
                      <entry-point class='gaej.example.client.SimpleGWTApp'/>
                      </module>
                      

          GreetingServiceImpl 是問(wèn)候服務(wù)應(yīng)用程序的實(shí)際實(shí)現(xiàn),如清單 5 所示。它運(yùn)行在服務(wù)器端,并且客戶機(jī)代碼通過(guò)一個(gè)遠(yuǎn)程過(guò)程調(diào)用來(lái)調(diào)用它。


          清單 5. greeting-service 應(yīng)用程序的實(shí)現(xiàn)(src/gaej.example.server.GreetingServiceImpl.java)
                      package gaej.example.server;
                      import gaej.example.client.GreetingService;
                      import com.google.gwt.user.server.rpc.RemoteServiceServlet;
                      /**
                      * The server side implementation of the RPC service.
                      */
                      @SuppressWarnings("serial")
                      public class GreetingServiceImpl extends RemoteServiceServlet implements
                      GreetingService {
                      public String greetServer(String input) {
                      String serverInfo = getServletContext().getServerInfo();
                      String userAgent = getThreadLocalRequest().getHeader("User-Agent");
                      return "Hello, " + input + "!<br><br>I am running " + serverInfo
                      + ".<br><br>It looks like you are using:<br>" + userAgent;
                      }
                      }
                      

          如清單 6 所示,GreetingService 是客戶機(jī)代碼使用的遠(yuǎn)程過(guò)程調(diào)用的接口:


          清單 6. 同步 API (src/gaej.example.client.GreetingService.java)
                      package gaej.example.client;
                      import com.google.gwt.user.client.rpc.RemoteService;
                      import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;
                      /**
                      * The client side stub for the RPC service.
                      */
                      @RemoteServiceRelativePath("greet")
                      public interface GreetingService extends RemoteService {
                      String greetServer(String name);
                      }
                      

          GreetingServiceAsync 是客戶機(jī)代碼將使用的實(shí)際接口,如清單 7 所示。每個(gè)方法都提供了一個(gè)回調(diào)對(duì)象,這樣就可以在完成遠(yuǎn)程過(guò)程調(diào)用后收到異步通知。至于內(nèi)部原理,GWT 使用了 Ajax。在客戶機(jī)上使用 Ajax 時(shí),最好不要阻塞客戶機(jī),這樣就不會(huì)阻塞異步調(diào)用。阻塞會(huì)違背使用 Ajax 的初衷:


          清單 7. 異步 API(src/gaej.example.client.GreetingServiceAsync.java)
                      package gaej.example.client;
                      import com.google.gwt.user.client.rpc.AsyncCallback;
                      /**
                      * The async counterpart of <code>GreetingService</code>.
                      */
                      public interface GreetingServiceAsync {
                      void greetServer(String input, AsyncCallback<String> callback);
                      }
                      

          SimpleGWTApp 中包含了最多的操作。它注冊(cè)了 GUI 事件,然后將 Ajax 請(qǐng)求發(fā)送到 GreetingService


          清單 8. 應(yīng)用程序的主入口點(diǎn)還構(gòu)建了啟動(dòng) GUI(src/gaej.example.client.SimpleGWTApp.java)
                      package gaej.example.client;
                      import com.google.gwt.core.client.EntryPoint;
                      import com.google.gwt.core.client.GWT;
                      import com.google.gwt.event.dom.client.ClickEvent;
                      import com.google.gwt.event.dom.client.ClickHandler;
                      import com.google.gwt.event.dom.client.KeyCodes;
                      import com.google.gwt.event.dom.client.KeyUpEvent;
                      import com.google.gwt.event.dom.client.KeyUpHandler;
                      import com.google.gwt.user.client.rpc.AsyncCallback;
                      import com.google.gwt.user.client.ui.Button;
                      import com.google.gwt.user.client.ui.DialogBox;
                      import com.google.gwt.user.client.ui.HTML;
                      import com.google.gwt.user.client.ui.Label;
                      import com.google.gwt.user.client.ui.RootPanel;
                      import com.google.gwt.user.client.ui.TextBox;
                      import com.google.gwt.user.client.ui.VerticalPanel;
                      /**
                      * Entry point classes define <code>onModuleLoad()</code>.
                      */
                      public class SimpleGWTApp implements EntryPoint {
                      /**
                      * The message displayed to the user when the server cannot be reached or
                      * returns an error.
                      */
                      private static final String SERVER_ERROR = "An error occurred while "
                      + "attempting to contact the server. Please check your network "
                      + "connection and try again.";
                      /**
                      * Create a remote service proxy to talk to the server-side Greeting service.
                      */
                      private final GreetingServiceAsync greetingService = GWT
                      .create(GreetingService.class);
                      /**
                      * This is the entry point method.
                      */
                      public void onModuleLoad() {
                      final Button sendButton = new Button("Send");
                      final TextBox nameField = new TextBox();
                      nameField.setText("GWT User");
                      // You can add style names to widgets
                      sendButton.addStyleName("sendButton");
                      // Add the nameField and sendButton to the RootPanel
                      // Use RootPanel.get() to get the entire body element
                      RootPanel.get("nameFieldContainer").add(nameField);
                      RootPanel.get("sendButtonContainer").add(sendButton);
                      // Focus the cursor on the name field when the app loads
                      nameField.setFocus(true);
                      nameField.selectAll();
                      // Create the popup dialog box
                      final DialogBox dialogBox = new DialogBox();
                      dialogBox.setText("Remote Procedure Call");
                      dialogBox.setAnimationEnabled(true);
                      final Button closeButton = new Button("Close");
                      // You can set the id of a widget by accessing its Element
                      closeButton.getElement().setId("closeButton");
                      final Label textToServerLabel = new Label();
                      final HTML serverResponseLabel = new HTML();
                      VerticalPanel dialogVPanel = new VerticalPanel();
                      dialogVPanel.addStyleName("dialogVPanel");
                      dialogVPanel.add(new HTML("<b>Sending name to the server:</b>"));
                      dialogVPanel.add(textToServerLabel);
                      dialogVPanel.add(new HTML("<br><b>Server replies:</b>"));
                      dialogVPanel.add(serverResponseLabel);
                      dialogVPanel.setHorizontalAlignment(VerticalPanel.ALIGN_RIGHT);
                      dialogVPanel.add(closeButton);
                      dialogBox.setWidget(dialogVPanel);
                      // Add a handler to close the DialogBox
                      closeButton.addClickHandler(new ClickHandler() {
                      public void onClick(ClickEvent event) {
                      dialogBox.hide();
                      sendButton.setEnabled(true);
                      sendButton.setFocus(true);
                      }
                      });
                      // Create a handler for the sendButton and nameField
                      class MyHandler implements ClickHandler, KeyUpHandler {
                      /**
                      * Fired when the user clicks on the sendButton.
                      */
                      public void onClick(ClickEvent event) {
                      sendNameToServer();
                      }
                      /**
                      * Fired when the user types in the nameField.
                      */
                      public void onKeyUp(KeyUpEvent event) {
                      if (event.getNativeKeyCode() == KeyCodes.KEY_ENTER) {
                      sendNameToServer();
                      }
                      }
                      /**
                      * Send the name from the nameField to the server and wait for a response.
                      */
                      private void sendNameToServer() {
                      sendButton.setEnabled(false);
                      String textToServer = nameField.getText();
                      textToServerLabel.setText(textToServer);
                      serverResponseLabel.setText("");
                      greetingService.greetServer(textToServer,
                      new AsyncCallback<String>() {
                      public void onFailure(Throwable caught) {
                      // Show the RPC error message to the user
                      dialogBox
                      .setText("Remote Procedure Call - Failure");
                      serverResponseLabel
                      .addStyleName("serverResponseLabelError");
                      serverResponseLabel.setHTML(SERVER_ERROR);
                      dialogBox.center();
                      closeButton.setFocus(true);
                      }
                      public void onSuccess(String result) {
                      dialogBox.setText("Remote Procedure Call");
                      serverResponseLabel
                      .removeStyleName("serverResponseLabelError");
                      serverResponseLabel.setHTML(result);
                      dialogBox.center();
                      closeButton.setFocus(true);
                      }
                      });
                      }
                      }
                      // Add a handler to send the name to the server
                      MyHandler handler = new MyHandler();
                      sendButton.addClickHandler(handler);
                      nameField.addKeyUpHandler(handler);
                      }
                      }
                      

          Web 部署描述符(web.xml,如清單 9 所示)將 GreetingService 映射為一個(gè)基于 servlet 的 Web 資源。它在 /simplegwtapp/greet 名稱(chēng)下映射 GreetingService servlet,這樣 SimpleGWTApp 就可以加載它并對(duì)它發(fā)起調(diào)用。Web 部署描述符還可以將 SimpleGWTApp.html 指定為應(yīng)用程序的歡迎頁(yè)面,這樣就會(huì)始終加載它。


          清單 9. 配置 GreetingServiceImpl (war/WEB-INF/web.xml) 的部署描述符
                      <?xml version="1.0" encoding="UTF-8"?>
                      <!DOCTYPE web-app
                      PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
                      "http://java.sun.com/dtd/web-app_2_3.dtd">
                      <web-app>
                      <!-- Default page to serve -->
                      <welcome-file-list>
                      <welcome-file>SimpleGWTApp.html</welcome-file>
                      </welcome-file-list>
                      <!-- Servlets -->
                      <servlet>
                      <servlet-name>greetServlet</servlet-name>
                      <servlet-class>gaej.example.server.GreetingServiceImpl</servlet-class>
                      </servlet>
                      <servlet-mapping>
                      <servlet-name>greetServlet</servlet-name>
                      <url-pattern>/simplegwtapp/greet</url-pattern>
                      </servlet-mapping>
                      </web-app>
                      

          HTML 前端為 SimpleGWTApp.html,如清單 10 所示。這是加載 simplegwtapp.js 和 simplegwtapp.nocache.js 的頁(yè)面,是由 GWT 從 Java 代碼中生成的 JavaScript 代碼。如前所述,這段代碼位于 gaej.example.client 包的 src 目錄中(來(lái)自清單 678)。


          清單 10. 顯示 GWT GUI (war/SimpleGWTApp.html) 的 HTML 頁(yè)面
                      <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
                      <!-- The HTML 4.01 Transitional DOCTYPE declaration-->
                      <!-- above set at the top of the file will set     -->
                      <!-- the browser's rendering engine into           -->
                      <!-- "Quirks Mode". Replacing this declaration     -->
                      <!-- with a "Standards Mode" doctype is supported, -->
                      <!-- but may lead to some differences in layout.   -->
                      <html>
                      <head>
                      <meta http-equiv="content-type" content="text/html; charset=UTF-8">
                      <!--                                                               -->
                      <!-- Consider inlining CSS to reduce the number of requested files -->
                      <!--                                                               -->
                      <link type="text/css" rel="stylesheet" href="SimpleGWTApp.css">
                      <!--                                           -->
                      <!-- Any title is fine                         -->
                      <!--                                           -->
                      <title>Web Application Starter Project</title>
                      <!--                                           -->
                      <!-- This script loads your compiled module.   -->
                      <!-- If you add any GWT meta tags, they must   -->
                      <!-- be added before this line.                -->
                      <!--                                           -->
                      <script type="text/javascript" language="javascript"
                      src="simplegwtapp/simplegwtapp.nocache.js"></script>
                      </head>
                      <!--                                           -->
                      <!-- The body can have arbitrary html, or      -->
                      <!-- you can leave the body empty if you want  -->
                      <!-- to create a completely dynamic UI.        -->
                      <!--                                           -->
                      <body>
                      <!-- OPTIONAL: include this if you want history support -->
                      <iframe src="javascript:''" id="__gwt_historyFrame" tabIndex='-1'
                      style="position:absolute;width:0;height:0;border:0"></iframe>
                      <h1>Web Application Starter Project</h1>
                      <table align="center">
                      <tr>
                      <td colspan="2" style="font-weight:bold;">Please enter your name:</td>
                      </tr>
                      <tr>
                      <td id="nameFieldContainer"></td>
                      <td id="sendButtonContainer"></td>
                      </tr>
                      </table>
                      </body>
                      </html>
                      

          使用 GWT,您可以通過(guò) CSS 控制應(yīng)用程序的觀感,如清單 11 所示:


          清單 11. GWT GUI (war/SimpleGWTApp.css) 的樣式表
                      /** Add css rules here for your application. */
                      /** Example rules used by the template application (remove for your app) */
                      h1 {
                      font-size: 2em;
                      font-weight: bold;
                      color: #777777;
                      margin: 40px 0px 70px;
                      text-align: center;
                      }
                      .sendButton {
                      display: block;
                      font-size: 16pt;
                      }
                      /** Most GWT widgets already have a style name defined */
                      .gwt-DialogBox {
                      width: 400px;
                      }
                      .dialogVPanel {
                      margin: 5px;
                      }
                      .serverResponseLabelError {
                      color: red;
                      }
                      /** Set ids using widget.getElement().setId("idOfElement") */
                      #closeButton {
                      margin: 15px 6px 6px;
                      }
                      

          部署到 Google App Engine

          創(chuàng)建了下一代殺手級(jí)應(yīng)用程序后(因?yàn)槲覀兇_實(shí)需要一個(gè)用戶友好的問(wèn)候應(yīng)用程序),您需要部署它。使用 Google App Engine 的重點(diǎn)就是可以將應(yīng)用程序部署到 Google 提供的可靠基礎(chǔ)設(shè)施中,使它更易于擴(kuò)展。Google App Engine 的設(shè)計(jì)初衷就是為構(gòu)建可伸縮應(yīng)用程序提供一個(gè)平臺(tái),可伸縮應(yīng)用程序就是指 “能夠在不觸動(dòng)基礎(chǔ)設(shè)施的情況下將用戶輕松增長(zhǎng)到數(shù)百萬(wàn)”(正如 App Engine 主頁(yè)中描述的那樣)。為使用這種基礎(chǔ)設(shè)施,您需要一個(gè) Google App Engine for Java 帳戶

          就像許多其他產(chǎn)品一樣,第一次體驗(yàn)總是免費(fèi)的。App Engine for Java 的免費(fèi)版提供了一個(gè)已部署的應(yīng)用程序,提供了足夠的 CPU、帶寬和存儲(chǔ)來(lái)為 500 萬(wàn)個(gè)頁(yè)面訪問(wèn)次數(shù)提供服務(wù)。超過(guò)使用次數(shù)開(kāi)始收費(fèi)(同樣需要注意,在撰寫(xiě)本文時(shí)可以獲得 App Engine for Java 平臺(tái)的預(yù)覽版)。

          獲得帳戶之后,您應(yīng)該可以在 App Engine for Java 站點(diǎn) 看到一個(gè)空的應(yīng)用程序列表。單擊 Create New Application 按鈕,應(yīng)當(dāng)出現(xiàn)一個(gè)如圖 8 所示的表單。輸入一個(gè)獨(dú)特的應(yīng)用程序名稱(chēng)和描述,之后您將看到一條顯示有應(yīng)用程序標(biāo)識(shí)符的確認(rèn)消息。

          該標(biāo)識(shí)符也位于應(yīng)用程序的 app.yaml 文件中。注意,該標(biāo)識(shí)符不可修改。如果對(duì)您的應(yīng)用程序使用 Google 身份驗(yàn)證,那么在訪問(wèn)應(yīng)用程序時(shí),“GAEj Article For Rick Part 1” 將顯示在 Sign In 頁(yè)面中。您將使用 gaejarticleforrick 和 App Engine for Java Eclipse 插件來(lái)將應(yīng)用程序部署到 Google App Engine。


          圖 8. 創(chuàng)建一個(gè)新的 App Engine for Java 應(yīng)用程序
          圖 8. 創(chuàng)建一個(gè)新的 App Engine for Java 應(yīng)用程序

          設(shè)置好應(yīng)用程序 ID 后,可以從 Eclipse 中部署您的應(yīng)用程序。首先,單擊看上去類(lèi)似 Google App Engine 徽標(biāo)的工具欄按鈕(顯示機(jī)翼和尾翼的噴氣式發(fā)動(dòng)機(jī)),如圖 9 所示:


          圖 9. App Engine for Java Eclipse 插件
          圖 9. App Engine for Java Eclipse 插件

          在單擊圖 10 所示的對(duì)話框中的 Deploy 之前,可能需要確保 App Engine for Java 項(xiàng)目被選中。將要求您輸入 Google 憑證,即您的電子郵件地址和用戶名。


          圖 10. 部署項(xiàng)目
          圖 10. 部署項(xiàng)目

          圖 10 中的對(duì)話框包含一個(gè) “App Engine Project setting” 鏈接。單擊此鏈接(也可以從項(xiàng)目設(shè)置文件訪問(wèn))并輸入應(yīng)用程序 ID(在本例中為 gaejarticleforrick),如圖 11 所示。填充好應(yīng)用程序 ID 后,單擊 OK,然后單擊 Deploy


          圖 11. Google App Engine 的項(xiàng)目設(shè)置
          圖 11. Google App Engine 的項(xiàng)目設(shè)置

          部署完應(yīng)用程序后,該應(yīng)用程序可以通過(guò) http://<application id>.appspot.com/ 訪問(wèn)。您還會(huì)看到應(yīng)用程序出現(xiàn)在 http://gaejarticleforrick.appspot.com/ 中。

          結(jié)束語(yǔ)

          Google App Engine for Java 系列的第一部分到此結(jié)束。至此,您已經(jīng)大致了解了 App Engine for Java 是什么,并通過(guò)使用 App Engine for Java Google Plugin for Eclipse 邁出了第一步。您創(chuàng)建了兩個(gè)啟動(dòng)應(yīng)用程序(一個(gè)基于 servlet,另一個(gè)基于 GWT)并隨后將 GWT 應(yīng)用程序部署到 Google App Engine 平臺(tái)。

          本文的示例到目前為止演示了可以更輕松地創(chuàng)建和部署基于 Java 的應(yīng)用程序的工具和功能 — 這些應(yīng)用程序有可能擴(kuò)展到 YouTube 或 Facebook 那樣的規(guī)模。在 第 2 部分 中,您將繼續(xù)為 Java 開(kāi)發(fā)人員介紹使用 App Engine for Java 的機(jī)會(huì)。從本文演示的示例應(yīng)用程序出發(fā),您將構(gòu)建一個(gè)定制的聯(lián)系人管理應(yīng)用程序。該應(yīng)用程序?qū)⒊蔀榈?3 部分的中心內(nèi)容,第 3 部分將進(jìn)一步了解 App Engine for Java 的數(shù)據(jù)存儲(chǔ)及其 GUI 前端。


          posted on 2009-09-22 19:51 周一 閱讀(298) 評(píng)論(0)  編輯  收藏


          只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


          網(wǎng)站導(dǎo)航:
           
          <2025年6月>
          25262728293031
          1234567
          891011121314
          15161718192021
          22232425262728
          293012345

          導(dǎo)航

          統(tǒng)計(jì)

          常用鏈接

          留言簿

          隨筆檔案

          搜索

          最新評(píng)論

          閱讀排行榜

          評(píng)論排行榜

          主站蜘蛛池模板: 华阴市| 英超| 呼伦贝尔市| 莱州市| 都昌县| 敦煌市| 兖州市| 彰武县| 西和县| 长丰县| 花垣县| 类乌齐县| 绥江县| 昌吉市| 满城县| 东辽县| 临颍县| 广水市| 东兰县| 旬邑县| 五莲县| 建昌县| 灵璧县| 兴和县| 苏尼特右旗| 盱眙县| 石嘴山市| 宕昌县| 邵武市| 普兰县| 衡山县| 安西县| 仁怀市| 淳安县| 无极县| 无为县| 团风县| 子长县| 肃宁县| 阜南县| 平江县|