在 SpringSource dm Server 中部署 GWT 应用程序 - 第 2 部分

工程 | Ben Corrie | 2008 年 11 月 24 日 | ...

介绍

这是由三部分组成的系列博客中的第二篇,描述了在 SpringSource dm Server™ 中构建和部署 GWT 应用程序的分步方法。第一篇博客着眼于如何从一个 GWT 示例应用程序创建一个简单的 WAR 文件。下一篇博客将探讨如何将我们在第 1 部分中创建的 WAR 文件转换为一个“共享库” WAR。这意味着我们将把应用程序的 GWT 依赖项外部化到一个 OSGi Bundle 中,以便它可以被任意数量的 GWT 应用程序共享。您可以将其视为使用 GWT 远程处理功能扩展了我们的 dm Server。

正如在第 1 部分中提到的,我在这篇博客中不使用Spring Framework,而是专注于 SpringSource dm Server™SpringSource Tool Suite 来部署“纯粹的” GWT。

请参阅第 1 部分了解 GWT StockWatcher 示例的背景以及我使用的软件。

快速回顾

第 1 部分中,我们从头开始将 GWT StockWatcher 示例应用程序构建为一个 Eclipse 项目,然后将代码生成到一个动态 Web 项目中,并将其部署到 dm Server。最后,我们将动态 Web 项目导出为一个 WAR 文件,并在 STS 外部进行部署。

此处描述的分步方法将基于我们在第 1 部分中完成的工作,而不是从头开始。我们在第 1 部分中现在要更改的唯一一件事是删除对以下库的显式依赖:gwt-servlet.jar库。

步骤 1:将我们的 GWT 依赖项转换为 OSGi Bundle

首先,再补充一点背景。“共享库”方法的整体概念是利用 OSGi Bundle 之间的显式导入和导出,在 dm Server 内部创建一个依赖项映射。对于像我们的 StockWatcher 示例这样的小型 WAR,这主要只是一项有趣的学术实践。然而,考虑到许多商业 Web 项目以大型 WAR 文件形式发布,其中打包了数十甚至数百个依赖的 Jar 文件,将这些依赖项分解为可共享资源不仅从空间占用角度来看有意义,而且还显著减轻了应用程序的打包、版本控制和维护的痛苦。

好消息是,创建这些依赖项的大部分工作已经有人为您完成了。SpringSource Enterprise Bundle Repository 包含了大多数常用库的“打包”版本。然而,在撰写本文时,我们的 GWT 依赖项是一个需要您自己将其转换为 Bundle 的库示例。幸运的是,Eclipse 3.4 使这个过程非常简单。

- 在“项目资源管理器”中右键单击,选择“新建”->“其他”->“插件开发”->“从现有 Jar 归档文件创建插件”。- 点击“添加外部”,然后浏览找到gwt-servlet.jar。- 点击“下一步”,并将项目命名为“com.google.gwt”。- 设置插件版本以反映 GWT 版本,在本例中是 1.5.3。- 选择“分析库内容并添加依赖项” - 选择 Equinox OSGi 框架

点击“完成”,无需切换到插件开发透视图。我们只是要直接将我们的 Bundle 再次导出。

您现在应该会看到生成的MANIFEST.MF文件,其中定义了 Bundle 的依赖项。您将在“运行时”选项卡中看到该工具已添加了所有com.google.gwt..包作为导出。您还将在“依赖项”选项卡中看到它已经识别出此 Bundle 需要一些javax.servlet..包以及一个junit包。

我们现在将移除 JUnit 依赖项,因为它在 dm Server 中默认将无法解析。当然,如果我们愿意,我们可以向 dm Server 添加一个 JUnit Bundle 来满足此依赖项,或者,我们可以通过添加以下内容使该依赖项成为可选的:required:=optional。选择junit.framework包,点击“移除”,然后保存。

值得指出的是,这是一种将 JAR 文件转换为 Bundle 的粗略方法,不能保证在所有情况下都能奏效。一方面,我们可能不想导出所有包。更重要的是,有一些源代码级别的陷阱可能在 OSGi 中表现不佳,例如使用Class.forName()。如有疑问,请务必首先查看 SpringSource Enterprise Bundle Repository,而不是尝试自己创建。

最后,我们需要将我们的插件项目导出为一个 OSGi Bundle。选择“导出”->“插件开发”->“可部署插件和片段”。选择一个方便的位置作为输出目录。

