IBM®
跳转到主要内容
    中国 [选择]    使用条款
 
 
Select a scope: Search for:    
    首页    产品    服务与解决方案     支持与下载    个性化服务    
跳转到主要内容

developerWorks 中国  >  SOA and Web services | Rational  >

用 RAD 开发基于 SOAP/JMS 的 EJB Web 服务及其构建 SOAP 请求消息

developerWorks
文档选项

未显示需要 JavaScript 的文档选项

样例代码


级别: 初级

吕 忠心 (lvzhongx@cn.ibm.com), 高级软件工程师, IBM

2009 年 4 月 09 日

本文描述了在 Rational Application Developer(RAD) 中开发基于 SOAP/JMS(SOAP over JMS) 的 EJB Web 服务的过程及其如何构建调用此 Web 服务的 SOAP 请求消息。在文章中给出调用 EJB Web 服务完整的过程,包括发送 SOAP 请求消息到请求队列、调用服务、SOAP 请求消息与服务方法入口参数之间的绑定,以及服务方法执行的结果转换为 SOAP 消息回送到返回队列。

引言

在一个企业大的分布式系统中,我们通常会发现不同种类的操作系统,应用软件,系统软件相互交织。企业为了对一个新的需求做出快速的反应,最好的方式是对现有的应用程序和应用基础结构重新利用,用尽可能少的投入来快速构建新的应用满足新的业务需求。SOA(Service-Oriented Architecture) 凭借其松耦合的特性,使得企业可以按照模块化的方式来添加新服务或把企业现有的应用作为服务以解决新的业务需要。面向服务的体系结构 SOA 是一个组件模型,它将应用程序的不同功能单元(称为服务)通过这些服务之间定义良好的接口和协议联系起来。接口的定义独立于实现服务的硬件平台、操作系统和编程语言。Web 服务是实现 SOA 的方式之一。用 Web 服务来实现 SOA 的好处是你可以实现一个中立平台,来获得服务。SOA 可以支持多种协议和消息格式。我们可以通过 JMS 协议,传输 SOAP(Simple Object Access Protocol) 消息,与 Web 服务进行通信调用。本文中,服务消费者用 WebSphere MQ 作为 JMS 提供者,通过 MQ 队列传输 SOAP 请求消息,从而调用 Web 服务。在本实例中 MQ Infrastructure 采用本地—远程队列基础架构传输 SOAP 消息。本文的读者定位为有 Eclipse 或 RAD 开发经验,对 SOA、WebService 有一定的了解,对 MQ 配置有所了解的人。

本文主要由以下几部分组成:

  1. 介绍系统架构、为示例程序搭建 MQ 环境及其在 WAS Console 中配置相应的信息。
  2. 介绍如何在 RAD 中开发 EJB Web 服务。
  3. 介绍如何构建基于特定 EJB Web 服务的 SOAP 请求消息,给出 EJB Web 服务调用完整的通信过程,SOAP 消息和 JAVA Object 之间的 Binding 及其示例程序的调用演示。

本示例的系统架构图如图 1 所示。

服务消费者通过 JMS 协议,发送一定格式的 SOAP 请求消息到本端的远程队列,请求消息经由通道传输到服务提供者端的本地队列中,服务提供者端的 Web 服务路由模块监听队列,读取、解析消息并调用服务实现,将服务执行的结果转换成 SOAP 消息,并放入服务提供者端的远程队列,回复消息经由通道传输到服务消费者端的本地队列中,整个通信过程完毕。


图 1. 系统架构图





回页首


为示例程序配置资源

首先要创建服务所需要的消息队列。为了更好的模拟真实的项目背景,我们采用的是本地队列 <---> 远程队列消息传递模式。如图 2 所示,这是服务的消费者和服务的提供者之间传递请求消息,回复消息的传递方式与之类似。通常情况下,服务的消费者和服务的提供者位于不同的主机上,各自拥有独立的队列管理器。可以采用如图所示本地队列 --- 远程队列的形式传递请求和响应消息。


图 2. 服务的消费者和服务的提供者之间的消息传递

创建队列连接

创建服务消费者端的队列

在命令行方式下执行下列的命令:

1. 创建队列管理器。

C:\>crtmqm –q –u SYSTEM.DEAD.LETTER.QUEUE SOURCE.QUEUE.MANAGER

