开发人员:SOA
   下载
 Oracle JDeveloper 10g
 OC4J 10g 单机版
 
   关键词
soa, java, opensource全部
 

 

Google Web 工具包简介


作者: Stéphanie Antoine、Julien Dubois 和 Jean-Philippe Retaillé

 

了解如何使用 Google Web 工具包执行各种基本任务和高级任务,如 RPC 通信、历史管理和打包生产就绪的应用程序。

2006 年 10 月发布

Web 2.0 及其相应的技术产品 Asynchronous JavaScript 和 XML (Ajax) 正由于 Gmail 和 Google Maps 等应用程序而势头日盛。对于 Web 应用程序,Ajax 的主要益处是极大地改善了用户体验。虽然作为 Ajax 的技术基础的 JavaScript 和 DHTML 问世已经有几年了,但由于它们难于掌握而受到大多数程序员的忽视。今天,用 JavaScript 编写的框架(如 Dojo)可以帮助您构建 Ajax 应用程序,但是您仍然需要深入了解 JavaScript 才能使用这些框架。Google 提供了另一种方法来帮助 Java 开发人员更加高效地创建 Ajax 应用程序。这一新框架称为 Google Web 工具包 (GWT),它可以与 Oracle JDeveloper 有效地配合使用。 http://code.google.com/webtoolkit 的 Apache License v. 2.0 下免费提供有 GWT。

 

主要特性和限制


利用 Ajax 进行开发的主要问题之一是您需要掌握大量各不相同的技术。根据您的项目(例如,业务应用程序)的性质,这可能是个大障碍。

此外,不同的 Web 浏览器支持 JavaScript 和 DHTML 的方式也不同。例如,Microsoft Internet Explorer 和 Mozilla Firefox 处理这些技术的方式稍有不同;如果您希望应用程序在用户的 PC 上无缝运行,则需要采取适当措施来应对这种情况。

虽然现在提供的大多数 Ajax 框架简化了开发工作,但是您仍然需要很好地掌握这一系列技术。因此,如果您使用 Ajax 只是为了改善应用程序的用户体验,而没有同时将其用作业务的战略优势,那么在该技术上花费大量的时间和金钱可能是不明智的。

GWT 提出了另一种创建 Ajax 应用程序的方法。它使用 Java 作为单一的编程语言,可同时用于客户端和服务器端。是返回 Java 小程序吗?完全不是:GWT 提供一个编译器,将客户端的 Java 代码转换为 JavaScript 和 DTHML。从程序员的角度而言,该解决方案极大地简化了涉及的一系列技术:您只需掌握 Java。缺点是您无法完全控制应用程序的客户端代码,因为它最终是由 GWT 编译器生成的。

您的应用程序的客户端的 Java 代码会受到一些限制,因为 JavaScript 不会实施 Java 中提供的所有面向对象的概念和 API。您只能使用 Java 关键字和 API(java.lang 和 java.util)的子集:
  • 直接支持所有基元类型(例如 byte、char、short 和 int)以及它们的相应类(例如 Byte 和 Char),但是 long 除外,它被转换为 JavaScript 等同于 double。建议您使用 int,不要使用 long。
  • 用户定义的例外(无论是否检查)都可能有,但方法 Throwable.getStackTrace() 不可用。某些 JVM 异常也会有(例如 IndexOutOfBoundException)。
  • 同步的关键字没有效果,因为 JavaScript 是单线程。多线程 API 不可用。
  • 不支持反射 (Reflection)。但是,您可以通过使用方法 GWT.getTypeName(Object) 获得对象的类名。
  • 不支持终结 (Finalization)。
  • 可以使用某些来自 java.util 的对象容器,例如 Stack、Vector 和 HashMap。Date 类也可用。
此外,GWT 提供特定的 API 来管理 GUI、国际化和 XML 分析。它还提供一个综合库来管理客户端和服务器之间的通信。它使用著名的远程过程调用 (RPC) 原则,该原则通过通用的 servlet (RemoteServiceServlet) 实现,可根据您自己的需要进行专门的规定。您还可以使用 JavaScript Object Notation (JSON) 作为通过 GWT HTTPRequest 类发送的 HTTP 消息的数据交换格式。

