一、 引言
如果你是一個Java軟件和
AJax開發者,那麼,Google Web Toolkit(GWT)應該已經引起你的關注。
Google公司已經於2006年5月在apache許可協議下發布了這種免費的開發工具包。GWT的設計目的是為了簡化用Java語言開發
AJax應用程序。Google初始發行的beta版本可以適用於Windows和Linux平台,並許諾稍後要發行一個Mac OS X版本。
本文將探討在Mac OS X上使用GWT和熟悉的Java工具,例如apache Ant,
Tomcat 5.0 servlet容器和IntelliJ IDEA集成開發環境開發一個簡單的
AJax應用程序的完整過程。
注 本文假定讀者具有一定的Java和Ant使用基礎。
二、 與GWT一起使用Ant 我下載的是GWT的Linux beta版本,並選用Java開發應用程序,然後使用一個Ant構建文件進行編譯,最後在一個
Tomcat 5.0實例上發布該應用程序。注意,這個Ant文件運行的是GWT Java-to-Javascript編譯器。其實,這個“編譯器”只是一個執行一個GWT Java類的命令行腳本,該Java類負責為應用程序編寫JavaScript。
使用GWT beta包括兩種開發方式:主機方式和web方式。
主機方式是使用一個嵌入式的GWT浏覽器和中間開發步驟;在這個方式中,你的編譯代碼繼續運行於一個Java虛擬機(JVM)中。然而,主機方式無法應用於我們這些使用Linux版本操作系統的Mac OS X用戶。只有Google發行了一個Mac OS X版本,我們才可以使用主機方式。
三、 不同風格的web開發 在創建遠程過程調用(RPC)服務的同時,本文將詳細討論一些典型的GWT開發者可能面對的
web開發有關的任務。RPC是一個軟件模型的一部分,主要為使用面向服務的架構(SOA)的應用程序而設計。這些開發任務包括:
· 使用一個構建文件(構建運行GWT編譯器,然後發布編譯器的輸出,並且把你的服務器端的Java類文件發布到一個servlet容器,例如
Tomcat,Jetty或
Resin)來自動化開發和發布步驟。
· 使用Firefox的DOM Inspector來觀察由該GWT應用程序生成的Html。
· 重新設置頁面中的各部件而不必存取內在的Html(既然你在使用GWT的Java API)。
· 確保HTML是有效的標記,例如,你的組織可能需要基於一個特別的XHtml文檔類型。
四、 服務功能 首先,我將簡短描述本文示例應用程序要創建的服務,設計這個示例是為了展示GWT使用的模型。
該應用程序在浏覽器中顯示一個表單,要求用戶輸入他們的姓名、年齡和原籍國家。當用戶通過點按按鈕提交表單時,該應用程序在一個文本域中顯示一個服務器響應,而不必初始化一個頁面刷新。圖1顯示了在Safari浏覽器中該應用程序看上去的樣子。
圖1.一個由GWT生成的簡單視圖
例如,當用戶保留一個文本框為空而點擊OK,Submit按鈕時將顯示出圖2所示結果。
圖2:該應用程序用紅色顯示一條錯誤消息
五、 巧妙的服務機制 在
AJax應用程序中使用RPC可以消除顯式地處理XMLHttpRequest和相關的服務器返回值的必要性,因為GWT對象能夠為你處理復雜任務。
你的應用程序定義的每個服務都要求實現兩個Java接口和一個Java類。為了編譯這些類,你必須確保gwt-user.jar庫位於你的classpath(由一個Ant文件入口負責實現這項任務)中。下列代碼示例展示了定義我們的服務的Java接口。
package com.parkerriver.gwt.testapp.clIEnt;
import com.google.gwt.user.clIEnt.rpc.RemoteService;
public interface ShowRespService extends RemoteService{
String displayResponse(String req);
}
這個服務接口要求擴展GWT接口RemoteService。它定義了單個方法displayResponse()。
另外,你還必須定義一個客戶端(或使用最終下載的Javascript代碼)用於調用這個服務方法的接口。當我顯示客戶端代碼時(請參考MyForm.Java),該GWT使用一個我描述的回調設計模式。
package com.parkerriver.gwt.testapp.clIEnt;
import com.google.gwt.user.clIEnt.rpc.AsyncCallback;
public interface ShowRespServiceAsync {
public void displayResponse(String s,AsyncCallback callback);
}
這個AsyncCallback對象負責(作為GWT API的一部分)為客戶端處理服務響應。
六、 一個Servlet 最後,你必須定義一個實現遠程服務接口的Java類。這個類將位於你的
AJax應用程序的服務器端。
package com.parkerriver.gwt.testapp.server;
import com.parkerriver.gwt.testapp.clIEnt.ShowRespService;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import Java.util.Date;
public class ShowRespServiceImpl extends RemoteServiceServlet
implements ShowRespService {
public String displayResponse(String req) {
if(req.length() < 1) {
throw new IllegalArgumentException(
"Blank submissions from the clIEnt are invalid.");
}
StringBuffer buf = new StringBuffer("Your submission: ");
Date date = new Date();
String serverInfo = this.getServletContext().getServerInfo();
buf.append(req);
buf.append("\n");
buf.append("Server response: ");
buf.append(date.toString());
buf.append("\n");
buf.append(serverInfo);
return buf.toString();
}
}
這個類必須繼承RemoteServiceServlet(這是本身繼承自Javax.servlet.http.HttpServlet的一個GWT API對象)。也就是說,這個類及其實現的接口必須被發布到你的servlet容器中。
七、 步驟 現在,既然定義了服務,那麼讓我們來回顧一個這個應用程序的目錄結構。GWT包括一個命令行腳本applicationCreator,它能夠為你生成一個框架性的工程目錄結構。在你解壓下載的GWT後,你會在頂級目錄下發現該applicationCreator。我使用下列命令行作為開始:
applicationCreator -out /Users/bruceperry/1gwt/secondapp/ com.parkerriver.gwt.testapp.clIEnt.MyForm
圖3展示了該目錄看上去的樣子。
圖3:一個GWT和IntelliJ工程目錄
applicationCreator生成./src目錄以及MyForm-compile和MyForm-shell腳本。我的Ant文件執行的是MyForm-compile;另一個腳本將在GWT模式下啟動主機方式。這個./src目錄中包括了嵌套的目錄以匹配你的包名,如圖4所示。
圖4.一個GWT應用程序的Java包和模塊
MyForm.gwt.XML文件是一個生成的配置文件,它其實是GWT調用的一個“模塊”。它指定描述你的應用程序“入口點”的Java類(這是一個類似於一個包含一個main()方法的Java類)。
<module>
<!—繼承核心Web Toolkit。-->
<inherits name='com.google.gwt.user.User'/>
<!--指定應用程序入口點類。-->
<entry-point class='com.parkerriver.gwt.testapp.clIEnt.MyForm'/>
</module>
其它的文件或目錄,包括./classes、./WEB-INF和./gwtproj.ipr,都是一個IntelliJ Web應用程序工程的必要組成部分;因此,你不必特別注意它們。
另外,直到生成你的應用程序代碼的GWT編譯器時,./www目錄才出現(除非你自己創建它)。我的工程使用了Ant文件gwtproj.XML,還有定義在gwtproj.propertIEs中的屬性。在我向你展示Ant構建文件前,我們先來看一下描述應用程序入口點的MyForm.Java類。
八、 入口點 這個MyForm.Java類實現了GWT API接口EntryPoint;因此,該類必須實現onModuleLoad()方法,當浏覽器加載你的
AJax應用程序時此方法為浏覽器的JavaScript引擎所調用。
也就是說,GWT編譯器把這個類編譯成Javascript代碼。MyForm.Java類為浏覽器視圖建立表單widget。該類還決定了點擊OK和Submit按鈕時用戶的響應。代碼中的注釋已經作了詳細的描述,所以在此不再多言。
注意,這個類中的大部分代碼是處理GWT API。有意思的是,如果你必須實現Javascript DOM編程(就象在showRpcStatus()方法中所展示的),那麼你可以用Java來實現com.google.gwt.user.clIEnt.DOM類。
九、 構建文件 下面是Ant構建文件的主要功能;這個構建文件:
1. 把Java文件編譯到工程目錄的./classes目錄下。
2. 執行GWT編譯腳本(在這個例子中是MyForm-compile)。
3. 把在./www目錄下生成的結果代碼移動到一個較大的已經發布到
Tomcat上的web應用程序。
4. 把編譯的Java servlet及相關接口(ShowRespService)復制到同一個web應用程序下。
注意,這裡的兩個目標:編譯Java類和初始化到JavaScript的轉換有可能使整個構建過程失敗,如果期間發生任何錯誤的話。
十、 Ant XML 下面是gwtpoj.propertIEs文件包含的內容(省略了其它的內容):
web.deploy.location=/users/bruceperry/parkerriver/gwt
web.classes.location=/users/bruceperry/parkerriver/WEB-INF/classes
下列XML描述了剛才的Ant文件的主要功能:
<?XML version="1.0" encoding="UTF-8"?>
<project name="gwtproj" default="all">
<property file="gwtproj.propertIEs"/>
<!—工程的頂級目錄與ant文件存在的位置-->
<dirname property="module.gwtproj.basedir" file="${ant.file}"/>
<!--目錄在頂級目錄內的./classes-->
<property name="gwtproj.output.dir" value=
"${module.gwtproj.basedir}/classes"/>
<!--這個目標調用MyForm-compile以創建./www目錄下的所有的內容-->
<target name="gwt-compile" depends=
"compile.production.classes"
description="use gwt's compiler">
<delete>
<fileset dir="${web.deploy.location}" includes="**/*"/>
</delete>
<exec executable=
"${module.gwtproj.basedir}/MyForm-compile"
failonerror="true"/>
<copy todir="${web.deploy.location}">
<fileset dir=
"${module.gwtproj.basedir}/www">
</fileset>
</copy>
</target>
<target name="compile.production.classes" description=
"Compile the gwtproj production classes">
<mkdir dir="${gwtproj.output.dir}"/>
<Javac destdir="${gwtproj.output.dir}" debug=
"on" failonerror="true" nowarn=
"off" memoryMaximumSize="128m" fork=
"true" executable="${module.jdk.home.gwtproj}/bin/Javac">
<classpath refid="gwtproj.module.classpath"/>
<src refid="gwtproj.module.sourcepath"/>
</Javac>
</target>
<!--把Java servlet類復制到web應用程序-->
<target name="deploy.classes" depends="gwt-compile"
description="copy classes to web directory">
<copy todir="${web.classes.location}">
<fileset dir="${gwtproj.output.dir}">
</fileset>
</copy>
</target>
<target name="all" depends="deploy.classes"
description="build all"/>
</project>
你可以從IDE(在IntelliJ中)中或在包含該構建文件的目錄下使用下列命令行來運行這個Ant文件:
ant -buildfile gwtproj.XML
大部分情況下,在修改應用程序和運行Ant後,你都可以通過重載浏覽器頁面而在浏覽器中看到這些變化。
十一、 安裝 注意,在安裝時,你必須把gwt-user.jar庫添加到你的web應用程序的/WEB-INF/lib目錄下。
我把創建的JAR文件,還有Javax包(gwt-user-deploy.jar)都添加到/WEB-INF/lib目錄下。這是因為
Tomcat不會加載單個的web應用程序的庫文件,如果它已經包含servlet API類的話。
十二、 難點 applicationCreator還會為你創建
AJax應用程序的HTML前端,在這個例子中是MyForm.Html。
如果你的應用程序的HTM必須滿足一種標准(例如XHTML Transitional 或Strict),那麼該怎麼辦呢?對於XHTML transitional,我首先把要求的DOCTYPE添加到MyForm.html的頂部以及該Html標簽的相關屬性中:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHtml 1.0 Transitional//EN"
"http://www.w3.org/TR/2000/REC-xhtml1-20000126/DTD/xHtml1-transitional.dtd">
<html XMLns="http://www.w3.org/1999/xHtml" XML:lang="en" lang="en">
然後,我把MyForm.Html上傳到WWW協會的HTM驗證程序(位於
http://validator.w3.org/)。
在運行該驗證程序後,我略微修改了一下該Html,例如適當關閉meta標簽並且把一個type="text/Javascript"添加到腳本標簽。
然而,如果你想滿足XHTML Strict標准,那麼還需要進行更為復雜的修改。例如,W3C的驗證程序將把iframe標簽顯示為“undefined element”,而這個標簽是GWT的歷史支持功能(提供與一個浏覽器back按鈕相同的功能)必需的。XHtml Strict中已經刪除了iframe元素。
這個對你來說可能不是個問題(可能會在GWT的未來版本中得到解決,還有任何其它明顯的問題);然而,你還可以實現其它可選策略,例如擴展GWT的類並創建你自己的兼容widget等。
十三、 位置對齊問題 一個在
web開發中普通存在的問題就是應用程序的可視化設計問題。工程的設計者可能想使頁面看上去如其在Adobe Illustrator創建一樣的效果,對不對?
盡管當你開發一個復雜的
AJax工程時你可能無法實現這種理想的視覺效果;但是,你至少可以使用Firefox的DOM Inspector來觀察你的Java類最終生成的Html。然後,再從這裡進行修改。
例如,轉到Firefox的“Tools=>DOM Inspector”菜單項(見圖5)。
圖5:使用DOM Inspector觀察後台實現內容。
上圖顯示了,你在Java代碼中使用的com.google.gwt.user.clIEnt.ui.Grid對象被實現為一個Html table標簽。這個table中的包含OK,Submit按鈕的TD標簽與一個style屬性(其值為"verticle-align:top")相關聯。
下面是在MyForm.java類中相關的初始化適當格式的Java代碼:
//設置OK按鈕單元格的垂直位置
grid.getCellFormatter().setVerticalAlignment(3,0,
HasVerticalAlignment.ALIGN_TOP);
如果在代碼中不進行這個調用,那麼該按鈕可能會在文本域的中間部分上下浮動。
十四、 總結 Google公司發布的Google Web Toolkit(GWT)已經引起業界的普遍關注。既然GWT的設計目的是為了簡化用Java語言開發
AJax應用程序,而
AJax是Web 2.0時代的技術基礎,所以GWT發行所引起的轟動應該在意料之中。
本文僅給出使用GWT和熟悉的Java工具開發一個簡單的
AJax應用程序的示例,而有關GWT的更多的探究剛剛開始,還依賴於廣大讀者的努力。