通过 JMS Web 服务使用请求响应 SOAP

来源:互联网 发布:ubuntu终端安装软件 编辑:程序博客网 时间:2024/06/06 08:36

使用基于 JMS 的 SOAP 传输比基于 HTTP 的 SOAP 传输更具可伸缩性、更高效;以下是一些有关如何入手的提示。

作者:Bob Murphy

2008 年 9 月发布

通过 Java 消息服务 (JMS) API 管理 SOAP 消息比通过 HTTP/S 提供更高的可伸缩性和可靠性。更高的效率源于 JMS 在一个队列中传输和存储 Web 服务请求和响应消息,直至服务器可以处理这些消息,然后客户端释放两个系统中的线程和其他资源。

配置为使用 JMS 传输的 Web 服务与其客户端通过一个 JMS 队列进行通信。客户端向该队列发送一条 SOAP 消息,并且在一个仅为 JMS 会话建立的临时列队中等待响应消息。Web 服务处理消息并将响应发回临时队列。

将基于 JMS 的 SOAP 配置为传输协议时,客户端的 Java 代码和服务不会更改。实际上,Web 服务可以同时支持基于 JMS 的 SOAP 和基于 HTTP 的 SOAP。这两个协议间的显著差异在于如何定义 Web 服务终端。

在本文中,您将学习使用基于 JMS 的 SOAP 传输的一些技巧,将 Oracle Service Bus(以前称为 BEA AquaLogic Service Bus)作为消息中枢。目前尚未对基于 JMS 的 SOAP 传输定义一个标准,如果您需要与其他供应商实施相集成,本文会有所帮助。

示例

以下示例代码显示了一个 Web 服务配置为同时支持 JMS 和 HTTP 传输。WLJmsTransport 批注配置 Web 服务中的 JMS 传输,其用法显示为粗体。除了服务 URI 和端口名之外,还定义了 JMS 队列和连接工厂的 JNDI 名称。这些项目为可选项,如果未定义,连接工厂默认为 weblogic.jms.ConnectionFactory,队列默认为 weblogic.wsse.DefaultQueue。

package example;import javax.jws.WebMethod;import javax.jws.WebParam;import javax.jws.WebResult;import javax.jws.WebService;import javax.jws.soap.SOAPBinding;import weblogic.jws.WLHttpTransport;import weblogic.jws.WLJmsTransport;import com.accounts.AccountData;import com.accounts.accountsXMLSchema.AccountDocument;import com.accounts.accountsXMLSchema.AccountNameDocument;import com.accounts.accountsXMLSchema.AccountType;@WebService(name="AccountsPortType", serviceName="AccountsWebService",targetNamespace="http://www.accounts.com/AccountsService")@SOAPBinding(style=SOAPBinding.Style.DOCUMENT,        use=SOAPBinding.Use.LITERAL,        parameterStyle=SOAPBinding.ParameterStyle.WRAPPED)@WLHttpTransport(serviceUri="Accounts",portName="AccountsPort")@WLJmsTransport(serviceUri="AccountsJMS",portName="AccountsJMSPort",connectionFactory=apps.service.cf, queue="apps.service.queue")public class AccountsWebService {@WebMethod(operationName = "create")@WebResult(name="Account")public AccountDocument createAccount(@WebParam(name="AccountName")AccountNameDocument accountNameDoc) throws Exception {AccountDocument doc =          AccountDocument.Factory.newInstance();AccountType account = doc.addNewAccount();account.setAccountName(accountNameDoc.getAccountName());account.setAccountId(1001);return doc;}}

WSDL

Web 服务定义语言 (WSDL) 描述 Web 服务,以便客户端可以发送请求和接收响应。WSDL 的 binding 和 service 部分包含为传输协议定义的具体内容。对于基于 JMS 的 SOAP,绑定传输定义为 http://www.openuri.org/2002/04/soap/jms/,服务的终端地址包括 JMS 队列和连接工厂的 JNDI 名称;协议为 jms 而不是 http。例如,上面显示的 Web 服务的终端地址为 jms://localhost:7020/AccountsWS/AccountsJMS?URI=apps.service.queue&FACTORY=apps.service.cf。