2. 启动创建的队列管理器。

C:\>strmqm SOURCE.QUEUE.MANAGER

3. 运行 MQSC 命令管理队列管理器。

C:\>runmqsc SOURCE.QUEUE.MANAGER

4. 在 MQSC 控制台中执行命令创建一个本地队列。

DEFINE QLOCAL (LOCAL.QUEUE.SOURCE) + DESCR('local queue for SOURCE systems') + 
PUT (ENABLED) + GET (ENABLED) + NOTRIGGER +	MSGDLVSQ (FIFO) + 
MAXDEPTH (5000) + MAXMSGL (4194304) + USAGE (NORMAL);

5. 在 MQSC console 中执行命令创建一个 SERVER 连接通道。

DEFINE CHANNEL ('SERV.CON') + CHLTYPE(SVRCONN) + TRPTYPE(TCP);

6. 在 MQSC console 中执行命令创建一个传输队列。

DEFINE QLOCAL ('SOURCE.XMITQ.QUEUE') USAGE (XMITQ);

7. 在 MQSC console 中执行命令创建一个远程队列。

DEFINE QREMOTE(REMOTE.QUEUE.SOURCE) + RNAME('LOCAL.QUEUE.TARGET') +
RQMNAME('TARGET.QUEUE.MANAGER') + XMITQ('SOURCE.XMITQ.QUEUE');

8. 在 MQSC console 中执行命令创建一个发送通道。

DEFINE CHANNEL ('SOURCE.TO.TARGET') + CHLTYPE(SDR) + CONNAME (127.0.0.1(1418)) +
XMITQ ('SOURCE.XMITQ.QUEUE') + TRPTYPE(TCP);

在本文中,我们在一台 server 上建立 2 个不同的队列管理器,在实际的工程环境中,这里的 IP 地址应该是目的服务器上的队列管理器的 IP 地址和端口号。

9. 在 MQSC console 中执行命令创建一个接收通道。

DEFINE CHANNEL ('TARGET .TO.SOURCE ') CHLTYPE(RCVR) TRPTYPE(TCP);

创建服务提供者端的队列

1. 创建队列管理器。

C:\>crtmqm –q –u SYSTEM.DEAD.LETTER.QUEUE TARGET.QUEUE.MANAGER

2. 启动创建的队列管理器。

C:\>strmqm TARGET.QUEUE.MANAGER

3. 运行 MQSC 命令管理队列管理器。

C:\>runmqsc TARGET.QUEUE.MANAGER

4. 在 MQSC 控制台中执行命令创建一个本地队列。

DEFINE QLOCAL (LOCAL.QUEUE.TARGET) + DESCR('local Queue for TARGET systems') + 
PUT (ENABLED) + GET (ENABLED) + NOTRIGGER + MSGDLVSQ (FIFO) + MAXDEPTH (5000) + 
MAXMSGL (4194304) + USAGE (NORMAL);

5. 在 MQSC 控制台中执行命令创建一个 SERVER 连接通道。

DEFINE CHANNEL ('SERV.CON') + CHLTYPE(SVRCONN) + TRPTYPE(TCP);

6. 在 MQSC 控制台中执行命令创建一个传输队列。

DEFINE QLOCAL ('TARGET.XMITQ.QUEUE') USAGE (XMITQ);

7. 在 MQSC 控制台中执行命令创建一个远程队列。

DEFINE QREMOTE(REMOTE.QUEUE.TARGET) + RNAME('LOCAL.QUEUE.SOURCE') +
RQMNAME('SOURCE.QUEUE.MANAGER') + XMITQ('TARGET.XMITQ.QUEUE');

8. 在 MQSC 控制台中执行命令创建一个发送通道。

DEFINE CHANNEL ('TARGET.TO.SOURCE') + CHLTYPE(SDR) + CONNAME (127.0.0.1(1416)) +
XMITQ ('TARGET.XMITQ.QUEUE') + TRPTYPE(TCP);

在这里,1416 是服务消费者端的队列管理器 SOURCE.QUEUE.MANAGER 的端口号。

9. 在 MQSC console 中执行命令创建一个接收通道。

DEFINE CHANNEL ('SOURCE.TO.TARGET') CHLTYPE(RCVR) TRPTYPE(TCP);

在定义发送通道和接收通道的时候需要注意,服务消费者端定义发送通道的名字和服务提供者端定义的接收通道的名字相同,反之亦然。





回页首


在 WAS Console 中配置队列目的地

1. 导航到 WebSphere MQ messaging provider > WebSphere MQ queue connection factories ,选择服务器范围,并单击 New。填写其他输入字段,如图 3 中所示。


图 3. 创建连接工厂

2. 创建队列目的地,导航到 WebSphere MQ messaging provider > WebSphere MQ queue destinations,并单击 New。填写其他输入字段,如图 4 所示。

3. 创建监听端口,导航到 Application servers > server1 > Message Listener Service > Listener Ports,并单击 New。填写相应的字段,端口名字 MyLisner,连接工厂 JNDI 的名字,填写 jms/MyQFC,队列目的地 JNDI 的名字,填写 jms/MyQue。


图 4. 创建队列目的地





回页首


开发服务提供者应用程序

我们按照以下的步骤开发一个 EJB WEB 服务:

1.使用 Create an EJB project 向导创建新项目,在 RAD6.0 中,导航到 File =>New =>Projects =>EJB =>EJB Project 去创建一个 EJB 工程。新项目的名字 SoapJMSEJBService,无需创建 EJB client JAR 工程,如图 5 所示。


图 5. 创建 EJB 工程

2. 创建一个无状态会话 Bean。

导航到 File =>New =>Other =>EJB =>Enterprise Bean,去创建一个无状态会话 Bean,该会话 Bean 提供服务的基本实现,如图 6 所示。


图 6. 创建会话 Bean

3.在 EJB Bean 类 SoapJMSEJBServiceImplBean 中添加一个方法。


清单 1. SoapJMSEJBServiceImplBean 中的getInfo()方法
public ResponseType getInfo(RequestType getRequest){
	System.out.println("Name:"+getRequest.getName());
	System.out.println("Sex:" +getRequest.getSex());
	ResponseType responseType = new ResponseType();
	responseType.setResult("success!");
	return responseType;
}

同时,我们在这个 SoapJMSEJBService 工程中定义两个类,分别为服务的请求和响应类(RequestType 和 ResponseType)。定义这两个类,主要是为了让读者理解 SOAP MESSAGE 是如何与 JAVA OBJECT 之间进行绑定的,即 SOAP Request 消息如何转化为 RequestType 的实例对象,ResponseType 的实例对象如何转化为 SOAP Response 消息的。当然这种绑定对于用户来说是透明的。在后面的发送 SOAP Request 消息的的时候,通过发送的消息的格式和内容,读者会进一步明白这种绑定是如何进行的。类的定义,如图 7 所示。


图 7. 类的定义

4. 创建 Router 路由模块。

创建一个新的 EJB 模块来将 SOAP 消息请求路由到无状态会话 Bean Web 服务的实现,命名为 SoapJMSEJBServiceRouter。此 EJB Project 需要在生成 EjB Web 服务实现之前创建好。将该模块添加到 SoapJMSEJBServiceEAR 中,无需创建 EJB Client 程序,如图 8 所示。


图 8. Router 工程创建

5. 创建 EJB WEB 服务

I. 右键点击 SoapJMSEJBServiceImplBean.java, 在弹出的菜单中选中 web services =>createwebservice,如果该菜单未出现,请选择 Window =>Preferences =>Capabilities,如图 9 和图 10 所示,选中 Web Services Developer 下面的 Web Services Development 选项。


图 9. EJB WEB 服务创建 details—创建服务


图 10. WEB 服务开发集成环境设置

II. 下一步,选择创建 EJB Web Service 服务类型,如图 11 所示。


图 11. 选择创建服务的类型

III. 下一步,选择 EJB Web 服务的实现类,如图 12 所示。


图 12. 选择创建服务的实现类和包

IV. 下一步,选择服务的 Project,如图 13 所示。


图 13. 选择服务的运行环境

V. 选择 Router Project,选择 SOAP over JMS 作为 transports,按照图 14 所示填写相应的字段的内容。


图 14. 选择传输协议和 EJB 服务配置

VI. 下一步,选择 getInfo 方法,单击 Finish,如图 15 所示。