GWT 还提供一个名为 JavaScript Native Interface (JSNI) 的接口,使您可以将自己手工编写的 JavaScript 代码与 GWT 编译器生成的代码混合在一起。JSNI 使用 Java Native Interface (JNI) 使用的关键字 native 来定义您自己的 JavaScript 函数。这些函数的主体在特定格式的注释中定义。

最后,您可以通过 GWTTestCase(JUnit 提供的类 TestCase 的规范)对 JDeveloper 中的代码进行单元测试。

聚焦通过 GWT 进行 GUI 编程


Ajax 显著改变了开发 Web 应用程序的方式。大多数时候,Ajax 应用程序只需要一个 Web 页。JavaScript 和 DHTML 对其内容进行动态修改以产生类似于本机应用程序提供的用户体验。

因此,GWT 提供了一个编程模型,其原则类似于 Swing 或 AWT 程序员。GUI 不再象在经典的 Web 应用程序中那样由 HTML 标记指定。它由 Java 代码直接进行编程,方式类似于 AWT 或 Swing。GUI 编程的著名概念可用于 GWT 中:

  • 小部件,包括一般项目(例如按钮、文本框和复选框)和更高级的项目(例如树和菜单栏)
  • 面板,包含小部件,具有其自己的布局(面板和布局不象在 Swing 中那样是独立的)
  • 小部件生成的事件。监听器必须实施特定的接口。
轻松加载 GWT JavaScript 库和指定应用程序的入口点:只需创建一个简单的 HTML 页。

GWT 使用层叠样式表 (CSS)。每个小部件都有其自己的样式,您可以根据需要进行更改。您必须创建自己的 CSS 来超载 GWT 定义的默认值。

如果标准小部件不适合您的需要,您还可以进行自定义。(但是该主题不在本文的讨论范围内。)

项目结构

GWT 项目必须符合预定义的结构才能被编译器接受。因此,必须为您的应用程序定义一个全局程序包。程序包名称的最后一部分必须是应用程序的名称(例如 global.package.yourApplicationName)。描述应用程序的 XML 文件必须可在该全局程序包的根下找到。该文件的名称必须是应用程序的名称后跟 .gwt.xml 扩展名(例如,yourApplicationName.gwt.xml)。此外,您必须创建三个子程序包:

  • “client”,包含客户端的 Java 代码(该代码必须符合前面提到的限制)
  • “server”,包含服务器端的 Java 代码(此处,您可以使用全部 J2SE/J2EE API)
  • “public”,包含应用程序的 HTML 页、CSS 和图像
您的项目必须声明若干 jar:
  • gwt-dev-windows.jar 或 gwt-dev-linux.jar:包括编译器在内的编程工具。GWT 提供的嵌入式 Web 浏览器依赖平台。
  • gwt-user.jar:GWT 运行时。
  • gwt-servlet.jar:该 jar 将部署到包含 GWT 编译器生成的代码的应用服务器上。它包含 RemoteServiceServlet。
编译的结果存储在一个目录中,其名称是应用程序的全局程序包的名称。该目录包含构成应用程序客户端的所有元素(例如 HTML 页、CSS 和 JavaScript 文件)。和通常一样,这些元素必须在 Web 应用程序内部部署。

托管模式和 Web 模式


在 GWT 中有两种执行模式。托管模式在嵌入式服务器和 Web 浏览器内执行应用程序代码,因此,您无需在应用服务器上部署代码。该模式在应用程序测试期间很有用,因为它可以简化调试。

Web 模式是在 OC4J 等真正的应用服务器上部署 Ajax Web 应用程序。当应用程序在生产中运行时,通常使用此模式。

 

使用 Oracle JDeveloper 构建您的第一个 GWT Web 应用程序

到目前为止,您已经了解了 GWT 的工作方式;现在,让我们编码示例 Web 应用程序。

示例应用程序是一个工作列表管理器。其特性十分简单:创建、编辑、删除工作列表并对其进行优先级排列。我们选择了该示例是因为它很容易理解,然而其实施涵盖了大量 GWT 的特性。

下面是最终应用程序的屏幕快照:

图 1

 

第 1 步:安装 GWT

从 Google 的 Web 站点 http://code.google.com/webtoolkit/ 下载 GWT。在写本文时,GWT 推出的是 Windows 和 Linux 版本。GWT 是特定于平台的,因为其托管模式在 Firefox 的修改版本中工作,该版本本身依赖于平台。(我们可以在 Apple 计算机上成功地使用 GWT 的 Linux 版本,但是托管模式不起作用。)

GWT 下载形式是一个归档文件,您必须使用 Linux 上的 tar -xvf 命令或者 Windows 上的解压缩工具进行解压缩。这就是您安装该工具包需要做的所有工作。

 

第 2 步:运行 applicationCreator 脚本

打开命令行,转至 GWT 的安装目录。该目录包含 applicationCreator 脚本,我们将使用该脚本启动我们的应用程序。由于我们希望应用程序存储在 Oracle Technology Network 目录中,因此我们将“-out otn”作为参数添加到脚本中。在 Linux 上,键入:

./applicationCreator -out otn otn.todo.client.TodoApp

图 2

在 Windows 上,使用:

applicationCreator -out otn otn.todo.client.TodoApp
该脚本生成基本的项目结构 — 请求的应用程序类中的示例“Hello word”代码以及两个脚本:TodoApp-shell(用于在托管模式下运行应用程序)和 TodoApp-compile(用于打包应用程序以便在 Web 模式下使用)。

 

第 3 步:在 JDeveloper 中打开项目

启动 JDeveloper 并创建一个新的 Web 项目:

图 3

单击 Next 按钮。JDeveloper 将询问新项目的位置。使用应用程序的名称作为 Project Name,选择应用程序根目录(如步骤 2 的定义)作为 Directory Name

图 4

单击 Next 按钮,并验证您的应用程序是 J2EE 1.4 应用程序:

图 5

单击 Next 按钮,并选择您的项目 Web 属性:Document Root 是当前项目的 www 目录,J2EE Web Application Name 和 J2EE Context Root 都是项目名称:

图 6

这将创建 JDeveloper 项目,但是将出现某些编译错误,因为 GWT 的库未包含在项目类路径中。在项目属性中,选择左侧端树的 Libraries 节点,并添加 gwt-user.jar 库:

图 7

 

您的项目现在应该可以编译,看起来与以下内容相似:

图 8

 

编写客户端代码

上面的 applicationCreator 脚本创建了一个基本的“Hello world”应用程序,可在 otn.todo.client 程序包中使用。下面是其主要方法:

public void onModuleLoad() {    
    final Button button = new Button("Click me");    
    final Label label = new Label();
    button.addClickListener(new ClickListener() {     
      public void onClick(Widget sender) {       
        if (label.getText().equals(""))
          label.setText("Hello World!");           
        else
          label.setText(""); 
      }   
    });
    RootPanel.get("slot1").add(button);    
    RootPanel.get("slot2").add(label);   
  }
}                                          
该方法将创建一个按钮“Click Me”。单击该按钮后,将显示“Hello World”。

该方法分为三部分:

  1. 创建 Button 和 Label 小部件
  2. 创建 ClickListener 对象。该代码与您用 Swing 编写的内容很接近;如果您具有桌面 Java 背景则更容易理解。
  3. 在 HTML 页上显示小部件:slot1 和 slot2 都是该页上的 HTML 元素
用作框架的 HTML 页位于 src/otn/todo/public 目录中。它将两个 HTML 元素(slot1 和 slot2)定义为表单元格。

在托管模式下运行和调试


现在您已经创建了应用程序并且已经看到其生成的内容,结下来让我们来执行它。

您可以通过从命令行使用 TodoApp-shell 脚本轻松地运行该项目。虽然这是启动应用程序的很好途径,但是您可能更喜欢直接从 JDeveloper 内启动应用程序。为此,单击 Run 菜单,选择 Choose Active Run Configuration > Manage Run Configurations。编辑默认的运行配置并使用以下命令:

  • 对于 Default Run Target:使用 com.google.gwt.dev.GWTShell,它在特定于平台的 GWT jar 内。在 Linux 上,它类似以下内容:
    path.to.your.gwt.installation.directory/gwt-devlinux.jar!/com/google/gwt/dev/GWTShell.class
    在 Windows 上,它类似以下内容:
    path.to.your.gwt.installation.directory/gwt-dev-windows.jar!/com/google/gwt/dev/GWTShell.class
  • 对于 Program Arguments,使用:
    -out path.to.your.gwt.installation.directory/otn/www otn.todo.TodoApp/TodoApp.html
  • 对于 Run Directory,使用
    path.to.your.gwt.installation.directory/otn
最终结果类似以下内容:

图 9

要运行您的应用程序,您必须向其类路径中再添加两个库:GWT 特定于平台的 jar 和应用程序的 src 目录:

图 10

您现在应能够从 JDeveloper 运行应用程序了。

这是一个很复杂的设置,但是令人欣慰的是,您可以重新使用它对应用程序进行调试。使用 Debug 按钮而不是 Run 按钮。然后,您可以象平常一样使用调试器 — 设置断点、逐步执行代码等:

图 11

关于该特性给人印象很深的是,您可以通过标准的 JDeveloper 调试器调试用 Java 编写的客户端代码。

 

扩展您的 GWT Web 应用程序

现在您已经创建了一个简单的 GWT Web 应用程序,让我们通过两个最常用的 GWT 特性对其进行扩展:RPC 机制(该机制允许应用程序调用服务器端代码)和 History 对象(通过该对象,用户可精确处理浏览器的 Back 按钮)。

 

使用 RPC 进行客户端和服务器之间的数据交换

到目前为止,您只创建了应用程序的客户端代码:使用 GWT 编译器,您已经生成了大量 HTML 和 JavaScript 文件,它们将在最终用户的浏览器中运行。但是,如果该应用程序不能与服务器通信就没有什么用处了。

使用 GWT,客户端/服务器通信就是对 servlet 进行编码并使其与应用程序通信。下面是您要做的工作。

创建一个定义您的服务的接口。该接口必须扩展 Google 的 com.google.gwt.user.client.rpc.RemoteService 接口,并可以放到客户端程序包(本例为 otn.todo.client)中。

然后,对接口进行编码以便允许您在服务器上读取和写入工作列表:

package otn.todo.client;
import java.util.List;
import com.google.gwt.user.client.rpc.RemoteService;
public interface TodoListBackupService extends RemoteService {
/**
* Save the to-do list on the server.
*/
void saveTodoList(List todoList);
/**     
* Get the to-do list on the server.
*/
List getTodoList();
}
                                        
对 Servlet 进行编码。在服务器端,您必须编码出具有以下特征的类:
  1. 扩展 Google 的 com.google.gwt.user.server.rpc.RemoteServiceServlet 类(该类反过来会扩展 Java 的 javax.servlet.http.HttpServlet,有效使其成为 servlet)
  2. 实施步骤 1 中编写的接口
  3. 位于服务器程序包(本例为 otn.todo.server)中
package otn.todo.server;   
import java.util.ArrayList;  
import java.util.List;      
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import otn.todo.client.Todo; 
import otn.todo.client.TodoListBackupService; 
import com.google.gwt.user.server.rpc.RemoteServiceServlet;  
public class TodoListBackupServiceImpl extends RemoteServiceServlet implements   
               TodoListBackupService {
 private static final String TODOLIST_KEY = "TODOLIST_KEY";
    public void saveTodoList(List todoList) {
           HttpServletRequest request = this.getThreadLocalRequest();
          HttpSession session = request.getSession();
         session.setAttribute(TODOLIST_KEY, todoList);
       }     
       public List getTodoList() {
         HttpServletRequest request = this.getThreadLocalRequest();
          HttpSession session = request.getSession();
         if (session.getAttribute(TODOLIST_KEY) == null) {
                   List todoList = new ArrayList();
                    Todo todo = new Todo("Hello from the server");
                      todoList.add(todo);
                 return todoList;    
            } else {
                    return (List) session.getAttribute(TODOLIST_KEY);
           }
   }
}                                        
该 servlet 在用户的 HttpSession 中只存储工作列表;这当然是保存数据的基本方法。在一般的应用程序中,您可以使用 JNDI 访问 EJB,或者使用任何经典模式从 servlet 访问业务服务。

最后,您必须在 servlet 容器内配置该 servlet。如果您使用的是 GWT shell,您可以在 *.gwt.xml 配置文件中进行配置,本例中该配置文件为 TodoApp.gwt.xml:

<module>
   <!-- Inherit the core Web Toolkit stuff. -->
  <inherits name='com.google.gwt.user.User'/>
   <!-- Specify the app entry point class. -->
   <entry-point class='otn.todo.client.TodoApp'/>
        <servlet path="/todoListBackupService" class="otn.todo.server.TodoListBackupServiceImpl"/>