以下几个 WSDL 片段重点关注了基于 JMS 的 SOAP 传输:

<s0:binding name="AccountsWebServiceSoapBindingjms"    type="s1:AccountsPortType">      <s2:binding style="document"         transport="http://www.openuri.org/2002/04/soap/jms/" />        <! ...    !></s0:binding><s0:service name="AccountsWebService"><s0:port binding="s1:AccountsWebServiceSoapBindingjms"name="AccountsJMSPort"><s2:address location = "jms://localhost:7020/AccountsWS/  AccountsJMS?URI=apps.service.queue&FACTORY=apps.service.cf" /> </s0:port></s0:service>

上面示例中的终端地址由以下部分组成:

协议............... jms
服务器地址......... localhost:7020
上下文路径...........AccountsWS
服务 URI............AccountsJMS
JMS 队列名......... apps.service.queue
JMS 连接工厂.... apps.service.cf

值得一提的是,WSDL 在使用 HTTP 的服务器上也同样可用。例如,我正使用的 WSDL 可以在 http://localhost:7020/AccountsWS/AccountsJMS?WSDL 上使用。

WebLogic JAX-RPC 客户端

独立的客户端使用 clientgen 实用程序在 WSDL 中生成的类。该实用程序创建一个 jar,包含生成 JAX-RPC 存根以与 Web 服务通信。下面是运行 clientgen 的 Ant 文件:

<project default="build-client">  <property name="weblogic.lib"     value="D:/bin/bea1020/wlserver_10.0/server/lib" /><property name="clientgen.wsdl" value="AccountsWebService.wsdl" /><property name="clientgen.jar" value="AccountsWSClient.jar" /><property name="clientgen.package" value="com.accounts.wsclient" /><path id="weblogic.class.path"><filelist dir="${weblogic.lib}"><file name="weblogic.jar"/></filelist></path><taskdef name="clientgen"classname="weblogic.wsee.tools.anttasks.ClientGenTask"    classpathref="weblogic.class.path"/>  <target name="build-client"><clientgenwsdl="${clientgen.wsdl}"destFile="${clientgen.jar}"packageName="${clientgen.package}"classpathref="weblogic.class.path"/></target></project>

Java Web 服务客户端代码使用生成的 javax.xml.rpc.Service 类来获取生成的 Stub。和使用 HTTP Web 服务协议一样,stub 类包含代理每个 Web 服务操作的方法。在上述 Web 服务实施示例中,创建了操作方法名称并将 AccountNameDocument XML 对象作为它唯一的参数。返回对象是一个 Account XML 对象。

因为 JMS 是一个传输协议,服务器地址是可以定义的;这决定着 JNDI 队列和连接工厂对象的检索位置。此外,还可以设置一个以毫秒为单位的超时值;这决定着等待 Web 服务响应的时间。如果未设置,默认行为为永远等待。

Java Web 服务客户端如下所示。

package com.accounts.jmswsclient;import java.rmi.RemoteException;import javax.xml.rpc.ServiceException;import javax.xml.rpc.Stub;import weblogic.wsee.jaxrpc.WLStub;import weblogic.wsee.jaxrpc.WlsProperties;import com.accounts.accountsxmlschema.AccountType;public class AccountsWSClient {  public static void main(String[] args)  {try {  AccountsWebServiceJMSService service =               new AccountsWebServiceJMSService_Impl();  AccountsPortType wlstub =                service.getAccountsProxyWebServiceJMSport();             ((Stub)wlstub)._setProperty(               WLStub.JMS_TRANSPORT_JNDI_URL,               "t3://localhost:7020");           ((Stub)wlstub)._setProperty(              WlsProperties.READ_TIMEOUT, 5000);   AccountType account = wlstub.create("new-account-01");} catch (RemoteException e) {e.printStackTrace();}catch (ServiceException e) {e.printStackTrace();}  }}

