使您的应用程序调用我的应用程序,第1部分(上)

Apache Geronimo 通信基础 —— 开发、部署和测试(上)

Apache Geronimo 包含顶尖的消息传递实现,还有世界级的 Enterprise JavaBeans(EJB)实现,从而使您可在自己的应用程序中使用 消息驱动 bean(MDB)。这也就意味着只要具备恰当的资源适配器,其他组件就可以使用消息传递与您的应用程序交互。本系列教程详细 叙述了 Java™ Platform, Enterprise Edition(Java EE)应用程序的创建,在这个应用程序中,用户、管理员甚至其他应用程序 都可利用 MDB 进行交互,方法是向 Java Apache Mail Enterprise Server(也称为 Apache James)发送电子邮件,而 Apache James 随 后又使用 J2EE ConnecTor Architecture(JCA)资源适配器反过来与 Geronimo 通信。

开始之前

本系列教程面向希望学习如何使用各种 Java EE 组件(包括 MDB 和 JCA 资源适配器)构建集成化解决方案的 Java EE 程序员。本教程假设您熟悉基本的 Java 和 Java EE 概念,例如 EJB、Java Message Service(JMS)、MDB 和 Unified Modeling Language(UML)图。

关于本系列

在这个共分三部分的系列教程中,您将构建一个示例应用程序,通过这种方式了解如何将 不同的 Java EE 组件集成在一起,来开发复杂的应用程序。

您可 下载 本文的示例应用程序,它示范了 Apache James 中电子邮 件的数据是如何通过 JCA 资源适配器、MDB、EJB 流向 Apache Geronimo 应用服务器的。

本期是系列教程的第 1 部分,介绍了如何开发 MDB、实体 bean 和容器管理的持久性(CMP),以及如何在 Apache Geronimo 中部署 和测试这些组件。

第 2 部分将解释如何创建电子邮件应用程序(mailet 和 matcher)并将它们部署在 Apache James 电子邮件服务器中。

第 3 部分将整个应用程序联系在一起。您将学习为 Apache James 电子邮件服务器(它通过 MDB 与 James 和 Geronimo 交互)开发 、部署和测试 JCA 资源适配器。

关于本教程

本教程是共分三部分的系列教程中的第 1 部分,集中关注开发、部署和测试一个集成化 Java EE 应用程序的各种组件。该应用程序示 范了 Apache James 电子邮件服务器中一封电子邮件的数据是如何通过 JCA 资源适配器和 MDB 流向 Apache Geronimo 应用服务器的。

系统需求

为完成本教程的学习,您需要具备以下工具:

Apache Geronimo —— Apache 提供的 Java EE 应用服务器

Apache James 2.2 —— 基于 Java 的 Simple Mail Transfer Protocol(SMTP)、Post Office Protocol V3(POP3)和 Network News Transfer Protocol(NNTP)新闻服务器

Apache Derby 数据库 —— 开放源码、轻量级数据库,嵌入在 Geronimo 内,因此无需单独安装

Sun Microsystems 公司提供的 Java 1.4.2

示例源文件

在本教程的 下载 部分可获得 geronimo.mdb.part1source.zip 文件,其中包含源代码、EJB JAR 和本教程的描述符文件。下面详细列 出了 .zip 文件的组成部分:

– dds(包含描述符 xml 文件)

– deploy(po-ejb.jar)

– lib(examples.jar 和 tester.jar)

– src(mdb 和实体 ejb 的 Java 文件)

– deploy.cmd

– undeploy.cmd

– runtester.cmd

EJB 程序包

下面列出了 po-ejb.jar 文件的组成部分(部署在 Geronimo 中):

描述符文件:

– META-INF/ejb-jar.xml

– META-INF/openejb-jar.xml

类文件:

– examples/po/ejb/*.class

– examples/po/bean/*.class

– examples/po/mdb/*.class

基本应用程序

本教程的目的是介绍在 Apache Geronimo 上编写可轻松与其他基于 Java 的技术集成的 Java EE 应用程序的概念和基础知识。您将通 过使用 Java EE 组件开发一个示例应用程序来学会这些知识。

在本教程的场景示例和编码练习中,您将使用 Foo, Inc.,这是一家虚拟企业。尽管 Foo, Inc. 是虚拟的,但业务场景和示例都是真 实的。

Foo, Inc. 的遗留采购订单流程

让我们来看一下 Foo, Inc 的遗留采购订单(PO)流程。这是一个手工流程,员工填写采购申请单,将申请单提交给采购部门进行授权 、批准,最终将 PO 提交给厂商。

这看上去是一个效率极其低下、耗费时间的流程,可以通过软件进行自动化。因此,Foo, Inc. 决定购买一套新的 PO 系统(假设是一 种基于数据库的产品),实现将内部采购申请单提交给厂商的电子化通信。

新的采购订单流程

新的 PO 流程允许一名员工发送电子邮件请求,由 PO 经理授权进行电子化批准,从而自动创建 PO 并将其提交给厂商。

新流程详述如下:

员工通过电子邮件将采购请求发送给采购部门。

采购请求电子邮件由一个 Java EE 应用程序处理,该应用程序确保请求者是经过授权的。若授权流程成功完成,电子邮件将转发到指 定文件夹中,以便进一步处理。

Java EE 应用程序的另外一个组件轮询包含经过授权的请求的电子邮件文件夹,并在 PO 系统中创建一个新采购订单。

随后可在 PO 系统中检索采购订单并将订单发送给厂商。

应用程序设计

既然已经了解了 Foo, Inc 的新采购订单流程,那么下一步就是设计我们的 Java EE 示例应用程序,来实现新流程。

示例应用程序需求

您将从为示例应用程序收集需求开始。

应用程序需要处理来自员工的传入采购请求电子邮件,并将其移动到可由采购部门访问的特定文件夹。

随后,您的应用程序将读取请求,并检查该员工是否确实来自 Foo, Inc.。

一旦经过授权,即创建一份新的采购订单,以便提交给厂商。

这样,您确定了三个需求,现在您需要将其建模为用例。

用例分析

用例分析捕获整体需求,并描述示例应用程序的单个特性和功能性。它由一组用例和参与者构成。

用例

用例代表业务需求。在我们的例子中,也就是我们刚刚为 Foo, Inc. 的新采购订单流程确定出的需求:

发送采购请求电子邮件。需求:Foo, Inc. 的员工向采购部门发送带有采购请求的电子邮件。

处理采购请求电子邮件。需求:应用程序通过授权和将采购请求电子邮件移动到其他文件夹中来处理这些电子邮件。

检查采购请求电子邮件。需求:应用程序将连续检查等候处理的新采购请求电子邮件。

在 PO 系统中添加采购订单。需求:应用程序将在 PO 系统中添加新采购订单。

参与者

参与者是在应用程序或系统中承担一个角色的实体。参与者可以是人、组织或应用程序。以下列表包含了您的参与者:

Foo, Inc. 的员工

电子邮件应用程序

J2EE 应用程序

参与者与应用程序交互,因而也显示为与 图 1 中所示的用例交互。

图 1. 参与者和用例

现在您就可以为您的应用程序确定实现这些用例所必需的不同 Java EE 组件了。

组件

确定出您的应用程序中的用例之后,下一步就是将这些需求/用例建模为 Java EE 组件。图 2 中的组件图显示了各种组件及其交互作 用。让我们从详细了解应用程序流程和各应用程序组件在更高层面中的角色开始。

应用程序流程

Foo, Inc. 的员工发送一封采购请求电子邮件。这种电子邮件将由您的示例应用程序处理(组件:mailet 和 matcher),它将授权采 购请求,并将请求转发到属于采购部门的特定电子邮件文件夹中。您的应用程序(组件:JCA 适配器)将不断轮询新采购请求。一旦接收 到新的采购请求电子邮件,示例应用程序(组件:JCA 资源适配器和 MDB)将调用消息侦听器来异步处理采购请求。应用程序(组件:MDB 和 EJB)随后在 PO 系统(数据库)中创建一份新的采购订单。

下面列举了组件及其相应用例。您将分三部分构建这个应用程序(在本系列的三篇教程中):

实体 bean —— CMP:在 PO 系统中添加采购订单(第 1 部分)。

MDB:在 PO 系统中添加采购订单(第 1 部分)。

电子邮件客户机应用程序:发送采购请求电子邮件(第 2 部分)。

电子邮件应用程序:处理采购请求电子邮件(第 2 部分)。

JCA 资源适配器:检查采购请求电子邮件(第 3 部分)。

图 2. 组件图

通常,可以在完全不了解将用于部署的应用服务器的情况下设计和开发 Java EE 应用程序。

在开发、部署和测试过程中,您要在 Apache Geronimo 和 Apache James 中部署示例应用程序。您将使用 Apache Derby 作为 PO 系 统数据库。(在本教程开始处的 系统需求 部分中可找到下载这些产品的链接。)

既然您已经了解了应用程序流程和示例应用程序的设计,接下来就该动手开发第 1 部分的应用程序组件了。

第 1 部分的应用程序开发

在这里,您将为用例在 PO 系统中添加采购订单 开发组件。在组件设计中,您将使用 MDB 异步接收来自 JCA 资源适配器和实体 EJB (CMP)的采购请求,在 PO 系统中添加新采购订单。

您将调用您的 MDB PurchaseOrderMDB 和实体 bean(CMP)PurchaseOrderEJB。PurchaseOrderMDB 接收到一个采购请求之后,它将调 用 PurchaseOrderEJB 在数据中插入一个新的采购订单。

下面详细介绍了在 Geronimo 内对 MDB 和 CMP 实体 bean 的实现和部署。

实体 bean(PurchaseOrderEJB)

您要根据 EJB 2.1 规范将 PurchaseOrderEJB 作为 CMP 实体 bean 实现。实体 bean 代表持久数据,在本例中,它表示作为 PO 系统 的数据库内的一行。由于您使用的是 CMP,Java EE 容器会读/写数据库中的数据。

在示例应用程序中,PurchaseOrderEJB 与数据库表 PURCHASEORDER 交互,定义参见 清单 1。

清单 1. 表定义

TABLE PURCHASEORDER (  PURCHASEORDERNUM  VARCHAR(30) PRIMARY KEY,  ITEM        VARCHAR(30) NOT NULL,  DESCRIPTION     VARCHAR(255),  UNITPRICE       INTEGER,  QUANTITY      INTEGER,  REQUESTorEMAIL   VARCHAR(30) NOT NULL ) 

接下来您将实现处理采购订单的 EJB。

实现 PurchaseOrderEJB

EJB 2.1 规范要求实体 bean 必须具有一个 主接口、一个远程接口、一个 bean 类和一个部署描述符。本节介绍以下所有必备 EJB 接口和类的实现。

EJB 主接口 —— RemotePurchaseOrderHome.java(请参见 清单 2)

EJB 远程接口 —— RemotePurchaseOrder.java( 请参见 清单 3)

EJB bean 类 —— PurchaseOrderEJB.java(请参见 清单 4 和 清单 5)

描述符 —— ejb-jar.xml 和 openejb-jar.xml(请参见 清单 6 和 清单 7)

可在 $part1.home/src/examples/po/ejb 目录 下找到 PurchaseOrderEJB 的源文件(.java),两个描述符文件均位于 $part1.home /dds 目录中。

EJB 主接口

EJB 主接 口用于在服务器中创建、删除或查找 bean 实例。这个接口在部署期间绑定到服务器 Java Naming and DirecTory Interface(JNDI)树 中。在运行时,客户机程序查找 JNDI 中的主接口来使用此 EJB。RemotePurchaseOrderHome 是 PurchaseOrderEJB 的主接口,它按照 EJB 2.1 规范实现了 EJBHome(参见 清单 2)。

清单 2. PurchaseOrderEJB 的主接口

public interface RemotePurchaseOrderHome     extends EJBHome, Remote {     public RemotePurchaseOrder create(String purchaseOrderNum,                      String item,                      String description,                      Integer unitPrice,                      Integer quantity,                      String requesTorEmail)     throws CreateException, RemoteException;     public Collection findByRequesTorEmail(String requesTorEmail)         throws FinderException, RemoteException;     public RemotePurchaseOrder findByPrimaryKey(String poNum)         throws FinderException, RemoteException;}

您有一个 create() 方法,它实际上是一个 bean 实例,在 PURCHASEORDER 表中插入新记录。存在两个 finder 方法:

findByRequesTorEmail() 返回指定请求者电子邮件为 REQUESTorEMAIL 的采购订单行的集合。

findByPrimaryKey 返回带有特定主键的一行。

EJB 远程接口

EJB 远程接口(参见 清单 3)为实体 bean 定义业务方法。远程接口中的这些方法应定义为公共方法,以使其可被远程客户机程序所 访问。RemotePurchaseOrder 是 PurchaseOrderEJB 的远程接口。

清单 3. PurchaseOrderEJB 的远程接口

public interface RemotePurchaseOrder     extends EJBObject, Remote {     public String getPurchaseOrderNum()         throws RemoteException;     public void setPurchaseOrderNum(String purchaseOrderNum)         throws RemoteException;     public String getItem()         throws RemoteException;     public void setItem(String item)         throws RemoteException;     public String getDescription()         throws RemoteException;     public void setDescription(String description)         throws RemoteException;     public Integer getUnitPrice()         throws RemoteException;     public void setUnitPrice(Integer unitPrice)         throws RemoteException;     public Integer getQuantity()         throws RemoteException;     public void setQuantity(Integer quantity)         throws RemoteException;     public String getRequesTorEmail()         throws RemoteException;     public void setRequesTorEmail(String requesTorEmail)         throws RemoteException;}

RemotePurchaseOrder 为数据库表 PURCHASEORDERSYSTEM 中的列定义了 getters 和 setters。

EJB bean 类

EJB bean 类实现远程接口,并为远程接口中定义的方法提供实际实现。了解此 EJB 是如何作为 CMP 部署之后,您不必实现 getter 和 setter 方法,而是将其定义为抽象方法(参见 清单 4)。

清单 4. PurchaseOrderEJB bean 类的 getter 和 setter 方法

public abstract class PurchaseOrderEJB      implements EntityBean {     private EntityContext context;     // Access methods for the CMP Fields.     public abstract String getPurchaseOrderNum ();     public abstract void setPurchaseOrderNum (String     purchaseOrderNum);     public abstract String getItem ();     public abstract void setItem (String item);...

create() 方法(参见 清单 5)在 PURCHASEORDER 表中插入一个带有特定参数值的新行。

清单 5. 主接口中定义的 bean 类内的 create() 方法实现

private String create (String purchaseOrderNum,                String item,                String description,                Integer unitPrice,                Integer quantity,                String requesTorEmail)         throws CreateException {         setPurchaseOrderNum(purchaseOrderNum);         setItem(item);         setDescription(description);         setUnitPrice(unitPrice);         setQuantity(quantity);         setRequesTorEmail(requesTorEmail);         return purchaseOrderNum;     }

下面定义 EJB 描述符。

定义 EJB 描述符

既然已经创建好了远程接口、主接口和 bean 类,那么接下来就该转向部署描述符了。您将定义两个描述符:根据 EJB 2.1 规范定义 的 ejb-jar.xml 以及在 Geronimo 中部署 EJB 所必需的 openejb-jar.xml。如 清单 6 所示,ejb-jar.xml 定义主接口、远程接口和 bean 类。请注意,持久性类型为 Container。

清单 6. ejb-jar.xml 片段

<ejb-jar version="2.1"   xmlns="http://java.sun.com/xml/ns/j2ee"   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   xsi:schemaLocation="http://java.sun.com/xml/ns/j2eePurchaseOrderEJBPurchaseOrderEJB;PurchaseOrderEJB;examples.po.ejb.RemotePurchaseOrderHome;examples.po.ejb.RemotePurchaseOrder;examples.po.ejb.PurchaseOrderEJBContainer;;java.lang.Stringfalse2.xPurchaseOrderpurchaseOrderNumitemdescription...purchaseOrderNumReference to Datasource in Geronimo.jdbc/PurchaseOrderDataSourcejavax.sql.DataSourceContainer

EJB 描述符 ejb-jar.xml 为一个 EJB 定义了 CMP 字段(参见 清单 7)。这些字段对应于 PurchaseOrderEJB 将与之交互的底层数据 库表的列。您还在 resorce-ref 部分中定义了绑定在服务器 JNDI 中的数据库数据源(jdbc/PurchaseOrderDataSource)。

描述符中定义的两个 finder 方法是:

findByPrimaryKey,给定采购订单号,返回采购订单。

findByRequestoEmail,返回一个特定电子邮件地址所请求的采购订单集合。

ejb-jar.xml 用 EJB Query Language(EJBQL)为上述 finder 方法定义查询。

清单 7. 在 ejb-jar.xml 中为 findByXXX() 方法定义查询

findByRequesTorEmailjava.lang.StringLocalSELECT OBJECT(p) FROM PurchaseOrder AS p WHERE  p.requesTorEmail = ?1findByPrimaryKeyjava.lang.StringLocalSELECT OBJECT(p) FROM PurchaseOrder AS p WHERE  p.purchaseOrderNum = ?1

随 Java EE 描述符 ejb-jar.xml 一起,您还必须为 Geronimo 定义一个 EJB 描述符(openejb-jar.xml),如 清单 8 所示。

清单 8. openejb-jar.xml 片段

  PurchaseOrderDataSource        PurchaseOrderEJB     PurchaseOrderEJB     PurchaseOrder             purchaseOrderNum        purchaseOrderNum                   item         item     ...   purchaseOrderNum

清单 8 中的这个描述符定义了 JNDI 名称(PurchaseOrder),该名称用于将 EJM 主接口绑定到服务器 JNDI 树中。 应与 ejb-jar.xml 中使用的名称匹配。Java EE 服务器将使用该描述符中定义的表名和 cmp 字段来执行数据库操作。

时光的消化是这样的缓慢。虽然这也仅仅是无处可说的委屈。而不是痛苦。

使您的应用程序调用我的应用程序,第1部分(上)

相关文章:

你感兴趣的文章:

标签云: