本文将使用 Apache Tomcat 作为示例 servlet 容器,因为它应用广泛,而且又可免费获得。其他 servlet 容器的表现与之类似。通常将是部署到现有服务器上;但如果不是,本文末尾的 参考资料 部分中的链接将告诉您 Tomcat 的下载位置。如果运行的是 Microsoft® Windows® 操作系统,Tomcat 有一个相对易用的面向 Windows 的二进制安装程序。如果是在 Mac 或 UNIX® 系统上,有一个可以提取并置于方便位置(通常是 /usr/local)的已编译版本;设置了一些环境变量后,您就可以继续学习了。
宽泛地讲,在 Tomcat 中部署 GWT 应用程序有两个步骤:
- 收集所有必需的文件并将其置于 Tomcat 可以查看的位置。
- 使 Tomcat 了解从 GWT 页面调用的所有服务器端操作。
![]() |
|
过程比设想的要多几个步骤,并且截至本文完稿时还没有官方方法可以将这几个步骤自动化。我在这里描述了手动过程。但是,当您读到本文时,可能已经有更健壮的工具支持部署。
- 定位到 Tomcat 的安装根目录。在该目录的下一级目录中,您会看到名为 webapps 的子目录。在 Tomcat servlet 容器内运行的每个应用程序都将从那里获得应用程序本身的目录。因此在 webapps 内创建一个名为 slicr 的目录。Java 服务器端应用程序的结构是按照 servlet 标准设定的,并且所有 servlet 运行程序都应当支持同一种结构。
- 在 slicr 目录内,创建一个名为 WEB-INF 的子目录 —— 是的,一定要大写。
- 将 GWT 编译的 JavaScript 页面和正常编译的 Java 类移至 slicr 目录。我将先展示如何创建 Java 类。
- 使用集成开发环境 (IDE)、Apache Ant 或者您希望使用的工具正常编译 Java 代码。此时有很多选择:
- 最快捷的选择是将整个 .class 文件树复制到 Tomcat 子目录 webapps/slicr/WEB-INF/classes,运行时 Tomcat 将把 webapps 目录的类路径变量自动置入该子目录中。
- 另一种方法是:可以使用您喜爱的工具把已编译的类转换为 JAR 文件,然后将 JAR 文件放入 webapps/slicer/WEB-INF/lib 目录。此外,Tomcat 将把 JAR 文件放入类路径变量的该目录中。
接下来,编译和移动 GWT 客户机文件。由于您已经在托管模式下完成了大部分工作,所以此时已经能够显式跳过此步骤的执行。编译代码的最快捷方法是使用托管模式控制台中的 Compile 按钮。但是有另一种方法可能更便于自动化。
在本系列的 第 1 部分 中,使用了 GWT applicationCreator
脚本创建 GWT 项目。当时,我偶然地提到随 GWT 项目文件一起创建的 “一些 shell 脚本”,但是并未进一步详细说明。
创建的其中一个 shell 文件名为 Slicr-compile。在 Windows 中,该文件的扩展名为 .cmd(当然,通常它是 项目名-compile)。从命令行调用文件,或者根据操作系统单击文件。脚本运行一段时间后将输出类似下面的代码:
Output will be written into ./www/com.ibm.examples.Slicr
Compilation succeeded
上面发生的情况是 GWT 正在将所有客户端 Java 代码编译成 JavaScript 代码。默认情况下,客户端代码是项目的 client 源目录中的所有代码;但是,您可以通过添加表单 <source path=path/>
的标记显式更改项目 .gwt.xml 文件的目录,其中 path 是需要添加的源路径。请注意,如果您添加自己的路径,则将不再包括默认的路径,因此如果还需要使用默认路径,必须显式添加默认路径。
通常,GWT 编译程序将成功转换合法的 Java 代码。但是,一些条目会造成问题。下面是一些需要特别注意的事项:
- GWT 客户机代码必须与 Java 1.4 兼容。那意味着没有类属性、没有自动装箱(autoboxing),也没有新样式 for-each 循环。我们推测,将来会兼容 Java 1.5。
- 只有极少的 Java Standard Library 子集受支持。受支持的库类限于 java.util 包和 java.lang 包。并不是所有这些包都受支持。具体来说,java.lang.reflect 包 不受 支持。正如您在本系列的 第 2 部分 中所见,此限制意味着必须将诸如数据库代码之类的条目置入系统的其他部分中。
- 如果使用正则表达式,则在运行时使用 JavaScript 规则而不是 Java 规则来解析正则表达式。
- Java 序列化不受支持。转而使用 GWT 远程过程机制,如在本系列的 第 3 部分 中所述。
- 浮点型数字和长变量类型的定义是有差异的。需要严格遵守浮点语义学的计算应当在服务器端处理。
- GWT 编译程序将忽略 Java
assert
语句。还忽略synchronized
声明,因为 JavaScript 解析程序不支持线程。
假定 Java 代码将传递那些要求并且编译成功,可以查看编译脚本的输出并研究 ./www/com.ibm.examples.Slicr 目录,如清单 1 所示。您看到的一些文件名可能有所不同。
1A0A627040909C0818A7A71B13246DCD.cache.html 1A0A627040909C0818A7A71B13246DCD.cache.xml 587B8CC6CF487EBD41844000481528BF.cache.html 587B8CC6CF487EBD41844000481528BF.cache.xml 64AF143E1C4C5866446137A8C42B4609.cache.html 64AF143E1C4C5866446137A8C42B4609.cache.xml B01955141995DDAD97AFC2941024CE4E.cache.html B01955141995DDAD97AFC2941024CE4E.cache.xml Slicr.html com.ibm.examples.Slicr.nocache.html gwt.js history.html tree_closed.gif tree_open.gif tree_white.gif |
先看简单的内容:Slicr.html 是您编码的 HTML 页面,直接从公共目录复制到这里。公共目录中的所有内容都将放在这里。三个 .gif 文件,正如您可以从名称推断的那样,将由 GWT 用于显示树小部件。由于 Slicr 应用程序中没有树小部件,因此可以假定 GWT 始终将这些图片放在输出中。继续来看带有可发音的名称的其余文件,history.html 有一些用于管理状态和保留 Back 按钮行为的 JavaScript 代码。gwt.js 和 nocache.html 文件都或多或少地包含了专门用于正确地启动和装入 GWT 应用程序的样例代码。
配有十六进制数字冗长名称的文件带给您什么内容。有六个 HTML/XML 对。打开 .html 文件,您将看到大量让人头晕的 JavaScript 代码,如清单 2 所示(这里的代码是随机选择的)。
function ob(pb,qb){z();pb.F = qb;return pb;} function rb(sb,tb,ub){z();sb.D = ub;sb.F = tb;return sb;} function vb(){} _ = vb.prototype = new f();_.jb = C;_.hb = E;_.i = 'java.lang.Throwable'; _.j = 1;_.D = null;_.F = null;function wb(xb){mb(xb);return xb;} function yb(zb,Ab){ob(zb,Ab);return zb;} function Bb(Cb,Db,Eb){rb(Cb,Db,Eb);return Cb;} |
希望那足够清晰。那是由 GWT 编译程序把干净、整洁的 Java 代码编译成 JavaScript 代码的实际结果。有五个 HTML/XML 对,每个对用于一种主要 Web 浏览器显示引擎:Firefox/Gecko(旧版本和新版本)、Windows Internet Explorer、Opera 和 Safari。XML 文件将管理一些数据类型映射,从而使用适于该引擎的正确数据类型。困惑在于压缩必须发送给浏览器的文本文件的大小。用户在浏览器中点击 GWT 页面时,样例页面中的 JavaScript 代码将为用户的浏览器自动检索和装入正确版本的代码。
要部署 GWT 页面,必须将 www/com.ibm.examples.Slicr 目录中的所有这些文件复制到 Tomcat Home/webapps/slicr 目录中。客户端上的操作就处理完了,现在转到服务器端。
如果以前从事过 servlet 开发,则这部分 GWT 过程应当十分熟悉。编写的服务器端代码就是另一个 servlet 应用程序,并且是使用 Servlet 标准部署的。
首先,必须使用一个简单普通的 Java 编译程序编译所有 Java 代码。此时,您有两种选择:
- 可以将所有 .class 文件放在 Tomcat Home/webapps/WEB-INF/classes 目录中。
- 另外一种方法可以可以将所有 .class 文件结合为一个 .jar 文件,并将该文件放在 Tomcat Home/webapps/WEB-INF/lib 目录中。
经过编译的代码应当包括已经使用 GWT 编译的客户端代码 —— 记住,在服务器端使用一些客户机类以帮助数据传输。
还必须将使用的所有第三方库放入同一个 /lib 目录中。此目录总是包括文件 gwt-servlet.jar,该文件本身是随 GWT 分发附带的。此文件包含应用程序所需的所有 GWT 用户文件。
注:最初分发的 GWT 不包括此文件。不过,您仍可以看到 Web 部署说明,其中涉及破解 gwt-user.jar 文件以删除将干扰 Tomcat 的 servlet 类。如果 GWT 分发是最新的,此步骤是不必要的。
在这种情况下,还需要使用 derby.jar 文件,该文件是 Derby 分发的一部分并且包含所需的 Derby 数据库类。如果需要迁移 Derby 数据库以便不丢失放在其中的浇头数据,只需安放 Derby 在本系列的 第 2 部分 中创建的 slicr 目录数据库(如果遵循第 2 部分中的指示,此目录应当位于与 /src 目录同级的项目顶级目录下)。把目录放在何处无所谓,但是 ToppingServiceImpl
类中为 Derby 定义 JDBC URL 的行必须反映该位置:
public static final String PROTOCOL =
"jdbc:derby:[LOCATION OF SLICR DB DIRECTORY]/slicr;";
我把 Derby 数据库放入了 Tomcat webapps/slicr 目录,这是与 WEB-INF 目录同级的目录,因此我的代码显示如下:
public static final String PROTOCOL =
"jdbc:derby:/usr/local/apache-tomcat-5.5.20/webapps/slicr/slicr;";
当代码对 Tomcat servlet 容器可见后,还必须专门定义 Tomcat Web 服务器可以理解的明确来自 GWT 应用程序的所有服务器端调用。这样做涉及在 Tomcat 应用程序的 web.xml 文件中将这些服务器端调用定义为 servlet。从本质上来说,在 gwt.xml 中定义的每个 servlet 都类似如下所示的代码:
<servlet path="/toppings" class="com.ibm.examples.server.ToppingServiceImpl"/>
必须在 web.xml 文件中创建 <servlet>
和 <servlet-mapping>
标记。清单 3 将显示 GWT 项目的整个 web.xml 文件,其中包含用于一个 ToppngService
servlet 的这两个标记。
<?xml version="1.0" encoding="UTF-8"?> <web-app> <servlet> <servlet-name>ToppingService</servlet-name> <servlet-class> com.ibm.examples.server.ToppingServiceImpl </servlet-class> </servlet> <servlet-mapping> <servlet-name>ToppingService</servlet-name> <url-pattern>/toppings</url-pattern> </servlet-mapping> </web-app> |
在 web.xml 文件内,gwt.xml 文件中提供的两部分信息被分割到两个不同的标记中,作为 Java “使所有内容都尽可能冗长”(make-everything-as-verbose-as-possible)计划的一部分。<servlet>
标记将获取 servlet 的全限定类名,而 <servlet-mapping>
标记将获取与您在 GWT 文件中定义相同的 URL 路径名。两者通过共同的 servlet-name
属性统一,该属性可以为任意内容,只要保持一致。不过,在可能的情况下,最好使用简单的命名约定。例如实际服务的 GWT 名称。
当然,此文件的创建操作可以由从纯文本处理到 XML 文档对象模型(Document Object Model,DOM)处理的任意数目个方法出色地自动完成。编写完此文件后,请将其置于 Tomcat WEB-INF 目录中。此时,Slicr 部署已完成,并且可以通过启动 Tomcat 并键入诸如 http://localhost:8080/slicr/Slicr.html
之类的 URL 测试应用程序。如果成功,您将看到像在本系列的 第 3 部分 末尾一样的 Slicr 页面。
问题 | 原因 |
---|---|
键入 URL 后根本没看到任何内容。 | 原因最有可能是 Slicr.html 页面不在正确位置。如果只得到了 Welcome to Slicr 而没看到任何 GWT 小部件,则最有可能未正确放置 GWT JavaScript 文件。 |
看到页面的左侧,但是浇头窗格中只显示标题而没有其他内容。 | 这意味着服务器调用失败。您只能去查看 Tomcat 日志来正确诊断此问题(或者采用另外一种方法,将回调中的 OnFailure() 方法更改为将错误消息打印到窗格中)。第一种可能性是 Slicr Java 类文件不在正确位置,这甚至阻止了 servlet 的装入(如果未正确设置 web.xml 文件,也可能出现这个问题)。 |
遇到 Derby 故障。 | 确保 JDBC URL 匹配 Derby 数据目录的位置。 |
至此,整个过程就介绍完毕了,可以看到,虽然步骤不少,但是没有一个步骤特别复杂。强烈建议尽可能使用脚本、Ant 任务或项目采用的任何其他方法进行自动部署。您将在 参考资料 中看到一个示例,如今,各种 Java IDE 越来越多地添加 GWT 支持,包括部署在内。拥有一键式部署将为您省去很多令人头痛之事。
通过学习 GWT 这一系列文章的课程,您已经构建了一个简单的 Web 应用程序,它演示了可以将 GWT 与数据库后端怎样结合使用来创建具有富客户机行为的健壮 Web 应用程序。虽然完成了四篇文章,但也只是触及了 GWT 必须提供的内容的表层。强大的功能 —— 例如 JUnit 测试集成,通过 JavaScript Serialized Object Notation (JSON) 数据交换使用其他 Web 服务,以及新 GWT 国际化功能 —— 值得您花时间查阅。GWT 开发持续增长,始终在添加新功能和追加工具。我希望您能够善加观察,找到构建目标应用程序所需的工具。
学习
- 访问 GWT 的 Google Groups 论坛。
- 查看 正式 GWT blog。
- 访问 Apache Derby 站点 以获得 Derby 资源。
- 查阅 developerWorks Apache Derby 项目资源中心 获取文档、教程和其他参考资料以帮助您立即使用 Derby 进行开发。
- 了解关于 IBM® Cloudscape™ 数据库 的更多信息,该数据库是使用 Apache Derby 代码库构建的。
- 访问 developerWorks 开源软件技术专区 以获得大量的 how-to 信息、工具和项目更新信息,可以帮助您利用开放源码技术进行开发,并与 IBM 的产品结合使用。
- 浏览 developerWorks Open Source 专区中所有可获取的 Apache 文章 和 免费 Apache 教程。
- 浏览 Safari 书店 中关于这些内容及其他技术主题的书籍。
- 随时关注 developerWorks 技术事件和网络广播。
- 获得 本系列文章的 RSS 提要。(查找关于 RSS 的更多信息。)