图 15. 选择服务包含的方法

你可以看到,一些新的代码和文件被自动的产生,如图 16 和图 17 所示,比如 SoapJMSEJBServiceImpl.wsdl 文件,它是 EJB Web 服务的描述文件。


清单 2. SoapJMSEJBServiceImpl.wsdl 文件
<wsdl:binding name="SoapJMSEJBServiceImplJMSSoapBinding" type=
"impl:SoapJMSEJBServiceImpl">
 <wsdlsoap:binding style="document" transport="http://schemas.xmlsoap.org/soap/jms"/>
 <wsdl:operation name="getInfo">
 <wsdlsoap:operation soapAction=""/>
 <wsdl:input name="getInfoRequest">
 <wsdlsoap:body use="literal"/>
 </wsdl:input>
 <wsdl:output name="getInfoResponse">
 <wsdlsoap:body use="literal"/>
 </wsdl:output>
 </wsdl:operation>
 </wsdl:binding>
<wsdl:service name="SoapJMSEJBServiceImplService">
 <wsdl:port binding="impl:SoapJMSEJBServiceImplJMSSoapBinding" 
 name="SoapJMSEJBServiceImplJMS">
 <wsdlsoap:address location="jms:/queue?destination=jms/MyQue&amp;
 connectionFactory=jms/MyQFC&amp;targetService=SoapJMSEJBServiceImplJMS"/>
 </wsdl:port>
 </wsdl:service>

我们还可以看到,RqeustType.java 和 ResponseType.java 生成了各自的三个 Helper Class,用于序列化、反序列化、XML 绑定。


图 16. 生成的 Web 服务的描述文件和辅助类

我们还会发现,在 Router Project 中,多了一个名为 JMSListenerMDB 的消息驱动 Bean,这个 MDB 负责监听 Request Queue 队列中的请求信息,解析 SOAP 消息,消息绑定,服务调用,回送消息到指定的队列。


图 17. 生成的 MDB

VII. 在 J2EE Perspective 下,导航 EJB Porjects =>SoapJMSEJBServiceRouter =>Message-Driven Beans,双击 WebServicesJMSRouter 工程,在 EJB Deployment Descriptor 编辑窗口中,单击 Listerner Port 选项,将 ListenerPort Name 设置为 MyLisner,如图 17 所示。

VIII. 在 VII 步骤中,单击 EJB Deployment Descriptor 编辑窗口中 Reference Tab 页,做 WebSphere Bindings 设置,如图 18 所示。


图 18. 回复队列连接工厂 JNDI 设置





回页首


部署 EJB Web 服务

1.右键单击 SoapJMSEJBServiceEAR 工程文件,在弹出的菜单中,选择 Export =>Export EAR,如图 19 所示。


图 19. 导出 EJB Web 服务 EAR 包

2.登陆 Console 控制台:http://localhost:9060/admin

导航 Applications =>Enterprise Applications,单击 Install,在接下来的页面中,单击 Browse 按钮,选择步骤 1 中 Export 出来的 EAR 文件,单击下一步,直到最后一步,单击完成。





回页首


构建 SOAP 消息请求

本例中,服务的消费者需发送一个 SOAP 请求消息,发送到服务消费者端的远程队列中,消息通过传输队列,自动发送到服务提供者端本地队列,JMSListenerMDB 监听服务提供者端的本地队列,当有消息到达,取出消息,解析消息,将消息与 getInfo() 中的参数 RequestType 进行绑定,调用服务,并将 getInfo() 返回的 ResponseType 实例转化为 SOAP 消息,放入到服务提供者端远程队列,Response Message 通过传输队列,发送到服务消费者端的本地队列,服务消费者从本端的本地队列得到服务调用的结果。

本例中,此 SOAP 请求消息为:


清单 3. SOAP 请求消息文件
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" 
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" 
xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Header>
<InternationalizationContext soapenv:mustUnderstand="0" 
xmlns="http://www.ibm.com/webservices/InternationalizationContext">
<Locales xmlns="">
<Locale>
<LanguageCode>en</LanguageCode>
<CountryCode>US</CountryCode>
</Locale>
</Locales>
</InternationalizationContext>
</soapenv:Header>
<soapenv:Body>
<p728:getInfo xmlns:p728="http://ejbs">
<getRequest>
<name>LvZhongxin</name>
<sex>Male</sex>
</getRequest>
</p728:getInfo>
</soapenv:Body>
</soapenv:Envelope>

