在当今的企业世界中,Web服务作为应用程序集成的一种机制正Swift普及。 它们还为应用程序提供功能,使其能够通过Internet与外部应用程序进行通信,以实现企业之间的企业对企业(B2B)集成。 简单对象访问协议(SOAP)是在这种情况下用于调用Web服务的协议之一。 SOAP为客户端和服务之间的通信提供了基于XML的标准化消息格式。 SOAP通常使用HTTP作为传输方式,但也支持JMS之类的其他传输方式。
服务的Web服务描述语言(WSDL)定义在端点,端口,操作和消息方面提供了该服务的详细信息。 服务和端点提供了到服务的连接详细信息,例如,如果服务可通过HTTP使用,则端点是URL;如果服务可通过JMS使用,则端点将提供JMS域名和查找名称。 网络服务端点的WSDL规范定义了端点可以支持的四个传输原语,也称为操作。 这些是单向,请求响应,请求响应和通知。 可以使用四种模式中的任何一种来调用Web服务。 尽管这些模式处于WSDL的抽象级别,但通过使操作面向RPC或面向文档,对SOAP的“绑定”在集成机制中提供了更多的可变性。 Java™应用程序世界的总体趋势是在开发应用程序时使用开源软件。 但是,在尝试在Java应用程序中实现这些集成模式时,我们发现没有单一的开源软件使我们能够实现所有这四种模式。 一个人需要使用Axis和WSIF等组件中的可用功能混合,才能实现这四种模式。
在请求-响应模式中,端点(在这种情况下为实际的Web服务)在从客户端接收到需要响应的消息后,将相关消息发送回去。 在这种模式下,客户端(即应用程序)调用Web服务,并且Web服务做出响应。 例如,当在线购物车将要借记的金额发送给信用卡公司,而信用卡公司以交易ID进行回复时。
在单向模式中,端点(Web服务)从客户端接收不需要响应的消息。 在这种模式下,客户端(应用程序)将信息发送到Web服务,并且不等待响应。 客户端可以从服务请求一些信息,但是不必等待它继续运行,该服务可以在以后的时间点发送响应。
接下来的部分将讨论如何实现Java应用程序与支持这两种模式的服务端点的集成。
用于Web服务实现的开源框架
可以使用各种开源框架来实现Web服务和客户端。 Apache的Axis是使用Web服务的框架,可用于编写Web服务客户端。 它提供了本质上是SOAP引擎的Axis服务器,以及允许客户端调用SOAP Web服务的Axis客户端。 它使用SOAP作为通信的应用程序协议。 Axis默认情况下支持HTTP作为传输协议,但也可以使用它来构建JMS扩展。
Web服务调用框架(WSIF)可用于为各种类型的服务编写客户端。 它使用服务的WSDL定义来调用它。 基于WSIF的客户端可用于调用Web服务以及调用使用Java消息服务(JMS),企业JavaBeans(EJB),Java程序和本机代码实现的服务。 使用WSIF的客户端不需要了解服务的实现细节,而仅需要该服务的WSDL。
WSIF框架使编写Web服务客户端非常容易,尤其是对于使用JMS,Java和EJB程序的动态客户端。 服务的参数详细信息在WSDL中定义,并且在执行时就知道。
应该注意的是,WSIF在调用文档样式的Web服务方面有一些限制。 从应用程序的角度(在本例中为客户端),我们将与请求-响应模式的集成称为活动的“同步调用”类型,将单向模式称为“异步调用”类型。
应用程序客户端端点调用的请求
同步调用
在同步调用中,客户端将消息发送到服务并等待响应。 该服务接收消息,对其进行处理,然后将响应发送回去。
根据WSDL语法中的定义,请求-响应操作包含输入和输出元素-先输入然后输出。
清单1.用于请求-响应操作的WSDL
<wsdl:definitions .... >
<wsdl:portType .... > *
<wsdl:operation name="nmtoken" parameterOrder="nmtokens">
<wsdl:input name="nmtoken"? message="qname"/>
<wsdl:output name="nmtoken"? message="qname"/>
<wsdl:fault name="nmtoken" message="qname"/>*
</wsdl:operation>
</wsdl:portType >
</wsdl:definitions>
WSIF提供了动态调用Web服务的灵活性。 WSIF解析WSDL,获取有关服务,为服务定义的端口,绑定和操作的信息。 用户可以在运行时选择端口,传输和操作,并提供输入参数以调用Web服务。 优点是无需为每个Web服务编写单独的客户端。 使用WSIF的另一个优点是,即使Web服务的端点发生了变化,客户端也不会发生变化。 实际上,客户端甚至不需要知道Web服务的位置。 而且,如果将来可以将给定的Web服务作为EJB使用,则客户端可以仅通过使用新的WSDL来调用EJB,而无需在客户端代码中进行任何修改。 WSIF解析WSDL,以创建WSIFService,WSIFPort,WSIFoperation,WSIFMessage的对象实例以用于输入,输出和故障,并执行该操作。 WSIF作为分发提供了用于动态调用Web服务的示例。 此方法对于调用简单的Web服务特别有用。 WSIF可用于调用RPC和文档样式的Web服务。 在清单2中,示例代码说明了如何使用WSIF来调用RPC样式的Web服务。
清单2.使用WSIF的RPC样式请求-响应Web服务的客户端示例代码
// ...
// get service factory object
WSIFServiceFactory factory = WSIFServiceFactory.newInstance();
// get service object for given wsdl definition object and service object
WSIFService dpf =
factory.getService(definition, service);
// get port from wsif service object for given port name
port = dpf.getPort(portName);
// get operation object from port object for given operation and input/output message names
WSIFOperation operation = port.createOperation(
operationName,
inputMessage,
outputMessage);
// create input message
WSIFMessage input = operation.createInputMessage();
// invoke the service
operation.executeRequestResponseOperation(input, output, fault);
如果该Web服务是文档样式的Web服务,则该应用程序可以使用Axis客户端:
- 该应用程序动态创建一个客户端(这是在应用程序执行期间完成的),并使用Web服务定义中提供的端点信息(具体端口地址)。
- 客户端创建Axis客户端的“ Call”对象的实例,并使用Web服务端点的地址在Call对象中设置目标端点地址。
- 接下来,客户端读取被调用服务所需的输入参数。 为此,客户端在运行时使用JAXB [I2]将Java中的输入参数转换为XML元素对象(org.w3c.dom.Element)。 XML绑定Java体系结构(JAXB)提供了一种将XML模式绑定到Java代码表示中的便捷方法。
- 客户端创建这些Element对象的数组,并调用Axis Call对象的invoke方法。
- invoke方法构造SOAP请求消息,并通过发送SOAP消息来调用服务,并从服务获取输出参数,并将其作为org.apache.axis.message.SOAPBodyElement对象的数组返回给客户端。
- 然后,客户端又在运行时使用JAXB(XML到Java绑定)来构造与从服务接收的Output参数相对应的等效Java对象。
- 然后,客户端将这组Java对象提供给应用程序。
这种调用方法对于调用具有复杂数据类型的文档样式的Web服务特别有用。 复杂的数据类型首先使用JAXB转换为XML,然后传递给服务调用程序。 输出参数也以XML格式接收,并使用JAXB在Java对象中解析。
清单3.使用WSIF的RPC样式请求-响应Web服务的客户端示例代码
SOAPBodyElement[] element = // create SOAPBodyElement array using input objects
// create call object
Service service = new Service();
Call call = (Call) service.createCall();
// set end point (http address)
call.setTargetEndpointAddress(endPoint);
// invoke the service by passing the SOAPBodyElement
Vector output = (Vector) call.invoke(element);
// create element array from vector
Element elemArray[] = new Element[output.size()];
for (int i = 0; i < output.size(); i++) {
if (output.get(i) instanceof Element) {
elemArray[i] = (Element) output.get(i);
}
if (output.get(i) instanceof SOAPBodyElement) {
elemArray[i] = ((SOAPBodyElement) output.get(i)).getAsDOM();
}
}
// convert Element array back into Java Objects using JAXB
异步调用
在异步调用中,客户端将消息发送到服务,然后服务使用它。 在这一点上,客户端不希望该服务做出答复。
正如WSDL语法中为单向操作定义的那样,操作仅具有输入元素。
清单4.单向操作的WSDL
<wsdl:definitions> <wsdl:portType .... > *
<wsdl:operation name="nmtoken">
<wsdl:input name="nmtoken"? message="qname"/>
</wsdl:operation>
</wsdl:portType >
</wsdl:definitions>>
可以使用基于JMS的SOAP来实现异步调用。 JMS具有异步消息传递的默认行为。
清单5.用于基于JMS的SOAP的单向Web服务的客户端的示例代码
// ...
// provide JMS handler
Call.addTransportPackage(packageName);
Call.setTransportForProtocol(
"JMSTransport",
JMSTransport.class);
// get client configuration
EngineConfiguration defaultConfig =
(new
DefaultEngineConfigurationFactory()).getClientEngineConfig();
// create custom configuration for client
SimpleProvider config = new SimpleProvider(defaultConfig);
// get chain for given transport
SimpleTargetedChain chain =
new SimpleTargetedChain(
new JMSSender(connectionFactory, destination));
config.deployTransport("JMSTransport", chain);
// create service using custom configuration
Service service = new Service(config);
Call call = (Call) service.createCall();
call.setOperation(super.getOperation());
// set JMS specific information (topic/queue name, context factory) in call
// these values will be needed in chain for actually sending JMS message
call.setProperty(JMSConstants.DESTINATION, destination);
call.setProperty(
"transport.jms.ConnectionFactoryJNDIName",
connectionFactory);
if (destinationDomain.equalsIgnoreCase("topic")) {
call.setProperty(JMSConstants.DOMAIN,
JMSConstants.DOMAIN_TOPIC);
}
if (destinationDomain.equalsIgnoreCase("queue")) {
call.setProperty(JMSConstants.DOMAIN,
JMSConstants.DOMAIN_QUEUE);
}
call.setTransport(new JMSTransport());
// invoke the web service
call.invoke(params);
// ...
WSIF支持异步调用。 通过在API中调用不同的方法,可以将同一WSIF客户端用于异步调用。
清单6.使用WSIF的单向Web服务客户端的示例代码
// ...
// create operation
WSIFOperation operation = port.createOperation(
operationName,
inputMessage,
outputMessage);
// prepare input
WSIFMessage input = operation.createInputMessage();
// invoke web service one-way
operation.executeInputOnlyOperation(input);
// ...
结论
我们已经研究了Web服务端点的请求-响应和单向模式。 一旦确定了功能,我们还将展示如何在开放源代码框架中使用适当的Web服务来支持与这两种模式的集成。 在本系列的第2部分中,我们描述了Web服务中涉及的客户端端点可以支持的另外两种集成模式,即请求响应和通知。
翻译自: https://www.ibm.com/developerworks/java/library/ws-pattern-open1/index.html