</module>                                        
如果您希望在其他应用服务器(如 OC4J)中对其进行配置,只需将平常的 XML 配置添加到 WEB-INF/web.xml 文件中即可:
<servlet>
  <servlet-name>TodoListBackupService</servlet-name>
 <servlet-class>otn.todo.server.TodoListBackupServiceImpl</servlet-class>
</servlet>  
<servlet-mapping>
 <servlet-name>TodoListBackupService</servlet-name>
      <url-pattern>/todoListBackupService</url-pattern>
</servlet-mapping>
添加一些粘合剂。我们需要的粘合剂是 Async 类,它必须遵循几个规则:
  • 位于客户端程序包(otn.todo.client)中。
  • 其名称与步骤 1 中描述的接口的名称相同,最后面添加 Async。
  • 其方法与步骤 1 中描述的接口的方法相同,但是它们都回调一个附加参数 com.google.gwt.user.client.rpc.AsyncCallback。
package otn.todo.client;
import java.util.List;
import com.google.gwt.user.client.rpc.AsyncCallback;
public interface TodoListBackupServiceAsync {
 /**
 * Save the to-do list on the server.
 */
 void saveTodoList(List todoList, AsyncCallback callback);
 /**
 * Get the to-do list on the server.
 */
 void getTodoList(AsyncCallback callback);
}                                 
在应用程序内使用该类。要从客户端应用程序内访问服务器端代码,使用 com.google.gwt.core.client.GWT 类,该类可以创建一个很特殊的对象:
TodoListBackupServiceAsync todoListBackupService = (TodoListBackupServiceAsync) GWT.create(TodoListBackupService.class);
这将在运行时创建一个实施两个接口的类:
  • 我们刚刚在步骤 3 中进行编码的 Async 接口
  • Google 的 com.google.gwt.user.client.rpc.ServiceDefTarget 接口
第二个接口用于配置类以便它可以指向步骤 2 中定义的 servlet:
ServiceDefTarget endpoint = (ServiceDefTarget) todoListBackupService; endpoint.setServiceEntryPoint("/todoListBackupService");
                                          


                                        
现在您已经将该对象配置为可访问服务器端服务,让我们来访问服务。如您在步骤 3 中所见,Async 接口允许您通过添加 AsyncCallback 回调参数访问在服务中定义的所有方法。该参数用于定义应用程序的行为,具体取决于服务器端调用的成功或失败:
 AsyncCallback callback = new AsyncCallback() {
         public void onSuccess(Object result) {
             printTodoList();        
         }
         public void onFailure(Throwable caught) {
             Window.alert("Warning : the to-do list could not be saved on the server. Maybe the server is down.");
         }        
     };                                        
让我们把它们全都放在一起。下面是访问 TodoListBackupService 业务服务的两个客户端方法的完整代码:一个用于在服务器端保存工作列表,另一个用于读取该列表:
 /**
     * Update the to-do list with data from the server.  
     */
    private void updateTodoListFromServer() {
        TodoListBackupServiceAsync todoListBackupService =
            (TodoListBackupServiceAsync)GWT.create(TodoListBackupService.class);
        ServiceDefTarget endpoint = (ServiceDefTarget)todoListBackupService;
        endpoint.setServiceEntryPoint("/todoListBackupService");
        AsyncCallback callback = new AsyncCallback() {
                public void onSuccess(Object result) {
                    todoList = (List)result;
                    saveTodoListInHistory();
                }
                public void onFailure(Throwable caught) {
                    Todo todo =
                        new Todo("ERROR!! Server could not be reached.");
                    todoList.add(todo);
                    saveTodoListInHistory();
                }
            };
        todoListBackupService.getTodoList(callback);
    }
    /**
     * Save the to-do list on the server.
     */
    private void saveTodoListOnServer() {
        saveTodoListInHistory();
        TodoListBackupServiceAsync todoListBackupService =
            (TodoListBackupServiceAsync)GWT.create(TodoListBackupService.class);
        ServiceDefTarget endpoint = (ServiceDefTarget)todoListBackupService;
        endpoint.setServiceEntryPoint("/todoListBackupService");
        AsyncCallback callback = new AsyncCallback() {
                public void onSuccess(Object result) {
                    printTodoList();
                }
                public void onFailure(Throwable caught) {
                    Window.alert("Warning : the to-do list could not be saved on the server. Maybe the server is down.");
                }
            };
        todoListBackupService.saveTodoList(todoList, callback);
    }      