此消息分为 SOAP Envelope, SOAP Header, SOAP Body 三部分,Body 部分根据具体的服务进行定制。<p728:getInfo xmlns:p728="http://ejbs"> 指明了需要调用 EJB Web 服务中的 getInfo。

<getRequest>
<name>LvZhongxin</name>
<sex>Male</sex>
</getRequest>

getInfo(RequestType) 的参数,通过 SOAP XML 消息和 Java Object 的自动绑定,在 EJB Web 服务中,getInfo 方法的参数 requestType 实例中的属性 name 和 sex 自动被付值为 LvZhongxin 和 Male. 那么,JMSListenerMDB 怎么知道消息中请求调用哪一个服务及其 Response Message 回送到哪个队列中,这部分信息需要服务的消费者在发送消息的时候,设置到 JMS 消息头中,示例代码如下:

msg.setJMSReplyTo(new MQQueue(m_queueMgr,m_replyQueue));
msg.setStringProperty("targetService", "SoapJMSEJBServiceImplJMS");
msg.setText(readFileText(m_filepath));//m_filepath 中含有上述完整的 SOAP XML 消息

我们需要设置 JMS 消息中 JMSReplyTo 信息,及其创建一个用户自定义的属性 targetService,其值为 SoapJMSEJBServiceImplJMS ,该值可以在自动生成的 WSDL 中找到,通过该值,JMSListenerMDB 部件知道调用哪个注册的服务。





回页首


EJB Web 服务测试

下载本文附带的测试程序,将该测试程序导入到 RAD 中。导向 File =>Import => Project Interchange,单击 Browse,查找下载到本地的 Zip 文件,选中所有的工程文件,单击 Finish 如图 20 所示,导入的工程如图 21 所示。


图 20. 导入测试程序


图 21. 导入的测试工程文件

JMSClient 工程下的 SoapMessage_getInfo 文件,包含要传输的 SOAP Request Message, client.properties 文件,设置服务消费者端的队列信息,包含需要访问的队列的主机、队列管理器的名字、端口号、请求队列、响应队列、通道的名字等等。SoapJmsClient.java 是客户端测试程序,负责发送消息和接收消息。运行测试程序 SoapJmsClient.java,程序运行的返回消息为:


清单 4. 运行测试程序后的返回消息
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" 
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" 
xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Header/>
<soapenv:Body>
<p728:getInfoResponse xmlns:p728="http://ejbs">
<getInfoReturn>
<result>success!</result>
</getInfoReturn>
</p728:getInfoResponse>
</soapenv:Body>
</soapenv:Envelope>





回页首


结束语

本文以实际的开发项目为背景,根据图 1 的结构图,从搭建 MQ Infrastructure 开始,一步一步详细的介绍了在 RAD 中开发通过 SOAP/JMS 访问 EJB Web 服务的步骤,完整的通信过程,并对通信过程中对读者透明的部分加以解释,便于读者的深入理解。这一通信过程涉及发送 SOAP 请求消息到请求队列,服务调用,到 SOAP 请求消息与服务方法入口参数之间的绑定,以及服务方法返回结果转换为 SOAP 消息回送到返回队列一系列行为。本文还介绍了如何构建特定于某一服务的 SOAP 请求消息和构造请求消息的注意事项,这一内容在实际的项目中,在与客户关于消息模板的定制中,起到重要的作用。






回页首


下载

描述名字大小下载方法
代码示例jmsclient_AND_service.zip24 KBHTTP
关于下载方法的信息


参考资料

学习

获得产品和技术


关于作者

吕忠心:2002 年获得计算机应用硕士学位,在电信、银行、交通行业软件开发方面有着丰富的经验,现在在 IBM GBS 大连分公司从事软件的设计和开发工作。




对本文的评价








IBM 公司保留在 developerWorks 网站上发表的内容的著作权。未经IBM公司或原始作者的书面明确许可,请勿转载。如果您希望转载,请通过 提交转载请求表单 联系我们的编辑团队。
    关于 IBM 隐私条约 联系 IBM 使用条款