Oracle Account
Manage your account and access personalized content.
Sign up for an Oracle Account
Sign in to Cloud
Access your cloud dashboard, manage orders, and more.
Sign up for a free trial
通过 JMS Web 服务使用请求响应 SOAP
使用基于 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;
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">
<clientgen
wsdl="${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 的属性(都是字符串值)。
在我们的示例 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 传输作为连接协议
Bob Murphy
是一名 Oracle 首席销售顾问。他在北美技术中心的 SOA/BPM 技术服务工作。