示例应用程序在启动时进行服务器端调用。该调用将返回用户的 HttpSession 中保存的最新工作列表,或者包含“Hello from the server”工作的新工作列表:

图 12

管理 Back 按钮

在高端 Web 应用程序中,浏览器的 Back 按钮经常断开。经典的 Ajax 应用程序不支持返回前一 Web 页的标准 Web 行为。

另一方面,GWT 允许对 Back 按钮进行编程处理。这是一个功能强大却又很难处理的特性,我们将在示例应用程序中对其进行探究。提议是将 Back 按钮用作 Undo 按钮:单击该按钮将显示最新事件之前的工作列表。同样地,Forward 按钮将用作 Redo 按钮。

实施 HistoryListener 接口。要以编程方式管理 Back 按钮,GWT 应用程序必须实施 com.google.gwt.user.client.HistoryListener 接口。这将强制编写 onHistoryChanged(String _historyToken) 方法:

public class TodoApp implements EntryPoint, HistoryListener {
    /**
     * This method is called whenever the application's history changes.
     */
    public void onHistoryChanged(String _historyToken) {
        if (Integer.parseInt(_historyToken) + 1 != historyToken) {
            if (historyMap.get(_historyToken) != null) {
                historyToken = Integer.parseInt(_historyToken);
                todoList = (List) historyMap.get(_historyToken);
            }
        }
        printTodoList();
    }                   
该方法意味着当浏览器的历史记录更改时接收事件。您必须将其作为监听器添加到 GWT 的 History 对象中。该操作通常在 onModuleLoad() 方法中完成,以便 History 对象在启动时正确初始化:
 /**
  * This is the entry point method.
  */
    public void onModuleLoad() {   
        History.addHistoryListener(this);
    }           
现在,每次浏览器的历史记录更改时都会调用 onHistoryChanged(String _historyToken) 方法。

该方法可以根据作为参数传递的令牌重新创建应用程序的状态。本例中,您将使用该令牌作为密钥来查找存储在历史地图中的工作列表。

向历史记录中添加条目。要使 onHistoryChanged(String _historyToken) 方法起作用,您必须预先在历史记录中存储一些条目。

利用 History 对象很容易实现,可以使用其静态 newItem(String historyToken) 方法:

private void saveTodoListInHistory() {
        List todoListClone = new ArrayList();
        Iterator it = todoList.iterator();
        while (it.hasNext()) {
            Todo todo = (Todo) it.next();
            todoListClone.add(todo.clone());
        }            
        historyMap.put(String.valueOf(historyToken), todoListClone);
        History.newItem(String.valueOf(historyToken));
        historyToken++;
    }               
在本例中,您将应用程序状态存储在了地图中,因此使用历史记录令牌可以找到它。注意,您使用了一个数字作为历史记录令牌,也可以改用任何字符串。

部署您的 Web 应用程序

要部署通过 GWT 构建的 Web 应用程序,您需要编译客户端代码,在 Web 应用程序的 .war 文件中打包结果,然后将 .war 文件部署到相应的应用服务器 OC4J 上。

 

编译客户端代码

编译客户端代码的方法有多种。使用 applicationCreator 脚本后,GWT 将创建一个名为 TodoApp-compile 的 shell 脚本。您可以从命令行启动该脚本。与 TodoApp-shell 一样,这是编译应用程序的很好方法;但是,您可能更喜欢直接从 JDeveloper 内部启动该脚本。

另一种编译代码的方法使以托管模式执行应用程序,以便直接从 JDeveloper 编译代码。您的应用程序的窗口的工具栏包含编译/浏览按钮,与下图类似:

 

 

图 13

在编译过程结束时,您的默认 Web 浏览器将打开以便您可以测试结果。GWT 开发 shell 的窗口将显示编译是否成功:

 

图 14

 

无论您使用何种方法编译代码,您都可以在项目的 www/otn.todo.TodoApp 中找到生成的文件。

最后一种编译代码的方法是使用 Ant。GWT 不提供特定的 Ant 任务,但是您可以通过标准的 Java Ant 任务启动任何 Java 类(例如 GWTCompiler)。首先,定义包含 GWT jar 的路径:

<path id="project.class.path">
 <pathelement path="${java.class.path}/"/>
 <pathelement location="src"/>
 <pathelement path="/your/path/to/gwt-user.jar"/>
 <pathelement path="/your/path/to/gwt-dev-linux.jar"/>
 <!-- ... -->
</path>   
在属性文件中设置 gwt.output.dir 和 entry.point.class 变量,如下所示:
gwt.output.dir=www
entry.point.class=otn.todo.TodoApp                                        
最后,在 Ant 脚本中声明属性文件(此处为 build.properties),如下所示:
<property file="build.properties"/>
您可以通过在任务的 Context 菜单中选择 Run Target GWTCompile 直接启动该新的目标:

图 15

Apache Ant Log 窗口将显示以下结果:

GWTcompile:
 [java] Output will be written into www\otn.todo.TodoApp
 [java] Compilation succeeded
BUILD SUCCESSFUL 

在 OC4J 中部署

编译完应用程序之后,在 OC4J 下部署它只需创建一个适当的部署配置文件。如果您按照前面描述的步骤进行操作,您应该已经具有一个默认的部署配置文件。如果没有,只需选择 File > New...> Deployment Profiles > WAR File,创建一个新的配置文件。

使用您的配置,一切都应该正常工作。但是,如果您遇到任何问题,请查看以下常见的错误:

  • 在 Project Properties 的 Project Content > Web Application 中,HTML Root Directory 应该是应用程序的 www 目录(在该目录中,GWT 将对应用程序进行编译以便在托管模式下运行)。
  • 在部署配置文件的 File Groups > WEB-INF/lib > Contributors 中,应该添加 gwt-user.jar。该 jar 文件包括 J2EE 规范中的 javax.servlet 程序包。这在本例中没有引起任何问题;但是,您通常不应将这些类部署在 Web 应用程序中,因为它们会引起麻烦。如果发生了这种情况,GWT 还提供有一个 gwt-servlet.jar 文件,它是没有 javax.servlet 程序包的 gwt-user.jar。
  • Web 应用程序的上下文根影响 GWT 的 RPC 机制。如果 GWT 客户端应用程序要与服务器通信(如“使用 RPC 进行客户端和服务器之间的数据交换”所述),它必须可以找到服务器。这就是我们已经讨论过的 endpoint.setServiceEntryPoint("") 方法的目的。在本例中,我们将应用程序部署到了服务器的根上,这就是 GWT shell 默认的工作方式。但是,如果您将应用程序部署到 TodoApp Web 上下文(在部署配置文件的一般属性中),请将端点设置为 /TodoApp/todoListBackupService 而不要设置为 /todoListBackupService。不用忘记该 URL 还应正确映射到应用程序的 web.xml 文件中(如前所述)。
  • 假设 OC4J 已在系统上正确安装,将应用程序部署到服务器是很简单的:只需右键单击部署配置文件,并选择 Deploy to OC4J。
部署的应用程序有两部分:
  • 客户端应用程序是一组先前编译的 HTML 和 JavaScript 文件。(请参见“编译客户端代码”部分。)OC4J 充当经典的 Web 服务器,将这些文件传递给用户。
  • 服务器端应用程序基本上是一个处理 RPC 通信的 servlet。在 OC4J 中部署后,该 servlet 可以访问诸如 EJB 或 JMS 提供商等企业资源。
性能智能化为静态资源提供了高效服务;应用程序的主要性能瓶颈来源于客户端/服务器通信。但是由于有 OC4J,我们可以访问一些有趣的服务器端性能图形:

图 16

如该图形所显示的,在正常负载下(每秒几个请求),应用程序的服务器端部分在平均不到 4 ms 的时间内进行响应,这是很难得的结果。
Stéphanie Antoine 是一位 J2EE 高级开发人员,目前在一家大型的法国软件咨询公司工作。 Julien DuboisJean-Philippe Retaillé 都是 J2EE 专家和作者,他们的最新书籍 Spring par la Pratique (Eyrolles, 2006) 是关于 Spring J2EE 框架的第一本法语书籍。

将您的意见发送给我们