扫一扫
分享文章到微信
扫一扫
关注官方公众号
至顶头条
在学习示例应用程序之前,首先要详细了解一下将用于创建和部署该应用程序的三种技术。
Maven 是用于构建 Java 应用程序的应用程序,它从源代码一直构建到打包至 Web 站点。Geronimo 应用服务器是使用 Maven 应用程序的构建系统构建的。在它的核心内,Maven 的可扩展框架允许创建模块以执行构建软件组件时需要的一些动作。谈到 Apache Ant(Java 构建工具),Maven 的动作已经与使用 UNIX 命令 make 生成的结果进行了比较。Maven 脚本还允许将应用程序自动部署到正在运行的 Geronimo 服务器中。本文展示了如何使用 Maven 来将源代码打包到完整的 Java 2 Platform, Enterprise Edition (J2EE) 企业应用程序中。
Struts 是基于 Model 2 架构的 Web 应用程序框架。该混合架构最大程度地将业务逻辑和显示逻辑隔离。Struts 通过将业务逻辑隔离到纯 Java 类中来实现这一目标,纯 Java 类操作数据,并提供了丰富的标记库,该库可用于在编写 JavaServer Pages (JSP) 时显示数据。(在无数可用的 Web 开发框架中,许多框架都有很高的人气,比如 Tapestry 和 JavaServer Faces,而 Struts 一直是我偏爱的一个。)
XDoclet 起源于存在已久的 Java 文档工具 Javadoc。XDoclet 开发人员最初以一种新颖的方法使用 Javadoc,即使用专门的注释来生成可以被编译成源代码的模型。他们从 Javadoc 的实际使用抽身而出,生产出他们自己的变种,叫做 Xjavadoc。但是在 XDoclet 早期,仍然需要在源代码的 Javadoc 注释中使用标记来自动生产自动生成的代码。编写 J2EE 应用程序中的源代码和部署描述符文件可能十分冗长乏味。对于每一百行左右的 Java 代码,预计可以生成至少三倍多的支持 J2EE 描述符代码来完成工作。为了大大减少开发多层企业应用程序的痛苦,XDoclet 伸出援手,提供了代码标记和代码生成以使大部分部署描述符自动生成。这样就隐藏了 J2EE 的大量复杂性,但您要清楚代码生成器在构建什么,因为当事情没有如期进行时,您必须检查它。
部署计划
正如在简介中提到的,Maven 被 Geronimo 团队用来构建整个应用服务器。您可以利用 Maven 构建工具的强大功能来编译应用程序源代码,执行代码生成(需要 XDoclet 的帮助),绑定企业应用程序模块,并最终将其部署到正在运行的 Geronimo 服务器中。
该过程以三个文件开始。第一个文件 project.xml 定义什么是所谓的项目对象模型(Project Object Model,POM)。它列出应用程序的相关信息,包括应用程序名称、编写者、版本号、对构建应用程序非常重要的依赖关系,以及对如何构建应用程序的概述。出于本文目的,我们将主要研究 POM 的依赖关系部分。
上的指定资源库中下载构建应用程序所需的工件。iBiblio就是这样一个资源库,它包含数百个开放源代码 Java 库和支持文件,以及这些库的 POM 信息。它收集了大量信息,目的只有一个,即简化应用程序构建过程中 Java 开发人员的生活。电话簿应用程序 project.xml 文件的依赖关系部分由 23 个依赖关系组成,其中一半是 .jar 文件,用于支持 Struts 和 DisplayTag 标记库。其余的依赖关系主要是 XDoclet 要求。不使用 Maven,每个依赖关系都需要与示例应用程序绑定在一起。
Maven 只在第一次编译应用程序时下载所有依赖的工件。以后的编译运行利用 Maven 的本地资源库(已下载工件的本地高速缓存,通常位于 $HOME 目录的 .maven 目录下)来获得工件。
安装 Maven 1.0.2
我们的示例应用程序需要对 Maven 进行一些初始设置以使一切正确工作。首先,需要安装 Maven 1.0.2(参阅 参考资料 中的 Maven Web 站点链接)。安装完成后,在命令行输入 maven,将看到类似如下的信息:
E:\Documents and Settings\Neal\My Documents\eclipse\workspace\Phonebook> maven
__ __
| \/ |__ _Apache__ ___
| |\/| / _` \ V / -_) ' \ ~ intelligent projects ~
|_| |_\__,_|\_/\___|_||_| v. 1.0.2
编译 Geronimo
然后,按照 Wiki上的指令从源代码编译 Geronimo。成功构建之后,Geronimo 工件将位于本地资源库。它们是构建示例应用程序所必需的。编译完 Geronimo 之后,查找 geronimo-deployment-plugin-1.0-SNAPSHOT.jar 文件,并将其安装到 $MAVEN_HOME/plugins 目录中,否则可能会看到如下消息:
Tag library requested that is not present: 'geronimo:deploy' in plugin: 'null'
安装 XDoclet 1.2.3
最后,需要将 XDoclet 1.2.3 安装到 Maven 资源库中。如果试图构建示例应用程序,会显示一条 Maven 消息,指明它无法找到一些其他的 XDoclet 1.2.3 工件,这时您可能需要下载 XDoclet 1.2.3(lib 包),并将 .jar 文件解压到位于 .maven/repository/xdoclet/jars 的本地 Maven 资源库中。如果您是 Windows 用户,应该在 C:\Documents and Settings\username 目录下查找该 Maven 目录。如果您是 UNIX 用户,应该在主目录下查找该目录。还应该通过将 maven-xdoclet-plugin-1.2.3.jar 添加到 $MAVEN_HOME/plugins 目录来安装 XDoclet Maven 插件。
成功构建
具备这些先决条件之后,构建过程应该能够顺利进行。当然,可以在进行上述工作之前尝试构建,查看 Maven 找不到哪些文件,然后只安装这些文件。可以尝试在示例应用程序的顶层目录中运行 Maven。首先,许多工件将被下载,最后,您将看到 BUILD SUCCESSFUL 消息。
要更多了解 Maven 做什么,研究一下 maven.xml 和 project.properties 文件。Maven 是面向目标的。它读取 maven.xml 并尝试满足顶层项目元素的默认属性中指定的所有目标。在本例中,它尝试满足部署目标指明为先决条件的所有事项;也就是说,它将尝试构建 .ear 文件,然后尝试停止和启动应用程序。应用程序的部署是通过上述的 Geronimo Deployment Maven 插件执行的。当然,Geronimo 服务器应该运行 —— 否则构建将失败,并显示如下消息:
Failed to retrieve RMIServer stub: javax.naming.ServiceUnavailableException
[Root exception is java.rmi.ConnectException: Connection refused to
host: 10.0.0.7; nested exception is:
java.net.ConnectException: Connection refused: connect]
如果收到该消息,使用如下命令启动 Geronimo 服务器:
E:\geronimo-snapshot>java -jar bin\server.jar org/apache/geronimo/
DebugConsole org/apache/geronimo/RuntimeDeployer
该命令告诉 Geronimo 启动其服务器、DebugConsole 应用程序配置和 RuntimeDeployer 配置。
调试控制台
DebugConsole(参见 图 1)是可选组件,允许通过一个小 Web 应用程序(参阅 http://localhost:8080/debug-tool)查看 Geronimo 服务器中在运行什么。
图 1. Geronimo DebugConsole
示例顶层页面
构建完示例应用程序之后,可以访问它的顶层页面,您将看到如 图 2 所示的内容。
图 2. 使用 Struts 1.2.7 构建的 Geronimo Phonebook 示例应用程序
完成应用程序构建之后:底层内容
既然已经成功构建了应用程序,而且看到了使用 Maven 的强大功能和简单性,现在该学习使它完全运行所必需的文件布局和部分代码。首先介绍 Web 应用程序层,然后介绍 EJB 层。最后将查看结缔组织 —— 连接所有组件并让它完全工作的部署计划和配置文件。参考 图 3 所示的文件目录树,以便找到这些文件。
图 3. 示例应用程序文件的目录布局
Web 应用程序层
Web 应用程序是用 JSP 和 Struts 1.2.7 框架编写的。在 src/webapp 目录中将会找到组成示例应用程序 Web 接口的文件。
该应用程序由两个主要视图组成:电话号码列表和电话号码编辑屏幕。从数据库中编辑简单记录必需的所有特性(添加、删除、编辑、更新和列表)都存在。src/java/org/acme/phonebook/struts 目录包含大量 Struts 动作来执行这些必需功能,比如创建新条目,删除条目,列出所有条目,编辑现有条目。
Struts Tiles 模板系统用于确保花费在让 Web 应用程序外观正常上的工作最少。webapp/pages 目录中的 site-template.jsp 文件定义应用程序的外观。
接下来介绍的两个主要 JSP 是 EditPhoneNumberPage.jsp 和 ListPhoneNumbersPage.jsp。其中每个页面及其在应用程序中的功能在 清单 1 中说明。
清单 1. EditPhoneNumberPage.jsp
<%@ page language="java"%>
<%@ taglib uri="/tags/struts-bean" prefix="bean"%>
<%@ taglib uri="/tags/struts-html" prefix="html"%>
<%@ taglib uri="/tags/struts-tiles" prefix="tiles"%>
<tiles:insert page="/pages/site-template.jsp" flush="true">
<tiles:put name="content" type="string">
<hr>
<h1><bean:message key="h1.EditPhoneNumberPage" /></h1>
<hr>
<h2><bean:write name="phoneBookEntryForm" property="action"/>
</h2>
<html:form action="/pages/SaveEntry.do">
<table>
<tr>
<td>
<bean:message key="prompt.EditPhoneNumberPage.name" />
</td>
<td>
<html:text property="name" size="40" />
</td>
</tr>
<tr>
<td>
<bean:message key="prompt.EditPhoneNumberPage.phoneNumber" />
</td>
<td>
<html:text property="phoneNumber" size="40" /></td>
</tr>
<html:hidden property="action" />
<html:hidden property="pk" />
<tr>
<td></td>
<td>
<html:submit>
<bean:message key="button.submit" />
</html:submit> <html:reset>
<bean:message key="button.reset" />
</html:reset>
</td>
</tr>
</table>
</html:form>
</tiles:put>
</tiles:insert>
在 清单 1 中,前几行设置将在页面中处于活动状态的标记库。它们还将站点模板的内容区域设置为内容将显示的位置。本例展示了一个简单的基于 Struts 的输入屏幕,它用一些
如果查看 清单 2 中 SaveEntry.java 源文件中的类 Javadoc 标记,将会看到该应用程序中 XDoclet 标记的第一个示例。这些标记定义生成 Struts 部署描述符 struts-config.xml 必需的所有属性。
清单 2. SaveEntry.java Javadoc 类标记
/**
* Save an Entry
*
* @struts.action
* name = "phoneBookEntryForm"
* path = "/pages/SaveEntry"
* scope = "request"
* input = "/pages/EditPhoneNumberPage.jsp"
* unknown = "false"
* validate = "false"
* @struts.action-forward
* name = "success"
* path = "/pages/ListNumbers.do"
* redirect = "true"
*/
该示例代码位于一个 nutshell 中,展示了 /pages/SaveEntry 动作从 EditPhoneNumberPage.jsp 中获取输入,并使用 phoneBookEntryForm 将来自页面的用户输入打包到 Java 代码中。该动作完成之后,它重定向到 /pages/ListNumbers 动作以显示号码列表。
ListNumbers 动作位于 ListNumbers.java 文件中,它使用 清单 3 中的代码调用名为 PhoneBookSession 的 Session EJB。
清单 3. ListNumbers 动作的 execute() 方法的代码段
PhoneBookSessionLocal session =
PhoneBookSessionUtil.getLocalHome().create();
// Call the method
Collection c = session.listEntries();
// Put the retrieved information into the request attributes
// so the page can render them.
request.setAttribute("numbers", c);
在 清单 3 中,可以看到 PhoneBookSessionUtil 类的使用。它是一个 XDoclet 生成的类,用于帮助获得 PhoneBookSession 对象的主接口。创建了一个会话,调用了它的 listEntries() 方法,该方法返回所有电话簿条目的集合。然后请求对象中的 numbers 属性被设置为该集合。这样做的效果是将电话号码放到指定位置,以便用于显示条目的 JSP 可以检索并写出列表,如 清单 4 所示。
清单 4. ListPhoneNumbersPage.jsp 中的 DisplayTag
<display:table name="numbers" requestURI="ListNumbers.do"
scope="request" pagesize="5" id="row_obj">
<display:column property="name" title="Name"/>
<display:column property="phoneNumber" title="Phone"/>
<display:column title="Actions">
<logic:present name="row_obj">
<html:link action="/pages/EditEntry"
paramId="id" paramName="row_obj"
paramProperty="name">Edit</html:link>
<html:link action="/pages/DeleteEntry"
paramId="id" paramName="row_obj"
paramProperty="name"
onclick="return confirmDelete('Number')">
Delete
</html:link>
</logic:present>
</display:column>
...
</display:table>
EJB 层
该应用程序中有两个 EJB 类。第一个类使用容器管理持久性(Container-Managed Persistence,CMP)来提供对简单数据库表 PhoneBookEntryBean 的基于对象的访问。第二个类是一个 Stateless Session bean,它提供业务逻辑。通常需要通过无状态会话 bean 来操作 CMP bean,因为会话 bean 可被设置来提供对数据库的事务处理,从而在发生错误时可以回滚更新。此外,在 Session bean 中执行所有 CMP 操作使得 Web 应用程序无需知道数据库访问层的任何实现细节。所以如果用另一种技术替换该层(比如使用 Hibernate 持久层),将无需更改 Web 应用程序中的代码。
XDoclet 主要用在 EJB 层中以提供部署描述符生成。这对于减少构建此类应用程序所需的维护工作是十分重要的。下载源代码并查看 PhoneBookEntryBean.java 和 PhoneBookSessionBean.java 的类 Javadoc 注释,以了解用于定义 EJB 类的大量 XDoclet 标记。
要生成无状态会话 bean 的方法,添加名为 @ejb.interface-method 的 XDoclet 标记,其视图类型属性可以为 local、remote 或 both。该属性告诉 XDoclet 在会话 bean 的本地接口、远程接口或两种接口中生成相应方法。您还可以控制事务处理类型。参见 清单 5,它是其中一个接口方法的示例,列出电话簿条目并返回它们的值对象表示。
清单 5. PhoneBookSessionBean.java 类的 listEntries() 方法
/**
* List all of the phone book entries.
* @return a collection of PhoneBookEntryValue objects.
*
* @ejb.interface-method view-type="both"
* @ejb.transaction type="Required"
*/
public java.util.Collection listEntries() {
ArrayList values = new ArrayList();
try {
Collection entries = PhoneBookEntryUtil.getLocalHome().findAll();
Iterator i = entries.iterator();
while(i.hasNext()) {
PhoneBookEntryLocal entry = (PhoneBookEntryLocal)i.next();
values.add(entry.getPhoneBookEntryValue());
}
} catch (Throwable ex) {
ex.printStackTrace();
}
return values;
}
结缔组织
在我的文章“将数据库连接到 Geronimo 应用服务器的三种方法”(developerWorks,2005 年 6 月)中详细介绍了 Geronimo 的各种部署计划的重要性。让这么小的应用程序到达功能状态是非常有挑战性的,需要在部署计划中提供许多小选项,还有 XDoclet 标记之间的交互、标记生成的代码,以及部署计划。但是详细介绍这些内容超出了本文的范围。示例程序有许多配置文件和部署计划,几乎所有的这些东西都能在项目的 src/resources 子树中找到。下文简要介绍了这些文件的相关细节,以说明需要进行哪些修改才能让将来的应用程序工作。
ear 子目录包含企业应用程序部署描述符、application.xml 文件和 geronimo-application.xml 文件。在该应用程序中,这些文件被配置以提供应用程序范围的 Java 数据库连接 (Java Database Connectivity, JDBC) 连接器。
如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。
现场直击|2021世界人工智能大会
直击5G创新地带,就在2021MWC上海
5G已至 转型当时——服务提供商如何把握转型的绝佳时机
寻找自己的Flag
华为开发者大会2020(Cloud)- 科技行者