除了对 Stub.setProperty() 的两次调用,JMS 传输的 Web 服务客户端代码和 HTTP 传输的 Web 服务客户端代码都是相同的。发送一条 XML 消息并接收一个响应。

客户端无需使用 clientgen 实用程序构建生成的 Web 服务存根,而是使用 JMS 直接像队列发送一条 XML 消息。注意,XML 必须包装在 SOAP 信封中。此外,必须在 JMS 消息上设置两个特定于 WebLogic 的属性(都是字符串值)。

JMS 属性_wls_mimehdrContent_Typetext/xml; charset="utf-8"URI/{context-path}/{service-uri}

在我们的示例 Web 服务中,URI 值为 /AccountsProxyWS/AccountsJMS。这个客户端还负责创建响应消息的列队和设置 JMSReplyTo 属性。

配置业务服务

Oracle Service Bus 的业务服务是用 WSDL 创建的。在我们的示例 Web 服务中,我定义了 HTTP 和 JMS 的绑定。如果您要使用此示例,确保使用 JMS 绑定或端口。

服务终端是从 WSDL 中复制的,但在 service bus 上,此终端无效,必须进行更正。在总线上,JMS 终端以 jms://{服务器:端口}/{连接工厂名}/{队列名} 形式出现。调用业务服务时,上下文路径和服务 URI 的值设置为一个传输头。

如果需要来自服务的响应,则需要一个物理响应列队。与 Web 服务客户端使用 clientgen 生成的存根不同,总线不会为回复目标创建一个临时列队。对于 Response Correlation Pattern,我使用 JMS Message Id。下面的屏幕截图显示了 Business Service Configuration 详细信息。

注意,您无法成功测试此服务。这是因为定义要调用的实际 Web 服务的 URI 未设置。另一个必需的 WebLogic 属性 _wls_mimehdrContent_Type 也没有设置。这些是在调用此业务服务的代理服务中设置的(参见下面的配置详细信息)。

配置代理服务

使用相同的 WSDL 构建一个用于公开我们的业务服务的简单代理服务,与业务服务一样,其终端地址也需要更正。还需要一个与业务服务所使用 JSM 队列不同的 JMS 队列。与业务服务不同,不需要指定响应列队,因为代理的客户端指定响应队列。

代理需要一个路由到业务服务的管道,并将传输头设置为请求操作:

只有响应传输头设置为响应 mime 类型时,客户端才能使用响应。

更新客户端以使用代理服务

配置完代理后,我们的客户端需要更新为使用新队列。在终端地址中用 WSDL 指定队列和连接工厂。可以使用 JmsTransportInfo 类改写这个值,通过将该类设置为 Stub 上的一个新属性,可以在客户端应用程序上定义一个新的终端地址。下面是设置 JmsTransportInfo 属性的 Java 代码:

String uri = "jms://localhost:7020" +    "/AccountsProxyWS/AccountsJMS?URI=sb.service.queue";((Stub)wlstub)._setProperty("weblogic.wsee.connection.transportinfo",   new weblogic.wsee.connection.transport.jms.JmsTransportInfo(uri));

结论

基于 JMS 传输协议来使用 SOAP 与基于更常见的 HTTP 协议不同。需要记住的重要内容:

  • 终端地址包括队列名和连接工厂。
  • 代理总线代理中需要设置 URI 和 mime 类型。
  • 读取延时、队列和连接工厂都有默认值。您很可能希望覆盖这些值。

其他信息

  • 使用 JAX-RPC 的 WebLogic Web 服务入门
  • 调用 Web 服务
  • 使用 JMS 传输作为连接协议