目前全球最大的软件服务商IBM在实施SOA方面一直处于领先地位,因此通过介绍如何用IBM的服务总线产品来构建SOA架构更能使开发人员把握SOA的精髓。
服务总线是实现SOA架构最核心的部分,它将对系统内部或者外部的各种服务进行集成和管理。目前IBM基于企业服务总线的产品有Websphere 6.0上自带的SIBus(Service Integration Bus),由于它已经和Websphere集成在一起,从而使它可以应用Websphere所带有的各种资源。从对这个产品的实施中,我们可以观察到它实施服务总线(ESB)架构的技术思路主要是通过消息引擎来驱动的。SIBus比BEA的Aqulogic服务总线多了一个消息层,也就是读者将要在下面章节中看到的目标(Destination),SIBus是通过消息队列Queue或者Topic等来实现的。这是服务总线中最核心的部分,笔者十分看好其技术前景。本章将通过一个网上书店的实例详细介绍如何基于IBM Websphere SIBus服务总线来构建SOA架构体系的整合应用,包括下面几个方面的内容。
l 基于企业服务总线SIBus构建SOA架构的基本原理。
l 创建网上书店系统的DB2数据库。
l 创建网上书店系统的组件架构。
l 创建网上书店的实际业务流程和实现步骤。
l 在Websphere RAD创建网上书店的业务模型服务组件,包括Session Bean、CMP和Web Service。
l 详细介绍构建基于SIBus服务总线的SOA架构的服务集成系统的步骤。
l 介绍如何实现服务总线和Struts用户界面的集成。
8.1 基于企业服务总线SIBus构建SOA架构的基本原理
以服务为导向的SOA架构体系是通过业务来驱动的,它通过业务来驱动服务,再通过服务来驱动技术。所以本节首先介绍网上书店系统的业务需求;接下来介绍网上书店系统的SOA实施,主要是如何划分服务接口,以形成一个松散耦合的服务系统;进一步介绍本章基于企业服务总线SIBus实现SOA的架构体系。
8.1.1 网上书店系统业务需求
网上书店系统的业务需求如下。
有一家大型的图书公司希望进一步拓宽市场渠道,计划创建一个自己的网上书店。这个网上书店主要包括下面几个子系统功能,下面从大的方面来介绍一下此网上书店系统应有的功能。
l 注册系统:用于用户注册和保存用户信息。
l 登录系统:用于用户登录校验。
l 用户查询系统:用来查询系统里有多少用户。
l 账户管理系统:用于管理用户资金账户,以便实现网上交易。
l 图书管理系统:用来管理系统中的图书,主要是往系统里加入新书。
l 图书查询系统:提供界面以便用户查询系统中的图书。
l 购物车:创建用户购书订单,实现实时网上交易。
上面只是这家图书公司对网上书店的功能需求,这家公司的其他业务考量如下。
公司下面有3个独立的部门:图书管理部门、客户管理部门和销售部门。每个部门都有自己一套独立的IT系统,提供自己独立的服务。
从成本和时间的角度出发,这家公司希望能重用3个部门的业务服务系统,将它们整合起来,快速创建一个网上书店。
1、由于内部工作业务的关系,每个部门的服务器的地址可能会经常发生改变;
2、另外也由于内部业务工作的需要,每个部门服务接口方法的名字可能会发生变化,以客户身份校验为例,checkUser可能会变为checkLogin等。处于业务工作的需要,
3、这3个部门要隐藏自己的服务器的地址和传输协议,不希望被外部用户直接访问到。
最后所开发成功的网上书店系统如图8-1所示。
图8-1 网上书店系统主页
8.1.2 网上书店系统SOA实施
SOA实施规划是最关键的。SOA的出发点完全不同于现有的建模和组件的出发点。现有的软件实施也是实际项目中的真实情况:软件的设计和开发人员在拿到业务需求后,马上就会想到是不是需要用到Struts的MVC架构,是用EJB还是用Hibernate,要不要用Web Service,使用什么样的应用服务器和数据库的服务器等。然后开始建模,并用程序设计语言和业务分析人员进行沟通,最后将业务需求分散为很多业务小点,一个个塞到各个软件模块之中。这样使得业务处于一个从属于技术和模块之下的地位,业务和技术紧紧地捆绑在一起。
笔者在做项目的过程中,深深体会到各个企业其实非常不希望自己的业务和技术紧密地捆绑在一起,它们非常担心它们所选中和应用的技术一旦被淘汰了,它们的业务服务也会跟着被淘汰。
SOA实施是以业务服务为导向的,业务服务独立于技术之上,技术处于从属的地位,一旦某种技术过时了,企业的业务服务可以很快切换到其他新的技术。
下面是SOA实施的过程。
(1)根据前述的SOA的第一个标准,结合图书公司的现状,将网上书店的业务分散为3个松散耦合的子服务系统,每个子服务系统中的服务可以是相关的,但是每个子服务系统之间完全是松散耦合的。实际上是3个服务接口:图书管理服务接口、客户管理服务接口和订单服务接口。
(2)从现有的3个部门的IT服务中整理和归纳出相应的服务接口方法,根据前述的SOA的第二个标准,这些服务接口的方法是粗粒度的,最接近实际业务服务本身。如图8-2所示为服务接口方法划分。
图8-2 根据SOA的要点划分服务接口
上面已经完成了本章8.1.1节所述的SOA的两个要点,剩下的要点3就需要服务总线来完成了。
8.1.3 基于企业服务总线SIBus的网上书店SOA架构
在完成上面的SOA实施的两步后,包括一系列的数据库开发、EJB开发后(后面章节将详细介绍),3个服务接口将会产生3个独立的服务接口,独立运行。具体来说,这里将产生3个独立的Web Service服务接口;然后将3个服务接口加入SIBus服务总线,通过服务总线将3个服务接口进行整合,所有的网上书店业务请求都通过Struts Web应用发给服务总线,服务总线根据不同请求,进行相应的转换;再判断不同的请求类型,路由给不同的服务接口。
这里的3个服务的具体实现、位置和传输协议对调用者来说都是透明的,因为前台Web应用只和SIBus服务总线打交道,把所有的业务请求都发给服务总线SIBus,完全不知道这3个具体服务的存在。此外SIBus还提供了消息中介的功能,如有需要,可以对输入参数、传输协议和返回结果根据业务需要进行相应的修改。这样就实现了前述的SOA要点3。
此外,SIBus可以对所提供的服务进行监控和监听,并能以入站服务的方式将所提供的服务以标准的Web Service发布给外部,并提供相应的WSDL文件。图8-3是本章实例所实现的基于企业服务总线SIBus的网上书店SOA架构。在这个架构中,服务总线上的端点监听器将负责接收所有的客户端的请求消息,它实际上是对应的入站服务。由于事先在创建入站服务时已经创建了它在接收请求消息时所要转向的目标,所以请求消息将会转给目标,目标在收到请求消息后通过事先创建的默认转发路由路径转给出站服务,出站服务将请求消息转给外部服务提供者,这里的外部服务接口分别为图书管理服务接口、用户管理服务接口和订单管理服务接口。具体的实现步骤后面将会详细介绍。
图8-3 本章实例所实现的SOA服务总线架构图
以上已经完成了网上书店系统的SOA实施和架构,下面的章节将详细介绍具体的实现过程。
8.2 创建网上书店系统的DB2数据库
数据库是网上书店系统的底层,用来持久保存网上书店系统的所有相关业务数据,这里首先予以介绍。
8.2.1 创建数据库的脚本
下面是在DB2创建数据库表结构的详细脚本,参见如下代码。
-- 1. 创建表bk_sequence_num,用来保存其他表的最大主键值
create table bk_sequence_num (
index_num int not null default 0, --主键,必填,默认值为0
max_user_id int not null default 0, --最大用户序号,必填,默认值为0
max_book_id int not null default 0, --最大图书序号,必填,默认值为0
max_account_id int not null default 0, --最大用户账户序号,必填,
默认值为0
max_order_id int not null default 0, --最大购书序号,必填,默认值为0
--定义index_num为主键
constraint pk_sequence_num primary key (index_num)
);
-- 2. 初始化表bk_sequence_num
insert into bk_sequence_num values(0,0,0,0,0); --将所有的初始序号都定为0
-- 3. 创建表book,用来保存图书的信息
create table book (
book_id int not null default 0, -- 图书号,必填,默认值为0
book_name varchar(50) not null, -- 图书名,必填
book_content varchar(750) not null, --图书内容,必填
author varchar(20), --笔者
press varchar(50), --出版社
publishing_date date, --出版日期
ISBN varchar(20), --书号
unit_price decimal(10,2) not null, --单价
discount decimal(10,2) not null, -- 折扣
constraint pk_book primary key (book_id) -- 定义book_id为主键
);
-- 4. 创建表order,用来保存用户每次购买的总的信息,如总价等
create table order (
order_id int not null default 0, --购书号,必填,默认值为0
userid int not null, --用户号,必填
total_price decimal(10,2), --总价
tax decimal(10,2), --税
order_time timestamp, --购买时间
constraint pk_order primary key (order_id), --定义order_id为主键
-- 定义userid为外键
constraint fk_order foreign key (userid) references user (userid)
)
-- 5. 创建表order,用来保存用户每次购书的购书明细,每一种书的购买情况
create table order_item (
order_item_num int not null, -- 购书明细号,必填
order_id int not null, -- 购书号,必填
book_id int not null, -- 图书号,必填
quantity int not null, -- 对应于一种书的数量,必填
discount decimal(10,2), --对应于一种书的折扣
--定义order_id和order_item_num为主键,此表有多个主键
constraint pk_order_item primary key (order_id,order_item_num),
constraint fk_order1 foreign key (order_id) references
order(order_id), --定义外键
--定义外键
constraint fk_order2 foreign key (book_id) references book(book_id)
)
-- 6. 创建表user,用来保存用户的相关信息
create table user (
userid int not null, --用户号,必填
login_name varchar(20) not null, --用户登录名,必填
name varchar(50) not null, --用户姓名,必填
password varchar(20), --用户登录密码
phone varchar(20), --用户的电话号码
email varchar(20), --用户email
createTime TIMESTAMP default current timestamp,-- 用户的创建时间
constraint pk_user primary key (userid), -- 定义userid为主键
);
-- 7. 创建表account,用来保存用户账户的相关信息
create table account (
accountid int not null, -- 账户号
userid int not null, -- 用户号
registrationFee decimal(10,2) not null,-- 注册费、资金结余
--定义accountid和userid为主键,此表有多个主键
constraint pk_account primary key (accountid,userid),
--定义外键
constraint fk_account foreign key (userid) references user (userid)
);
8.2.2 数据库的ERWin数据库图
ERWin是实际项目中最常用的数据库表结构关系的设计工具,下面介绍基于ERWin的数据库图,它包括物理(Physical)图,即实际的数据库图;逻辑(Logical)图,即逻辑的易懂的视图。
1.物理(Physical)图
本章的数据库的ERWin物理图如图8-4所示,它是数据库的真实反映。
图8-4 数据库的ERWin物理图
2.逻辑(Logical)图
本章的数据库ERWin的逻辑图如图8-5所示,它可以让开发者更好地理解业务,和真实的数据库表是一一对应的关系。
图8-5 数据库ERWin的逻辑图
8.2.3 在DB2上创建数据库表
本章所创建的数据库表如图8-6到图8-10所示。
(1)USER表——记录用户详细信息
图8-6 DB2的USER表
(2)ACCOUNT表——记录用户资金账户信息
图8-7 DB2的ACCOUNT表
(3)BOOK表——记录图书的详细信息
图8-8 DB2的BOOK表
(4)ORDER表——记录用户每次购买的总的信息
图8-9 DB2的ORDER表
(5)ORDER_ITEM表——记录用户每次购买的详细信息
图8-10 DB2的ORDER_ITEM表
网上书店系统 - SOA实施(一)
2008-05-05 13:54
8.3 网上书店组件设计架构 上述的SOA架构是从一个宏观的架构进行设计,具体实现时,还需要基于SOA架构进行具体的模块和组件架构设计,SOA是从业务功能来划分的。这里进一步介绍模块组件设计架构,以便读者对本章的网上书店系统有一个全面的了解。 1.组件图 Rational Rose是实际项目中最常用的设计工具。图8-11是基于Rational Rose的组件图,它反应整个应用所使用的技术。
图8-11 网上书店的组件图 l JSP组件:负责页面显示。 l Struts Action Bean:控制器部分,负责页面层和逻辑层的结合,以及页面转换。 l ClientManagerImp:负责调用后台服务的组件。 l SOA Service Integration Bus:SOA服务总线。 l Web Service:Web服务组件。 l Session Bean:负责业务逻辑。 l Entity Bean CMP:负责数据库层的操作。 2.Class类图 图8-12是基于Rational Rose所设计的网上书店系统的总的类图。
图8-12 网上书店的类图 8.4 网上书店的业务和时序图 为了让读者尽快掌握创建SOA应用的方法和步骤,下面详细介绍各个服务接口是如何实现的,包括介绍业务需求、时序图和实现步骤,使读者能尽快掌握精髓,运用到实际项目中去。 8.4.1 注册系统业务和时序图 用户需要通过注册系统来将他们的有关个人信息登记到网上书店系统中,这些个人信息是用户购书时需要的。下面将介绍具体的业务需求及实现步骤。 1.注册系统业务需求简介 做任何一个项目首先需要知道具体的业务,下面首先介绍注册系统总的业务需求。 (1)提供用户界面给用户,输入他们的Name(姓名)、Login Name(登录名)、Password(密码)、Phone(电话号码)、E-mail和Registration Fee(注册费)。 (2)提供用户界面的输入校验。 l 所有字段不能为空。 l 登录名和密码不能少于5个字符。 l 电话号码必须按888-888-8888的格式输入。 l E-mail必须按[email protected]的格式输入。 l 注册费必须输入数字。 (3)用户填完注册信息,单击【Save】按钮之后,所有的用户信息将会记录到数据库USER表和ACCOUNT表中。 (4)用户注册成功后系统将给出成功注册的信息。 2.注册系统业务及实现 下面是本章所实现的业务详解。 (1)用户输入个人信息后,系统进行页面校验。 这里通过Struts的Validator来实现页面校验,返回具体的错误信息让用户修正,如图8-13所示,在Web应用项目的WEB-INF/bkstore目录下面的bkstore- validation.xml文件中定义了对该页面的所有字段的校验规则。 页面显示层是通过Regis.jsp来实现的,业务逻辑是通过Action Bean- RegistrationAction调用模型层来实现的。
图8-13 注册页面校验错误 (2)用户根据提示的错误信息输入正确的信息。 用户输入正确的信息,如图8-14所示,页面校验通过,Action Bean将会把用户所输入的保存在Form Bean中的用户个人信息取出,存入到UserAccountDTO对象中,进而调用BookStoreMgrImp对象中的saveUserInfo方法,以调用SOA的服务总线的入站服务方法,最后将用户个人信息存入到数据库。
图8-14 注册页面输入正确信息 (3)用户注册成功后系统将给出成功注册的信息。 用户个人信息存入到数据库后,Action Form经过ActionMapping的forward方法将页面消息输出到页面,在ActionMessages对象中定义一个注册成功的消息,如图8-15所示。真正的消息内容在资源文件中,而在ActionMessages对象中存入的是一个成功信息的关键词。
图8-15 注册页面成功 (4)查看数据库USER表,如图8-16所示,检查用户的注册信息是否已经记录到数据库中。
图8-16 数据库USER表 (5)查看数据库ACCOUNT表,如图8-17所示,检查用户的注册信息是否已经记录到数据库中。
图8-17 数据库ACCOUNT表 3.实现注册系统时序图 图8-18是实现业务的时序图,显示了实现上述业务逻辑时的类之间的调用顺序关系。
图8-18 注册系统时序图 4.注册系统具体实现步骤 下面是实现如图8-18所示的时序图的具体步骤。 (1)用户单击“URL Regis.do”链接时,Struts的ActionServlet通过config.xml的配置将会指向Regis.jsp的页面。 (2)用户填写完注册信息后单击【Submit】按钮时,Struts会通过config.xml的配置调用RegistrationAction类的execute()方法,并将用户的信息存到UserAccountDTO类中。 (3)RegistrationAction类的execute()方法调用BookStoreMgrImp类的saveUserInfo()方法。 (4)BookStoreMgrImp类的saveUserInfo()方法调用WSDL所生成的接口的ServiceLocator去调用相应的SOA Service Integration Buses的入站服务。 (5)Service Integration Buses的入站服务调用相应的Web Service。 (6)Web Service调用Session Bean(BKStoreMgr)的saveUserInfo()。 (7)Session Bean调用Entity Bean CMP(UserCMP)。 8.4.2 登录系统业务和时序图 本系统是通过登录系统来实现安全管理的,有些页面(如购物车)只有用户登录后才能进入。下面将介绍具体的业务需求及实现步骤。 1.登录系统业务需求简介 下面是登录系统业务需求简介。 (1)创建用户的登录界面。 (2)对用户的用户名和密码进行校验。 (3)如用户登录失败,则需要输出错误信息。 (4)如用户登录成功,则输出成功信息,并保留用户Session,因而用户登录其他页面(如Shopping Cart购物车)时不需要再次登录。 2.登录系统业务及实现 下面是本章所实现的登录系统业务详解及实现。 (1)进入“登录”界面。 页面显示层是通过Login.jsp来实现的,如图8-19所示,业务逻辑是通过Action Bean- LoginAction调用模型层来实现的。
图8-19 “登录”界面 (2)用户输入一个系统没有注册的用户名时,系统会提示相应的错误。 程序回到后台数据库通过CMP来查询是否在数据库中有该用户名,通过findByLoginName的方法来查询。如果通过findByLoginName查不到任何记录,表示没有该loginName,服务器端会将该错误作为异常抛出到Action Bean,Action Bean截获该异常后,取出异常信息。该异常信息只是错误信息的关键词,程序将会从字样资源文件中取出该错误信息,输出到前台页面,如图8-20所示。
图8-20 输入未注册用户名 如果输入一个系统中没有的用户名时,将会出现如图8-21所示的错误。
图8-21 用户名不对或未注册造成的错误登录界面 (3)用户输入的用户名和密码不匹配时会输出错误信息。 程序回到后台数据库通过CMP来查询是否在数据库中有该用户名和密码,通过findByLoginName的方法并以loginName作为参数来查询。找出一个记录,从该记录中取出对应的密码,如果和输入的密码不一致,如图8-22所示,则服务器端抛出用户输入的用户名和密码不匹配的异常,Action Bean截获该异常后,输出到前台页面。
图8-22 输入用户名与密码不匹配 因为输入的用户名存在,但是密码不正确,所以页面输出下列错误,如图8-23所示。
图8-23 用户名和密码不一致的错误信息界面 (4)用户输入正确的用户名和密码。 程序回到后台数据库通过CMP来查询是否在数据库中有该用户名和密码,通过findByLoginName的方法并以loginName作为参数来查询。找出一个记录,从该记录中取出对应的密码,如果和输入的密码一致,则服务器端返回true到前端,Action Bean接到返回值后,通过资源文件输出校验正确的信息到前台页面,如图8-24所示。
图8-24 成功登录界面 3.登录系统时序图 图8-25是实现上面业务的时序图,显示了实现业务逻辑时的类之间的调用顺序关系。
图8-25 登录系统时序图 4.登录系统具体实现步骤 下面是实现上述时序图的具体步骤。 (1)用户单击URL Login.do链接时,Struts的ActionServlet通过config.xml的配置指向Login.jsp的页面。 (2)用户填完登录信息后单击【Submit】按钮时,Struts会通过config.xml的配置调用LoginAction类的execute()方法。 (3) LoginAction类的execute()方法调用BookStoreMgrImp类的checkUserLogin()的方法。 (4)BookStoreMgrImp类的saveUserInfo()方法调用WSDL所生成的接口的ServiceLocator去调用相应的SOA Service Integration Buses的入站服务。 (5)Service Integration Buses的入站服务调用相应的Web Service。 (6)Web Service调用Session Bean(BKStoreMgr)的checkUserLogin ()。 (7)Session Bean调用Entity Bean CMP(UserCMP)。 8.4.3 用户查询系统业务和时序图 用户查询系统将显示网上书店的所有用户及其相关信息。 1.用户查询系统的业务需求 业务需求是显示所有已经注册的用户信息。 页面显示层是通过UserList.jsp来实现的,业务逻辑是通过Action Bean- UserListAction调用模型层来实现的。 单击“用户查询”链接时,所有注册的用户信息会显示,如图8-26所示。
图8-26 “用户查询”界面 2.用户查询系统时序图 图8-27是实现上面业务的时序图,显示了实现业务逻辑的类之间的调用顺序关系。
图8-27 用户查询系统时序图 3.用户查询系统的具体实现步骤 下面是实现上述时序图的具体步骤。 (1)用户单击“URL UserListdo”链接时,Struts会通过config.xml的配置调用UserListAction类的execute()方法。 (2)UserListAction类的execute()方法调用BookStoreMgrImp类的getUserList()的方法。 (3)BookStoreMgrImp类的getUserList()方法调用WSDL所生成的接口的ServiceLocator去调用相应的SOA Service Integration Buses的入站服务。 (4)Service Integration Buses的入站服务调用相应的Web Service。 (5)Web Service调用Session Bean(BKStoreMgr)的getUserList ()。 (6)Session Bean调用Entity Bean CMP(UserCMP)。 (7)将UserList的信息返回到UserList.jsp。 8.4.4 用户账户管理系统业务和时序图 用户选中某些书,想进一步购买时,其账户中必须有足够的资金,本系统主要功能是管理用户账户资金。 1.用户账户管理系统需求和实现 用户账户管理系统需求如下。 l 显示用户账户中的存款余额。 l 用户账户可以增加新的资金。 页面显示层是通过BookMgr.jsp来实现的,业务逻辑是通过Action Bean- AccountMgrAction调用模型层来实现的。下面介绍具体的需求和实现。 (1)登录之后再单击“账户管理”链接,显示用户账户中的存款余额。 具体的实现是在Action Bean中,通过Session的信息得到用户名,然后调用用户名参数和getUserInfo的方法,得到用户的个人信息(包括资金信息),显示在AccountMgr.jsp中,如图8-28所示。
图8-28 “账户管理”界面 (2)在用户账户中注入新的资金。 如果用户注入新的资金,如图8-29所示,单击【Submit】按钮后,新的资金会存到Form Bean中传到Action Bean,Action Bean会从Form Bean中通过accountMgrForm.getString("newAmount")方法取出界面填入的资金值,存到UserAccountDTO对象中,调用updateAccountBalance的方法将用户新的资金更新到后台数据库。
图8-29 在账户里注入新资金 上面的更新数据库资金成功后,如图8-30所示,服务器端将不会报出一个异常到客户端。Action Bean将创建一个资金更新成功的信息到ActionMessages对象中,输出到前端JSP页面。
图8-30 成功更新账户 (3)显示新的用户账户中的存款余额 单击“账户管理”链接,显示用户账户中的存款余额,如图8-31所示,整个调用方法同步骤(1)。
图8-31 显示新的用户账户中的存款余额 2.用户账户管理系统时序图 图8-32是实现上面业务的时序图,显示了实现业务逻辑时的类之间的调用顺序关系。
图8-32 账户管理系统时序图 3.用户账户管理系统的具体实现步骤 下面是实现上述时序图的具体步骤。 (1) 用户单击“Account Manager”链接时,Struts的ActionServlet通过config.xml的配置指向AccoutMgr.jsp页面。 (2)用户填完新的资金后单击【Submit】按钮时,Struts会通过config.xml的配置调用AccountMgrAction类的execute()方法。 (3)AccountMgrAction类的execute()调用BookStoreMgrImp类的updateAccount- Balance()方法。 (4) BookStoreMgrImp类的updateAccountBalance()方法调用WSDL所生成的接口的ServiceLocator去调用相应的SOA Service Integration Buses入站服务。 (5)Service Integration Buses的入站服务调用相应的Web Service。 (6)Web Service调用Session Bean(BKStoreMgr)的updateAccountBalance ()。 (7)Session Bean调用Entity Bean CMP(AccountCMP)将新的资金存入数据库。 8.4.5 图书管理系统业务和时序图 网上书店数据库中的图书信息是管理人员通过图书管理系统加入的。下面将介绍具体的业务需求及实现步骤。 1.图书管理系统业务需求及实现 业务需求是提供用户界面让系统管理员将新的图书加入到数据库中,详细内容如下。 页面显示层是通过BookMgr.jsp来实现的,业务逻辑是通过Action Bean- BookMgrAction调用模型层来实现的。 (1)进入“图书管理”界面,如图8-33所示,加入新的图书的所有信息。
图8-33 “图书管理”界面 在图8-33中,用户输入图书有关的信息,单击【保存】按钮时,这些图书信息将会保存到Form Bean,并被Actionservlet送到Action Bean,Action Bean将会把存在Form Bean中的图书信息取出,存入BookDTO对象中,调用BookStoreMgrImp对象中的createBook方法,进而调用SOA的服务总线的入站服务方法,最后将图书信息存入数据库。 (2)新的图书加入后,系统返回成功加入的信息。 图书信息存入数据库后,Action Bean在ActionMessages对象中定义一个图书成功加入的消息,Action Form经过ActionMapping的forward方法将消息输出到新的转向页面,如图8-34所示。
图8-34 新书成功加入系统 (3)查看数据库表BOOK,如图8-35所示。
图8-35 数据库表BOOK 2.图书管理系统时序图 图8-36是实现上面业务的时序图,显示了实现业务逻辑时的类之间的调用顺序关系。
图8-36 图书管理系统时序图 3.图书管理系统具体实现步骤 下面是实现图8-36所示时序图的具体步骤。 (1)用户单击“BookMgr.do”链接时,Struts的ActionServlet通过config.xml的配置指向BookMgr.jsp的页面。 (2)输入图书信息后单击【Submit】按钮时,Struts会通过config.xml配置调用BookMgrAction类的execute()方法,并将用户的信息存到BookDTO类中。 (3)RegistrationAction类的execute()方法调用BookStoreMgrImp类的create- Book()的方法。 (4)BookStoreMgrImp类的createBook()方法调用WSDL所生成的接口的ServiceLocator去调用相应的SOA Service Integration Buses的入站服务。 (5)Service Integration Buses的入站服务调用相应的Web Service。 (6)Web Service调用Session Bean(BKStoreMgr)的createBook()。 (7)Session Bean调用Entity Bean CMP(BookCMP)。 8.4.6 图书查询系统业务和时序图 用户购买图书时,首先需要知道网上书店有哪些图书,下面将介绍具体的业务需求及实现步骤。 页面显示层是通过BookList.jsp来实现的,业务逻辑是通过Action Bean- BookListAction调用模型层来实现的。 1.图书查询系统业务需求 本系统的业务需求如下。 l 显示所有图书的信息。 l 用户单击某一图书的链接时,可以直接加入到购物车(Shopping Cart)。 单击“图书查询”链接,所有图书信息会显示出来,如图8-37所示。
图8-37 “图书查询”界面 2.图书查询系统时序图 图8-38是实现上面业务的时序图,显示了实现业务逻辑时的类之间的调用顺序关系。 3.图书查询系统的具体实现步骤 下面是实现图8-38所示时序图的具体步骤。 (1)用户单击“BookList.do”链接时,Struts会通过config.xml配置调用BookListAction类的execute()方法。 (2) BookListAction类的execute()方法调用BookStoreMgrImp类的getBookList()的方法。 (3)BookStoreMgrImp类的getUserList ()方法调用WSDL所生成的接口的ServiceLocator去调用相应的SOA Service Integration Buses的入站服务。 (4)Service Integration Buses的入站服务调用相应的Web Service。 (5)Web Service调用Session Bean(BKStoreMgr)的getBookList ()。 (6)Session Bean调用Entity Bean CMP(BookCMP)。 (7)将BookList的信息返回到BookList.jsp。
图8-38 图书查询系统时序图 8.4.7 购物车业务和时序图 当用户选中某些书时,需要通过购物车来购买,下面将介绍具体的业务需求及实现步骤。购物车系统是网上书店系统最复杂的一块,它本质上是实现了图书系统、用户系统和订单系统的集成。 1.购物车的业务需求及实现 购物车系统是网上书店最复杂的一个系统,其业务需求主要包括以下几个方面。 l 用户可以将自己要买的图书加入购物车。 l 用户可以将自己不想买的图书从购物车中删除。 l 用户可以修改自己所要买的图书的数量。 l 系统可以判断用户的账户中是否有足够的资金买购物车中的书。 l 购买成功后,系统将用户的购买信息存入数据库。 (1)用户从BookList中单击某一本书名的链接,如图8-39所示。
图8-39 选择图书 (2)系统显示选中书的详细内容,如果用户想买,则可单击【加入购物车】按钮。 单击书名的链接时,将指向viewBook.do,这样将调用ViewBookAction,同时把该图书的ID号也包含在请求中;ViewBookAction根据request.getParameter的方法得到该书的ID号,并将它作为参数来调用BookStoreMgrImp的getBook方法,得到该图书的所有信息,显示在下页,如图8-40所示。
图8-40 将选中图书加入购物车 (3)系统显示被选书已经加入购物车。 如果用户单击图8-40中的【加入购物车】按钮,页面将会继续提交到ViewBookAction,ViewBookAction将会根据request.getParameter("addShopCart")来确认这个请求是否真正来自“加入购物车”。如果是,将相关的图书信息从Form Bean中取出,存到一个ShoppingCartDTO实例中,因为一个用户可能会买很多书,一个ShoppingCartDTO代表所购买的一种书,将它加入到一个List中保存起来。值得说明的是,在购物车提交之前,所有购书的信息都是存在内存List中的。如果没有加入的错误信息,则通过Action Bean的saveMessages方法,将成功的信息加入到ActionMessages对象之中,再通过ActionMapping对象的findForward方法将成功信息显示到加入购物车的成功信息页面上,如图8-41所示。
图8-41 图书成功加入购物车 (4)用户可以直接从“图书查询”页将自己想买的书加入购物车,如图8-42所示。
图8-42 直接从“图书查询”页将选中图书加入购物车 直接单击“加入购物车”链接,会调用BookListAction。它首先检查是否含有一个action为addShopCart的请求,如果有,将从bookListForm中得到有关书的序号、书名、价格和折扣的信息,存到ShoppingCartDTO中,再存到前述的专门存购物车信息的缓存List之中。值得说明的是,在这个阶段,所有存到购物车之中的信息都还没有存到后台数据库,所以还可以非常方便地进行增加、删除和修改操作。 (5)用户单击“购物车”链接,进入“购物车”界面,如图8-43所示。 通过前面的操作,用户可以将他想买的书加入到购物车。在“购物车”界面,用户可以删除不想买的书,修改所要购买的书的数量;也可以看到每本书的单价、折扣和购买数量,以及购买全部图书的总价。 页面显示层是通过ShoppingCart.jsp来实现的,业务逻辑是通过Action Bean- ShoppingCartAction调用模型层来实现的。
图8-43 进入“购物车”界面 (6)删除不想买的书。 如果用户想删除不想买的图书,需要选中每种书前面的复选框checkbox,然后单击【删除】按钮,如图8-44所示,这个删除请求和所要删除的图书序号将会通过Struts的ActionServlet送到ShoppingCartAction;在ShoppingCartAction的execute方法中,将会检查是否有删除请求,如果有,则通过请求中该图书的序列号,从缓存的List中找到含有该图书序号bookID的ShoppingCartDTO,从List中删除该ShoppingCartDTO,选中图书将会从购物车中消失,如图8-45所示。
图8-44 删除所选图书
图8-45 所删除图书从购物车消失 (7)修改所要买的书的数量。 如果需要修改所买书的数量,则选中要修改数量的书,然后单击【修改数量】按钮。提交请求后,对应于每本书的数量将会在请求消息中存在,并通过ActionServlet转交给ShoppingCartAction;在ShoppingCartAction的execute方法中,它将会检查是否有“修改数量”的请求,如果有这样的请求,则取出请求中的图书数量,取出缓存List中的ShoppingCartDTO对象,修改其中的数量,如图8-46所示。
图8-46 修改购物车中所买图书的数量 (8)提交购物车。 一旦确定了所要购买的图书和数量,单击【Submit】按钮提交购物车,如图8-47所示。该请求会提交到ShoppingCartAction,它将在execute方法中检查是否有submit的请求,如果有这样的请求,它将首先从Session中取出用户的信息;接着产生一个OrderDTO的实例,将用户号、购买时间、税和总价存入到OrderDTO的实例中,然后调用BookStoreMgrImp的createOrder方法,将总的购书信息存入到数据库,并返回一个购书号。接着创建一个OrderItemDTO的数组,将每本书的书号、明细号、数量、折扣等信息存在OrderItemDTO中,调用BookStoreMgrImp的createOrder方法,将购书明细存入数据库。
图8-47 提交购物车 (9)显示用户账户里的资金不足。 提交购物车时,程序将会在服务器端检查用户的账户资金是否大于购书总价,如果账户资金小于购书总价,服务器端将会抛出账户资金不够的异常,Struts的Action Bean截获到异常后,将错误信息显示到页面上。 因为本例账户资金为300.00元,而图书的总价为358.00元,所以显示账户资金不足,如图8-48所示。
图8-48 显示账户资金不足 (10)根据用户账户中的资金修改购买数量,如图8-49所示。
图8-49 根据账户资金修改购买数量 (11)修改数量后重新提交,显示购买成功,如图8-50所示。
图8-50 购买成功 成功提交后,需要检查购书信息是否存到了数据库。 (12)ORDER表记录了总的购书信息,如图8-51所示。
图8-51 ORDER表 (13)ORDER-ITEM表记录了详细的购书信息,如图8-52所示。
图8-52 ORDER-ITEM表 2.购物车系统时序图 图8-53是实现上面业务的时序图,显示了实现业务逻辑时的类之间的调用顺序关系。
图8-53 购物车系统时序图 3.购物车系统具体实现步骤 下面是实现图8-53所示时序图的具体步骤。 (1)用户单击“shoppingCart.do”链接时,Struts的ActionServlet通过config.xml配置将会指向ShoppingCart.jsp的页面。 (2)用户单击【Submit】按钮时,Struts会通过config.xml配置调用Shopping- CartAction类的execute()方法,并将购物车信息存到OrderDTO和OrderItemDTO类中。 (3)RegistrationAction类的execute()方法调用BookStoreMgrImp类的create- Order()的方法和createOrderItem()的方法。 (4)BookStoreMgrImp类的saveUserInfo()方法调用WSDL所生成的接口的ServiceLocator去调用相应的SOA Service Integration Buses的入站服务。 (5)Service Integration Buses的入站服务调用相应的Web Service。 (6)Web Service调用Session Bean(BKStoreMgr)的createOrder ()和create- OrderItem()的方法。 (7)Session Bean调用Entity Bean CMP(OrderCMP和OrderItemCMP)将总的购书信息和详细的购书信息记录到数据库中。 8.5 在Websphere RAD上创建Entity Bean CMP、 Session Bean和Web Service 在创建了数据库和完成所有的设计工作之后,就可以用本书第7章所讲述的方法在Websphere RAD上创建相应的Entity Bean CMP和Session Bean,然后将Session Bean发布成相应的Web Service了。下面是RAD的创建结果。 RAD所创建的网上书店的Session Bean和CMP如图8-54所示。
图8-54 RAD所创建的网上书店的Session Bean和CMP |
8.5.1 在RAD上创建Entity Bean CMP
在RAD上创建的所有的Entity Bean CMP如图8-55所示。本例有6个数据库表,每个CMP对应相应的数据库表如下。
l AccountCMP对应于数据库表ACCOUNT。
l BkSequenceNumCMP对应于数据库表BK-SEQUENCE-NUM。
l BookCMP对应于数据库表BOOK。
l OrderCMP对应于数据库表ORDER。
l OrderItemCMP对应于数据库表ORDER_ITEM。
l UserCMP对应于数据库表USER。
图8-55 在RAD上创建的Entity Bean CMP
下面是各个Entity Bean CMP。
(1)AccountCMP:负责对ACCOUNT数据库表的select、insert、update和delete,如图8-56所示。
l CMP实现类:AccountCMPBean
l CMP主键类:AccountCMPKey
l CMP本地接口:AccountCMPLocal
l CMP本地Home接口:AccountCMPLocalHome
图8-56 AccountCMP
(2)BkSequenceNumCMP:负责对BK-SEQUENCE-NUM数据库表的select、insert、update和delete,如图8-57所示。
l CMP实现类:BkSequenceNumCMPBean
l CMP主键类:BkSequenceNumCMPKey
l CMP本地接口:BkSequenceNumCMPLocal
l CMP本地Home接口:BkSequenceNumCMPLocalHome
图8-57 BkSequenceNumCMP
(3)BookCMP:负责对BOOK数据库表的select、insert、update和delete,如图8-58所示。
l CMP实现类:BookCMPBean
l CMP主键类:BkSequenceNumCMPKey
l CMP本地接口:BkSequenceNumCMPLocal
l CMP本地Home接口:BkSequenceNumCMPLocalHome
图8-58 BookCMP
(4)OrderCMP:负责对ORDER数据库表的select、insert、update和delete,如图8-59所示。
l CMP实现类:OrderCMPBean
l CMP主键类:OrderCMPKey
l CMP本地接口:OrderCMPLocal
l CMP本地Home接口:OrderCMPLocalHome
图8-59 OrderCMP
(5)OrderItemCMP:负责对ORDER-ITEM数据库表的select、insert、update和delete,如图8-60所示。
l CMP实现类:OrderItemCMPBean
l CMP主键类:OrderItemCMPKey
l CMP本地接口:OrderItemCMPLocal
l CMP本地Home接口:OrderCMPLocalHome
图8-60 OrderItemCMP
(6)UserCMP:负责对USER数据库表的select、insert、update和delete,如图8-61所示。
l CMP实现类:UserCMPBean
l CMP主键类:UserCMPKey
l CMP本地接口:UserCMPLocal
l CMP本地Home接口:UserCMPLocalHome
图8-61 UserCMP
8.5.2 在RAD上创建Session Bean
Session Bean负责实际的业务需求,归根到底可以分为以下两大类。
(1)根据前端所传来的数据,调用相应的Entity Bean CMP,对相应的数据库的数据进行相应的insert、update或delete操作。
(2)根据前端所传来的数据,调用相应的Entity Bean CMP,对相应的数据库的数据进行相应的select操作,形成相应的数据结果传到前端。
Session Bean形成相应的处理各种业务需求的接口供调用,如图8-62所示。
图8-62 RAD所创建的Session Bean
在此将创建3个Session Bean。
(1)BookMgr,如图8-63所示。
l Session Bean远程实现类:BookMgrBean
l Session Bean远程接口类:BookMgr
l Session Bean的远程Home接口:BookMgrHome
(2)OrderMgr,如图8-64所示。
l Session Bean远程实现类:OrderMgrBean
l Session Bean远程接口类:OrderMgr
l Session Bean的远程Home接口:OrderMgrHome
图8-63 Session Bean BookMgr 图8-64 Session Bean OrderMgr
(3)UserAccountMgr,如图8-65所示。
l Session Bean远程实现类:UserAccountMgr Bean
l Session Bean远程接口类:UserAccountMgr
l Session Bean的远程Home接口:UserAccountMgrHome
图8-65 Session Bean UserAccountMgr
8.5.3 在RAD上将Session Bean发布为Web Service
上面所创建的Session Bean的远端接口需要用RMI传输协议来调用。可以使用分布式系统的集成,也可以基于服务总线的应用协议转换功能来集成,本章基于标准化的考虑,将Session Bean发布为基于HTTP/SOAP传输协议的Web Service。
将上面的Session Bean发布成相应的Web Service如图8-66所示,包括如下3个Web Service。
l BookMgrService
l OrderMgrService
l UserAccountMgrService
图8-66 将Session Bean发布成Web Service
8.6 SIBus服务总线的基本原理
在介绍具体的如何实现SIBus服务总线之前,简单介绍一下服务总线的创建过程(后面会一步步详细介绍创建过程),然后再介绍其基本原理。
1、首先创建一个服务总线的名称,然后将应用服务器作为总线成员加入到服务总线中来
2、接着创建端点监听器来接收外部的服务请求。
3、接下来为每个服务总线创建目标队列,以便端点监听器在收到服务请求后,将请求消息传到目标队列。
4、再创建出站服务,建立相应的出站服务器端口,接下来在目标队列中配置默认转发路由路径,以便目标队列在收到端点监听器的请求消息后,通过默认路径转给出站服务器端口。
5、最后为每一个服务创建相应的入站服务,并绑定相应的端点监听器和相应的目标队列,以便端点监听器在收到入站服务的外部请求消息后,将请求消息转发到相应的目标队列。进一步将入站服务发布成Web Service的WSDL,以便外部调用。
下面进一步介绍服务总线工作的基本原理。
服务请求者在收到服务总线入站服务发布的WSDL文件后,将创建相应的SOAP请求信息给服务总线。因为创建每个服务的入站服务时,已经将每个入站服务与端点监听器及目标队列进行了关联,所以当服务请求者根据入站服务的WSDL发出服务请求时,这个服务请求就会发给相应的端点监听器,并有端点监听器将这个服务请求转给建立入站服务时所指定的目标队列。
以下代码是本章网上书店图书管理服务接口的入站服务的WSDL文件的service部分(将会在下面章节创建),当服务请求者根据这个地址向服务总线发出服务请求时,它自然会被对应图书管理入站服务的端点监听器收到,端点监听器会将这个服务转发给对应于图书管理入站服务的目标队列。
binding="sibusbinding:SOAPHTTPChannel1InboundPortBinding">
location="http://localhost:9080/wsgwSOAPhttp1/SOAPhttpengine
/BookStoreBus/BookMgrDestinationInboundService
/SOAPHTTPChannel1InboundPort"/>
目标队列将服务请求消息根据所创建的默认转发路由路径(创建为指向图书管理出站服务)转给图书管理的出站服务器端口,图书管理的出站服务器端口将服务请求转给外部的真正的图书管理服务提供者(这里服务总线可以根据业务需求进行相应的消息格式转换,甚至传输协议的转换)。
外部的服务管理提供者在实现服务请求后,会将服务响应消息返回给出站服务器端口,出站服务会返回给目标队列,端点监听器从目标队列得到响应消息后,将响应消息返回给服务请求者。
SIBus服务总线的工作原理如图8-67所示。
图8-67 SIBus服务总线的工作原理
8.7 创建基于Websphere 6.0 SIBus服务总线的
SOA架构的服务集成系统
上一节介绍了SIBus的工作原理,前面章节已经在Websphere RAD上创建了Entity Bean CMP、Session Bean和Web Service。网上书店系统的所有业务通过3个Web Service组件(BookMgrService、OrderMgrService和UserAccountMgrService)来分别对外发布。下面将详细介绍如何通过基于SOA架构的Websphere 6.0 SIBus服务总线来将这3个Web Service组件集成到服务总线上来。
8.7.1 创建SDO库
SDO,其全称为Service Data Objects,中文为“服务数据对象”,是IBM和BEA共同发布的一个基于Java平台的编程框架和API,目的在于使对数据的检查、读取和更新更加容易,前面提到ESB需要有消息格式转换的功能。SIBus上的所有消息格式都是基于SDO接口的,这样可以调用SDO的接口对消息格式实现转换,所有SDO的数据类型都存在SDO库中。
此外,SIBus会用SDO库存储WSDL的定义以支持进一步的Web 服务请求。
所以,需要先在Websphere自带的Cloudscape上创建SDO库。下面是创建SDO Repository的过程。
首先从Admin Console中找到相应的应用服务器的名称和节点,以便在下面的命令行中调用,下面是笔者的服务器的信息,如图8-68所示。
l 应用服务器的名称:server1
l 节点:liangahNode01
图8-68 应用程序服务器
在下列命令窗口中,读者需要将路径改为自己所安装的RAD的目录(如笔者将RAD安装在E:/rad/),然后执行相关命令。
注意:E:/rad/runtimes/base_v6/bin路径是笔者安装的RAD所带的Websphere 6.0的bin路径,读者需要根据自己安装的RAD目录确定相应的bin路径;如果读者直接安装Websphere 6.0(不通过RAD),也可以直接找到相应的Websphere 6.0的“bin”路径。
在目录E:/rad/runtimes/base_v6/bin中运行下面的命令,如图8-69所示。
wsadmin -f installSdoRepository.jacl -createDb liangahNode01 server1
图8-69 运行创建SDO Repository命令
通过上面的liangahNode01和server1,读者要根据图8-68来查找自己的应用服务器的名称和节点,SDO库创建成功后,读者可以在Console中看到成功信息,如图8-70所示。
图8-70 SDO库安装完成
在Admin Console中的“企业应用程序”下面可以看到多了一个相应的“SDO Repository”运行程序如图8-71所示。
图8-71 Repository运行程序
8.7.2 创建SIBus Web服务资源适配器
资源适配器,英文为Resource Adapter,是J2EE Connector Architecture(JCA)所定义的一个组件,其目的在于使得J2EE的各种应用程序通过资源适配器的标准接口,对各种资源进行调用,类似大家熟悉的通过JDBC的标准接口来调用数据库的资源。这里安装Web服务资源适配器,以便服务总线能够调用Web服务,其创建过程如图8-72所示。
图8-72 资源适配器成功启动界面
在目录E:/rad/runtimes/base_v6/bin运行下面的命令。
wsadmin -f ../util/sibwsInstall.jacl INSTALL_RA -installRoot
"E:/rad/runtimes/base_v6" -nodeName liangahNode01
8.7.3 安装和配置端点监听器
创建端点监听器以便服务总线接收Web服务的请求,所有外部或者内部送给服务总线的请求都需要端点监听器来接收。
安装端点监听器之前,首先需要安装sibws,它是服务总线启动Web Service所需要的,过程如下。
1.安装sibws运行程序
运行命令“wsadmin -f ../util/sibwsInstall.jacl INSTALL”,如图8-73所示。在目录E:/rad/runtimes/base_v6/bin下运行如下的命令。
wsadmin -f ../util/sibwsInstall.jacl INSTALL -installRoot
"E:/rad/runtimes/base_v6" -serverName server1 -nodeName liangahNode01
图8-73 安装sibws运行程序
安装完毕sibws,启动sibws,可以看到sibws.liangahNode01.server1运行在企业应用程序中,如图8-74所示。
图8-74 启动sibws界面
2.安装支持HTTP协议的端点监听器应用程序
要使用支持HTTP协议的端点监听器,还需要安装两个应用程序,如图8-75所示。
运行命令“wsadmin -f ../util/sibwsInstall.jacl INSTALL_HTTP”,在目录E:/rad/ runtimes/base_v6/bin下运行如下命令。
wsadmin -f ../util/sibwsInstall.jacl INSTALL_HTTP -installRoot
"E:/rad/runtimes/base_v6" -serverName server1 -nodeName liangahNode01
图8-75 安装支持HTTP协议的端点监听器应用程序
安装完毕sibwshttp1,启动sibwshttp1,可以看到sibwshttp1.liangahNode01.server1运行在企业应用程序中,如图8-76所示。
安装完毕sibwshttp2,启动sibwshttp2,可以看到sibwshttp2.liangahNode01.server1运行在企业应用程序中,如图8-76所示。
图8-76 安装启动应用程序
3.创建端点监听器
安装完成基于HTTP协议的端点监听器应用程序后,这里就可以进一步创建端点监听器了,其创建过程如下。
(1)在Admin Console中的左边列表中选择“服务器”→“应用服务器”选项,打开“应用程序服务器”对话框,如图8-77所示。
图8-77 打开“应用程序服务器”对话框
选择“应用程序服务器”对话框的“名称”目录下面的“server1”选项,打开“server1”的属性对话框,如图8-78所示。
图8-78 打开“server1”的属性对话框
(2)创建端点监听器Endpoint Listeners
选取“其他属性”下面的“Endpoint Listeners”选项,进入“Endpoint Listeners”的新建对话框,如图8-79所示。
图8-79 “Endpoint Listeners”新建对话框
单击【新建】按钮,弹出“Endpoint Listeners”创建对话框,如图8-80所示,输入下面信息。
l 名称:SOAPHTTPChannel1
l URL根:http://localhost:9080/wsgwSOAPhttp1/
l WDSL服务HTTP URL 根:http://localhost:9080/wsgwSOAPhttp1/
图8-80 创建端点监听器Endpoint Listeners
配置完后,单击【应用】按钮,可以看到配置的Endpoint Listeners如图8-81所示。
图8-81 所创建的端点监听器Endpoint Listeners
8.7.4 创建服务总线
服务总线是一组相连的服务器和集群,创建服务总线就是先命名一个服务总线的名称,有了这个名称后,服务器将以总线成员的身份加入到此服务总线,服务器上的应用程序通过与该服务器相关联的消息传递引擎连接到服务总线上来。服务总线支持基于消息和棉线服务体系的应用程序。下面是创建服务总线的过程。
选择左边列表的“服务集成”→“总线”选项,弹出新建总线对话框,如图8-82所示。
图8-82 新建总线对话框
单击【新建】按钮,进入服务总线的创建对话框,如图8-83所示,在“名称”文本框中输入所创建的服务总线名称“BookStoreBus”。
图8-83 服务总线创建对话框
单击【应用】按钮,完成服务总线的创建,如图8-84所示。
图8-84 所创建的服务总线
8.7.5 创建总线成员
创建总线成员的目的是将应用服务器和节点加入到相应的服务总线,以便将服务器所提供的各种服务和服务总线建立联系,其创建过程如下。
选择图8-84中“名称”目录下的“BookStoreBus”选项,打开“总线”对话框,如图8-85所示。
图8-85 创建总线成员
在对话框中选取目录“其他属性”下面的“总线成员”选项,弹出总线成员的添加对话框,如图8-86所示。
图8-86 进入总线成员的添加对话框
单击【添加】按钮,弹出“选择服务器或集群”对话框。在“服务器”下拉列表中选择应用服务器,如图8-87所示,这里实际上就是本地的应用服务器Websphere 6.1所对应的节点名称和服务器名称。
图8-87 选择服务器或集群
单击【下一步】按钮弹出“确认新总线成员的添加”对话框,如图8-88所示。
图8-88 确认新总线成员的添加
单击【完成】按钮,完成总线成员的添加工作,如图8-89所示,将显示所添加的节点和服务器。
图8-89 所添加的总线成员
8.7.6 创建总线目标
创建总线目标的目的在于创造一个虚拟的信息交换的场所,是一个消息接收地,可以是队列(queue)、主题空间(topic)等,本章将采用队列。当端点监听器收到服务请求消息后,会根据入站服务的设定,将服务请求转发到总线目标来。总线目标会根据默认转发路由路径将服务请求转给出站服务器端口。出站服务器端口会进一步将请求消息转给外部服务提供者得到返回消息。总线目标在收到出站服务器端口的返回消息后,会转给端点监听器。
本章的网上书店系统有3个Web Service的服务接口。
(1)BookMgrService
(2)OrderMgrService
(3)UserAccountMgrService
本章将分别对上面3个服务接口创建总线目标,下面是创建对应BookMgr总线目标的过程。
打开服务总线“BookStoreBus”对话框,如图8-90所示。
图8-90 服务总线对话框
在服务总线对话框中选取“其他属性”目录下面的“目标”选项,弹出目标新建对话框,如图8-91所示。
图8-91 新建目标
单击【新建】按钮,弹出目标类型创建对话框,如图8-92所示,选择“队列”选项。
图8-92 目标类型创建对话框
单击【下一步】按钮,弹出“设置队列属性”对话框,如图8-93所示,在文本框“标识”中输入目标的名称“BookMgrDestination”。
图8-93 设置队列属性
单击【下一步】按钮,弹出“将队列指定给总线成员”对话框,如图8-94所示,在“总线成员”下拉列表中选择相应的服务器。
图8-94 将队列指定给总线成员
单击【下一步】按钮,弹出“确认队列创建”对话框,如图8-95所示。
图8-95 确认队列创建
单击【完成】按钮,完成目标的创建,如图8-96所示。
图8-96 所创建的目标
上面完成了对应于BookMgr服务的总线目标“BookMgrDestination”的创建,同样的方法可建立对应于OrderMgr和UserAccountMgr的总线目标OrderMgrDestination和UserAccountMgrDestionation,如图8-97所示。
图8-97 对每个服务创建总线目标
这些总线目标的名字可以随便起,在后面建立入站服务时要被选择到。
8.7.7 创建出站服务
如前所述,总线目标在收到服务请求后,会将请求消息送到出站服务器端口上。出站服务器端口会进一步将请求消息转给外部服务提供者,出站服务器端口在得到响应消息后,会返回给总线目标。下面是创建出站服务的过程。
创建出站服务前,首先要找到外部服务提供者的Web Service组件的WSDL的路径地址,如图8-98所示。
图8-98 创建出站服务
在BookMgr.wsdl中找到相应的wsdlSOAP:address,见下面的黑体部分。
<wsdlSOAP:address location=
"http://localhost:9080/routerProject/services/BookMgr"/>
在浏览器链接中输入http://localhost:9080/routerProject/services/BookMgr?wsdl,也就是在从WSDL文件中所得到的地址(Web服务地址)后面加上?wsdl,以便查出WSDL文件的地址,如图8-99所示。
图8-99 通过URL得到WSDL的地址
这样可以得到BookMgr.wsdl的路径如下。
http://localhost:9080/routerProject/services/BookMgr/wsdl/BookMgr.wsdl
同样的方法可以得到OrderMgr.wsdl和UserAccountMgr.wsdl的路径如下。
http://localhost:9080/routerProject/services/OrderMgr/wsdl/OrderMgr.wsdl
http://localhost:9080/routerProject/services/UserAccountMgr/wsdl/UserAccountMgr.wsdl
有了上面的WSDL文件的路径,就可以创建相应的出站服务了,下面是创建BookMgr的出站服务过程。
打开服务总线“BookStoreBus”对话框,如图8-100所示。
选择“其他属性”目录下面的“出站服务”菜单,弹出“出站服务”新建对话框,如图8-101所示。
图8-100 创建BookMgr出站服务
图8-101 新建出站服务
单击【新建】按钮,弹出“查找目标服务WSDL”对话框,在“WSDL位置”文本框中输入所得到的WSDL路径,在“WSDL 位置类型”目录下面选取“URL”选项,这里是直接通过WSDL中所提供的URI地址来查找服务的,如图8-102所示。
图8-102 查找目标服务WSDL
单击【下一步】按钮,在弹出的对话框中的“服务”下拉列表中选择相应的服务接口。一个WSDL文件中可能含有多个服务接口,选择一个要加入服务总线的服务接口,如图8-103所示。
图8-103 选择服务
单击【下一步】按钮,弹出“选择端口”对话框,如图8-104所示,选取默认设置。
图8-104 服务器端口
单击【下一步】按钮,在弹出的对话框中输入下面的属性值,如图8-105所示,以便以后在总线目标中创建默认转发路由路径时调用。
l 出站服务名称:BookMgrOutboundService
l Service destination名:BookMgrOutboundService
l Port destination名:BookMgrOutboundPort
图8-105 出站服务和destination的名称
单击【下一步】按钮,在弹出的对话框的“总线成员”下拉列表中选择相应的服务器,如图8-106所示。
图8-106 将端口指定给总线成员
单击【完成】按钮,所创建的出站服务如图8-107所示。
图8-107 所创建的出站服务
上面是所创建的对应于BookMgr的出站服务BookMgrOutboundServer,同理可建立对应于OrderMgr和UserAccountMgr的出站服务,如图8-108所示。
图8-108 建立对应于OrderMgr和UserAccountMgr的出站服务
8.7.8 创建默认转发路由路径
在总线目标中创建默认转发路径的目的在于当总线目标收到端点监听器传来的服务请求消息后,它将根据这里设置的默认转发路由路径将服务请求消息转到它所定义的出站服务器端口,出站服务器端口会进一步将请求消息转给外部服务提供者。下面是在BookMgrDestination中创建默认转发路由路径的过程。
打开“目标”对话框,如图8-109所示。
图8-109 选择目标
选择目标“BookMgrDestination”选项,打开如图8-110所示对话框,在“默认转发路由路径”文本框中输入相应的出站服务的名称,如“BookStoreBus: BookMgrOutboundService”。
图8-110 目标对话框
同理可在OrderMgrDestination和UserAccountMgrDestination中创建下面的默认转发路由路径。
l BookStoreBus:OrderMgrOutboundService
l BookStoreBus:UserAccountMgrOutboundService
8.7.9 创建入站服务
服务总线需要给它所提供的每个服务创建相应的入站服务,并同时与端点监听器和总线目标相关联,以便以后服务调用者根据入站服务的WSDL文件向服务总线发出SOAP请求消息时,能被该端点监听器收到,同时将请求消息转到这里定义的总线目标。下面是创建BookMgr入站服务的过程。
打开服务总线“BookStoreBus”对话框,如图8-111所示。
图8-111 创建BookStoreBus入站服务
选择“其他属性”目录下面的“入站服务”选项,弹出“入站服务”新建对话框,如图8-112所示。
图8-112 选择入站服务
单击【新建】按钮,在弹出对话框的“模板WSDL位置”文本框中输入BookMgr服务的WSDL路径;在“模板 WSDL 位置类型”目录下面选取“URL”选项,这里是直接通过WSDL中所提供的URI地址来查找服务的;在“Service destination 名”下拉列表中选择对应于该入站服务的总线目标,如图8-113所示。BookMgr的WSDL路径如下。
http://localhost:9080/routerProject/services/BookMgr/wsdl/BookMgr.wsdl //和前面的一样吗? 一样的!
图8-113 选择service destination和模板WSDL位置
单击【下一步】按钮,在弹出的对话框中的“服务”下拉列表中选择相应的服务接口。一个WSDL文件中可能含有多个服务接口,选择一个要加入服务总线的服务接口,如图8-114所示。
图8-114 从模板WSDL选择服务
单击【下一步】按钮,弹出入站服务命名对话框,为入站服务起一个相应的名称,并选择端点监听器,如图8-115所示。
图8-115 命名入站服务并选择endpoint listeners
单击【下一步】按钮,弹出入站服务发布对话框,如图8-116所示。
图8-116 定义UDDI发布属性
单击【完成】按钮,则完成了BookMgr的入站服务的创建工作,如图8-117所示。
图8-117 完成BookMgr入站服务的创建
同样可根据OrderMgr和UserAccountMgr的WSDL路径创建相应的入站服务,如图8-118所示,它们的WSDL路径如下。
http://localhost:9080/routerProject/services/OrderMgr/wsdl/OrderMgr.wsdl
http://localhost:9080/routerProject/services/UserAccountMgr/wsdl/UserAccountMgr.wsdl
图8-118 根据OrderMgr和UserAccountMgr的WSDL路径创建相应的入站服务
8.7.10 发布入站服务
创建入站服务后,需要给外部请求者创建相应的入站服务的WSDL文件,以便外部请求者能根据WSDL文件向服务总线发出相应的服务请求。发布入站服务包括下面两个步骤。
(1)企业服务总线通过发布入站服务,创建相应的WSDL文件,给其他系统来调用。
进入服务总线页面,打开“入站服务”对话框,选择所要发布的入站服务的选项,打开相应的入站服务对话框,如图8-119所示。
图8-119 发布入站服务
在对话框的“其他属性”目录下面,选择“将WSDL文件发布到ZIP文件”选项,弹出“发布WSDL文件”对话框,如图8-120所示。
图8-120 将WSDL文件发布到ZIP文件
选中图8-120中的ZIP文件链接,则可将ZIP文件下载到本地目录,如图8-121所示。
图8-121 将ZIP文件下载到指定目录
每个入站服务可以得到4个WSDL文件。
(2)验证企业服务总线的服务是否正常工作。
打开BookStoreBus.BookMgrDestinationInboundServiceService.wsdl文件,参见如下代码。
xmlns:sibusbinding="http://www.ibm.com/websphere/sib/webservices
/liangahNode01Cell/BookStoreBus/
BookMgrDestinationInboundService/Binding"
xmlns:impl=http://sessionbean.ejb.bkstore.com
xmlns:intf="http://sessionbean.ejb.bkstore.com"
xmlns:wsdlSOAP=http://schemas.xmlSOAP.org/wsdl/SOAP/
xmlns:wsi="http://ws-i.org/profiles/basic/1.1/xsd"
xmlns:xsd=http://www.w3.org/2001/XMLSchema
xmlns:tns2="http://dto.service.model.bkstore.com"
xmlns:SOAP=http://schemas.xmlSOAP.org/wsdl/SOAP/
xmlns:wsdl="http://schemas.xmlSOAP.org/wsdl/">
BookMgrDestinationInboundService/Binding" location=
"BookStoreBus.BookMgrDestinationInboundServiceBindings.wsdl"/>
binding="sibusbinding:SOAPHTTPChannel1InboundPortBinding">
图8-122 输入入站服务地址
页面有输出,表示企业服务总线服务正常。
8.8 在Eclipse3.1.2上创建相应的调用SOA Web
Service的Struts客户端类
这里将在Eclipse3.1.2+WebLogic 9.1+Struts上来调用RAD Websphere 6.0所提供的SOA服务,实现跨不同服务器的基于SOA架构的整合应用。
8.8.1 复制入站服务的WSDL到相应的Eclipse目录下
如下为入站服务的3类WSDL。
l 后缀为ServiceService.wsdl
l 后缀为ServicePortTypes.wsdl
l 后缀为ServiceBindings.wsdl
具体说明分别如下。
Ø BookStoreBus.BookMgrDestinationInboundServiceService.wsdl
Ø BookStoreBus.BookMgrDestinationInboundServicePortTypes.wsdl
Ø BookStoreBus.BookMgrDestinationInboundServiceBindings.wsdl
Ø BookStoreBus.OrderMgrDestinationInboundServiceService.wsdl
Ø BookStoreBus.OrderMgrDestinationInboundServicePortTypes.wsdl
Ø BookStoreBus.OrderMgrDestinationInboundServiceBindings.wsdl
Ø BookStoreBus.UserAccountMgrDestinationInboundServiceService.wsdl
Ø BookStoreBus.UserAccountMgrDestinationInboundServicePortTypes.wsdl
Ø BookStoreBus.UserAccountMgrDestinationInboundServiceBindings.wsdl
将上述内容复制到相应的Eclipse目录下,如图8-123所示。
图8-123 将入站服务的3类WSDL复制到Eclipse目录下
8.8.2 创建相应的Ant Build脚本生成相应的Web Service客户端程序
将上面的9个WSDL文件复制到相应的目录后,就可以创建build.xml,以及用Ant Build生成相应的Web Service客户端程序了。
1.创建相应的build.xml(可参见第5章)
在build.xml中定义如下相应的输出路径。
在build.xml定义如下原始的WSDL文件和所在目录。
<target name="bindings1">
location="./wsdl/BookStoreBus.UserAccountMgrDestinationInbound
ServiceService.wsdl"/>
target>
2.在Eclipse中运行相应的Ant Build
用鼠标右键单击“build.xml”,在弹出的快捷菜单中选【Run As】→【Ant Build】命令,如图8-124所示。
图8-124 选择【Ant Build】
弹出“Ant Build”对话框,如图8-125所示。
图8-125 “Ant Build”对话框
3.Ant Build所生成的Web Service客户端程序
创建com.bkstore.ejb.sessionbean 包下面的客户端Java类,如图8-126所示。
图8-126 创建com.bkstore.ejb.sessionbean目录下面的客户端Java类
下面是所创建的exception、dto、Binding和BookStoreBus.Service包下的类,如图8-127所示。
图8-127 创建的exception、dto、Binding和BookStoreBus.Service包下的类
上面所创建的类的内容类似于5.7节所介绍的WSDL所创建的Web Service的客户端Java类。
8.8.3 客户端和SOA Web Service的集成
本节将通过BookStoreMgrImp类(model class)来调用相应的SOA Web Service,实现客户端和SOA Web Service的集成,如图8-128所示。
图8-128 实现客户端和SOA Web Service的集成
客户端对SOA的Web Service的调用在BookStoreMgrImp.java中实现,参见如下代码,包括本章所实现的所有业务逻辑的接口调用。
package com.bkstore.model.service.imp;
……
public class BookStoreMgrImp implements BookStoreMgrIfc {
private BookMgr getBookMgr() throws ServiceException
{
BookMgrDestinationInboundServiceLocator bookMgrLocator = new
BookMgrDestinationInboundServiceLocator();
return bookMgrLocator.getSOAPHTTPChannel1InboundPort();
}
private OrderMgr getOrderMgr() throws ServiceException
{
OrderMgrDestinationInboundServiceLocator orderMgrLocator = new
OrderMgrDestinationInboundServiceLocator();
return orderMgrLocator.getSOAPHTTPChannel1InboundPort();
}
private UserAccountMgr getUserAccountMgr() throws ServiceException
{
UserAccountMgrDestinationInboundServiceLocator
userAccountMgrLocator=new
UserAccountMgrDestinationInboundServiceLocator();
return userAccountMgrLocator.getSOAPHTTPChannel1InboundPort();
}
public boolean checkUserLogin(String loginName, String password)
throws ServiceException,
RemoteException {
return getUserAccountMgr().checkUserLogin(loginName,password);
}
public void saveUserInfo(UserAccountDTO userAccountDTO) throws
ServiceException, RemoteException
{
getUserAccountMgr().saveUserInfo(userAccountDTO);
}
public List getUserList() throws ServiceException, RemoteException {
ArrayList list = new ArrayList();
ArrayOf_tns2_UserAccountDTO userAccountArrays =
getUserAccountMgr().getUserList();
UserAccountDTO[] userAccountDTOs =
userAccountArrays.getUserAccountDTO();
for (int i=0; i
list.add(userAccountDTOs[i]);
}
return list;
}
public void updateAccountBalance(UserAccountDTO userAccountDTO)
throws RemoteException, ServiceException
{
getUserAccountMgr().updateAccountBalance(userAccountDTO);
}
public UserAccountDTO getUserInfo(String loginName) throws
ServiceException, RemoteException {
return getUserAccountMgr().getUserInfo(loginName);
}
public void createBook(BookDTO bookDTO) throws RemoteException,
ServiceException {
getBookMgr().createBook(bookDTO);
}
public BookDTO getBook(int getBookBookID) throws RemoteException,
ServiceException {
return getBookMgr().getBook(getBookBookID);
}
public ArrayOf_tns2_BookDTO getBookList() throws RemoteException,
ServiceException {
return getBookMgr().getBookList();
}
public void createOrderItem(ArrayOf_tns2_OrderItemDTO
orderItemDTOs) throws RemoteException, ServiceException
{
getOrderMgr().createOrderItem(orderItemDTOs);
}
public int createOrder(OrderDTO orderDTO) throws RemoteException,
ApplicationException, ServiceException
{
return getOrderMgr().createOrder(orderDTO);
}
}
(1)getBookMgr:得到基于SOA 的关于图书管理BookMgr入站服务的Web Service组件。
l 得到基于SOA的关于图书管理BookMgr入站服务的Web Service的地址。
l 返回基于SOA的关于图书管理BookMgr入站服务的Web Service服务接口。
(2)getOrderMgr:得到基于SOA 的关于购物车OrderMgr入站服务的Web Service组件。
l 得到基于SOA的关于购物车OrderMgr入站服务的Web Service的地址。
l 返回基于SOA的关于购物车OrderMgr入站服务的Web Service服务接口。
(3)getUserAccountMgr:得到基于SOA 的关于用户账户管理UserAccountMgr入站服务的Web Service服务接口。
l 得到基于SOA的用户账户管理UserAccountMgr入站服务的Web Service的地址。
l 返回基于SOA的关于用户账户管理UserAccountMgr入站服务的Web Service服务接口。
(4)checkUserLogin:检查用户登录的用户名和密码是否正确。
调用SOA的Web Service接口checkUserLogin去检查用户名和密码是否正确。
(5)saveUserInfo:存储用户信息。
调用SOA的Web Service接口saveUserInfo存储用户信息。
(6)getUserList:得到所有用户信息。
l 创建一个ArrayList的实例。
l 调用SOA的Web Service接口getUserList得到所有用户的数组。
l 将所有用户的数组加入到ArrayList之中。
l 返回ArrayList。
(7)updateAccountBalance:更新用户账户资金。
调用SOA的Web Service接口updateAccountBalance更新用户账户资金。
(8)getUserInfo:通过用户名得到用户信息。
调用SOA的Web Service接口getUserInfo通过用户名得到用户信息。
(9)createBook:创建和加入新的图书到图书管理系统。
调用SOA的Web Service接口createBook创建和加入新的图书到图书管理系统。
(10)getBook:通过系统的图书号查询一本书。
调用SOA的Web Service接口getBook通过系统的图书号查询一本书。
(11)getBookList:得到包含系统所有图书的一个数组。
调用SOA的Web Service接口getBookList得到包含系统所有图书的一个数组。
(12)createOrderItem:创建图书订单明细。
调用SOA的Web Service接口createOrderItem创建图书订单明细。
(13)createOrder:创建图书订单。
调用SOA的Web Service接口createOrder创建图书订单。
8.8.4 创建相应的Struts Action Bean类
Struts Action Bean类将模型层和视图JSP关联起来,在Action Bean的execute方法中调用上面的BookStoreMgrImp的接口,如图8-129所示。
图8-129 创建相应的Struts Action Bean类
8.8.5 创建相应的JSP
用JSP来实现视图(页面显示),JSP是通过相应的Struts的tag lib和Form Bean来创建的,如图8-130所示。
图8-130 创建相应的JSP
8.9 运行本章的例子
本书所附光盘提供了本章实例源代码,读者需要先创建一个对应于RAD的chapter_8_rad_workspace,然后导入光盘目录chapter_8_rad_workspace下面的bkstoreEJBProjectEAR.ear即可(可参照第7章例子)。
读者还需要完成下面的工作。
(1)配置数据源(参见第7章)。
(2)根据8.2节创建DB2的数据库表和数据。
(3)在RAD上启动Server和Admin Console。
(4)完成8.7节所说明的建立服务总线的所有工作。
(5)发布入站服务,生成WSDL文件。
此外读者需要将光盘里的chap_8_eclipse.zip解压缩,然后将Eclipse3.1.2指向chap_8_eclipse以运行WebLogic(参照第7章的例子),以实现WebLogic对Websphere的服务总线的调用。因为每个人的机器的名字不同,读者需要用自己所创建的入站服务的WSDL文件覆盖光盘中的WSDL文件,重新生成和编译客户端代码。
8.10 小结
本章首先介绍了SOA出现的原因在于其能够提供一个整合和监控各种松散耦合(或完全解耦)的各种服务的整合平台。然后详细介绍了如何创建基于Websphere 6.0 SIBus服务总线的SOA架构的企业服务系统。以一个网上书店系统的实例,详细介绍了SOA的实施过程。完成了在Websphere 6.0上建立SOA的网上书店服务体系。在WebLogic 9.1中应用Struts调用SOA Web Service客户端,实现基于SOA架构的Struts、EJB和Web Service的J2EE跨服务器平台整合应用开发。一个SOA的实施主要包括以下几个方面。
l 对业务需求进行分析,将整个业务需求划分为松散耦合的子服务系统,实现本章第1节所述的SOA的第1个要点。
l 对服务接口方法进行粗粒度的接近业务实际服务操作的划分,实现SOA的第2个要点。
l 在进一步细化业务需求的基础上对各个子服务系统进行数据库设计、建模、创建类图、创建时序图等设计工作。
l 在设计的基础上通过IDE工具实现各个子服务系统的编程开发。
l 通过服务总线将各个子服务系统进行集成,实现SOA的第3个要点,即服务的地址和传输协议的透明化(此外Websphere 6.0 SIBus作为服务总线进一步提供了修改消息格式,修改消息内容,以及改变传输协议等强大功能,有兴趣的读者可以进行进一步的探讨)。
l 将用户界面系统和服务总线进行集成。
本书第6章介绍了基于BEA Aqulogic服务总线的SOA的实现,本章进一步介绍了基于IBM SIBus服务总线的SOA的实现,这样对基于目前市面上两大主流J2EE厂商的服务总线ESB产品进行SOA的实施本书都介绍到了,希望能对读者将来的SOA项目实施起到帮助和借鉴作用。