我们现在应该会在以下路径看到一个文件:<export path>/plugins/com.google.gwt_1.5.3.jar。下一步是将文件重命名,使其与 dm Server 中其他命名 Bundle 的格式一致:只需将下划线更改为破折号com.google.gwt-1.5.3.jar。如果不进行此更改,当前版本的 STS 将无法识别 Bundle 版本。如果您想跳过此步骤,可以从此处下载。

最后,将 Bundle 复制到 dm Server 的 Bundle 仓库中:<dm Server installation root>/repository/bundles/usr。这里的任何 Bundle 都可以成为 dm Server 中其他 Bundle 的依赖项,但重要的是它在 STS 中也将作为依赖项可见,正如我们在下一步中将看到的。

此时,您可以从工作区中删除 com.google.gwt 项目,因为它已经完成了它的使命。

步骤 2:将我们的 WAR 项目迁移到新的 OSGi Bundle

希望您现在已经有一个 dm Server 安装,并且其仓库中包含一个com.google.gwt-1.5.3.jar

下一步是“破坏”我们的应用程序!

右键单击 StockWatcherWar,选择“属性”->“Java EE 模块依赖项”,取消选中gwt-server.jar文件,然后点击“确定”。我们现在已经删除了 WAR 对 GWT 的显式依赖,给自己造成了许多新问题,其中大多数是编译器错误。

所以我们需要将我们的动态 Web 项目转换为一个 dm Server Bundle,以便它可以从我们新创建的 GWT Bundle 中显式导入所需的依赖项。为此,右键单击 StockWatcherWar,选择“Spring Tools”->“添加 OSGi Bundle 项目特性”。您应该注意到该项目现在带有一个非常重要的“S”符号,并且MANIFEST.MF也改变了字符。

它现在处于破损状态,因为它看起来不像一个 Bundle 清单。让我们添加一些合理的默认值(您可以复制粘贴以下内容,或使用选项卡中的字段)。请注意,您也可以在编辑器中使用 ctrl-space 来提示您有效的选项。

Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: com.google.sample.stockwatcher Bundle-SymbolicName: com.google.sample.stockwatcher Bundle-Version: 1.0.0 Bundle-Description: Shared Libraries StockWatcher demo

这样就解决了一个问题,我们的 Bundle 清单现在看起来正常了。然而,我们尚未定义对 GWT Bundle 的依赖,所以我们仍然有一些编译器错误。

选择“依赖项”选项卡并点击“添加”。在顶部,您应该会看到一个 com.google.gwt Bundle 神奇地出现了。现在,重要的是要明白,此列表中出现的 GWT Bundle 与我们在步骤 1 中创建的插件项目无关。如果您从工作区中删除该项目,该 Bundle 仍然会出现在列表中。这是因为 dm Server 安装被设置为项目的运行时环境,因此工具会在 dm Server 的仓库中查找您可能想要导入的任何 Bundle。如果这一步没有成功,则说明 GWT Bundle 没有被复制到正确的位置,或者 WAR 项目的运行时环境没有设置为 dm Server。请在“属性”->“目标运行时”中检查后者。

接下来,选择 GWT Bundle,点击“确定”,然后保存清单。您现在应该会看到 GWT Bundle 被添加到一个名为“Bundle Dependencies”的新类路径元素中。

您现在应该注意到大多数错误已经解决,但仍有几个未解决。这是因为我们的 GWT Bundle 依赖于javax.servletAPI,并且在我们获得这些类之前,类型层次结构是不完整的。

那么,为什么当我们之前只有gwt-servlet.jar在我们的构建路径中时,没有遇到这个问题呢?嗯,在我们进行更改之前,我们的 WAR 会拾取其构建路径上的所有内容,其中包括 dm Server 目标运行时提供的所有 JAR。然而,您可能已经注意到,当我们添加 OSGi Bundle 项目特性时,构建路径上的 dm Server JAR 文件消失了。这是因为,通过成为 OSGi Bundle,我们进入了一个不同的依赖关系世界,在这个世界里,导入和导出必须显式声明。

声明我们对javax.servletAPI 的依赖项很简单,只需导入另一个 Bundle。您将在列表中看到它,名称为com.springsource.javax.servlet。添加这两个 Bundle 后,保存清单,您现在应该会看到所有错误都已解决。太好了!

如果我们现在导航到 MANIFEST.MF 选项卡,我们可以看到我们所有点击操作的效果:清单中有一个绿色的“Import-Bundle”条目。请注意,我们也可以在“Import-Bundle”后面轻松使用 ctrl-space,它会建议所有可能的导入。

如果您想查看我的项目,可以从此处下载一个压缩副本。它包括在嵌入式 Tomcat 中以 hosted mode 启动、在 dm Server 中以 hosted mode 启动以及启动 GWT 编译器的运行时配置。请注意,使用嵌入式 Tomcat 的 hosted mode 可以正常工作,无需修改 Bundle WAR 项目。这是因为javax.servlet从 dm Server 获得的包不再位于构建路径中。要使用我的项目,您需要设置GWT_ROOT_INSTALL变量,并且您可能需要选择您的 dm Server 目标运行时实例,如第 1 部分中所述。

步骤 3:部署到 dm Server

部署我们的“共享库”应用程序的步骤与第 1 部分中的步骤 7 和 8 完全相同,因此没有必要再次详细介绍。当然,关键区别在于我们已成功地将依赖项分解为一个可以由多个 GWT 应用程序共享的库。如果您下载我的StockWatcher 共享库 WAR,您会看到它现在只有 290k,而不是 890k。

为了好玩,让我们看看幕后,瞧瞧我们的 Bundle 是如何交互的。

dm Server 启动时,会通知您其 OSGi 控制台可以通过 telnet 客户端访问:

[2008-10-27 16:48:04.266] main                     <SPOF0001I> OSGi telnet console available on port 2401.

让我们打开一个终端窗口,看看能发现什么:

> telnet localhost 2401 Trying ::1... Connected to localhost. Escape character is '^]'.

osgi>

您可以通过输入 help 来获取 Equinox 控制台可用的命令列表。还有一篇有用的 DeveloperWorks 文章,您可以在此处阅读。

输入ss可以列出所有正在运行的插件:

osgi> ss

Framework is launched.

id    State       Bundle . . . . 73    ACTIVE      com.google.sample.stockwatcher_1.0.0 74    ACTIVE      com.google.gwt_1.5.3

输入packages 74可以列出 GWT Bundle 中所有导出的包。它显示所有导出的包都被 StockWatcherWar Bundle 导入。这是我们使用“Import-Bundle”的结果。实际上,我们的 StockWatcherWar 唯一需要的包是com.google.gwt.user.client.rpccom.google.gwt.user.server.rpc。如果我们愿意,我们可以改用“Import-Package”更具选择性地显式导入这些包。这在dm Server 编程指南中有所描述。

file:////Users/bcorrie/dmServer-1.0.0/springsource-dm-server-1.0.0.RELEASE/work/com.springsource.server.deployer/Module/StockWatcherWar.war-0/StockWatcherWar.war [73] imports com.google.gwt.i18n.client.constants; version="0.0.0"<file:////Users/bcorrie/dmServer-1.0.0/springsource-dm-server-1.0.0.RELEASE/repository/bundles/usr/com.google.gwt-1.5.3.jar [74]>

步骤 4:在共享库上部署其他 GWT 应用程序

值得注意的是,并非所有 GWT 应用程序都需要使用gwt-servlet.jar。只有使用某种形式的远程处理的 GWT 应用程序才依赖于此库。Google 在其发行版中附带了许多示例应用程序,其中大多数仅生成 javascript 和 html,因此部署时没有 Java 代码。将这些示例转换为 WAR 文件很简单,只需按照第 1 部分步骤 5 所述,将编译脚本运行到动态 Web 项目中,然后创建一个合适的 web.xml 即可。

然而,GWT 发行版中包含的一个确实使用了简单远程处理的示例叫做 DynaTable。我使用这些博客中描述的相同步骤和原理,将此示例转换为一个共享库 WAR 文件。要了解我是如何做的,您可以下载压缩后的项目WAR 文件查看。这非常简单地演示了在 dm Server 中运行多个应用程序的原则,所有这些应用程序都共享我们创建的 GWT Bundle。

展望第 3 部分

在本系列的最后一篇博客中,我们将进一步模块化我们的 StockWatcher 示例,将其服务抽象为可以热插拔进出运行服务器的 Bundle。

订阅 Spring 新闻通讯

通过 Spring 新闻通讯保持联系

订阅

领先一步

VMware 提供培训和认证,助您加速发展。

了解更多

获取支持

Tanzu Spring 通过一个简单的订阅提供 OpenJDK™、Spring 和 Apache Tomcat® 的支持和二进制文件。

了解更多

即将举行的活动

查看 Spring 社区所有即将举行的活动。

查看全部