SOAP 栈 和 XML marshaling
ServiceMix使用JAX-WS和JAXB 2.0来创建WS-*规范的POJO、接口,例如WS-Addressing, WS-Notification, WS-ReliableMessaging, WS-Resourceframework等等。这种方法的唯一缺点是Java5特定的,因此不能在JDK1.5以下的JVM运行。要使用JDK1.4 JVM,或者使用XMLBeans,或者使用Retroweaver之类的工具来把JDK1.5实现转换为JDK1.4二进制。
WS-Notification支持当前的实现是POJO,可以直接在应用中调用,也可以打包为远程的Web服务(使用遵从JAX-WS约束的SOAP协议栈来访问)。当前使用JAX WS RI(参考实现)
WS-Notification的接口在org. servicemix.wspojo.notification 包中,由JAX-WS从WSDL文件生成而来,依赖于JAVA5 、JAX-WS以及JAXB2.0注解。
WS-Notification的实现在WS-Notification interfaces are here .
接口的apidoc: org. servicemix.wspojo.notification
为了可以pub/sub,需要一个NotificationConsumer 或者 NotificationBroker。当前的实现都是ActiveMQNotificationBroker 。这个Broker会创建一个到ActiveMQ代理的连接(缺省时是在同一个JVM内嵌入broker)。
Publish一个 WS-Notification消息,如下:
EndpointReferenceType consumer = createEPR(ReceiverComponent.SERVICE, ReceiverComponent.ENDPOINT);
wsnBroker.subscribe(consumer, "myTopic", null, true);
Element body = parse("<hello>world</hello>");
wsnBroker.notify("myTopic", body);
sub:
PullPoint pullPoint = wsnCreatePullPoint.createPullPoint();
Subscription subscription = wsnBroker.subscribe(pullPoint.getEndpoint(), "myTopic", null);
除了ActiveMQ、ServiceMix的依赖之外,WS-Notification还依赖于:
- JAX-WS 、JAXB 2.0 APIs (annotations) 将内嵌在JAVA 6,但在JDK 5中只需添加jaxb-api.jar 以及 jaxws-api.jar
- JAX-WS 实现。如果想要支持SOAP协议上的远程服务。当前使用JAX-WS RI
ServiceMix WSIF Example
Web Services Invocation Framework (WSIF)提供了调用Web服务的Java API,隐藏服务是如何提供的细节,例如是通过SOAP、JMS还是其他什么,等等。
下面说明ServiceMix构件使用不同的实现协议,例如Axis、本地Java、EJB、JMS、JCA或者CCI,与Apache WSIF交互,来调用Web服务。
WSIF example 演示了如何通过WSIF展示Web服务(over JMS队列):
NOTE: 本实例不是一个完整的可run的例子。它只是通过一个典型实用的web应用演示了WSIF在ServiceMix构件中是怎么使用的。图中的下列构件没有完全实现:
1. Web Form没有创建
2. 没有配置servicemix.xml 中HTTP BC的目标服务为"checkAvailabity"构件.
下面给出的关键代码片断摘自一个更大的例子,可以在Apache Web Services Project中找到。本实例中,客户端应用通过JMS队列向Web服务应用提交ZIP Code。Web服务检查此ZIP Code地区的DSL服务是否可用,发送JMS队列消息回映客户。客户端使用WSIF来隐藏JMS实现细节。
在本实例中,客户端使用WSIF API向Web服务发送ZIP Code请求。WSIF为客户端处理JMS细节。ServiceMix WSIF API向客户提供统一的API,处理Web服务调用的细节,简化客户端编程。
此实例中显示了ServiceMix客户API的重要特性:如何为一个Web服务绑定包含附加的用来配置服务实现的WSIF元数据的WSDL文件。
也就是说,servicev.wsdl文件包含了WSIF对WSDL的扩展,扩展中绑定了Web服务的传输协议。
例子的详细分析
逻辑流程:
1. 用户打开Web浏览器,访问一个Web表单,输入信息后按submit按钮提交Zip Code。提交按钮发送表单和Zip Code
2. ServiceMix HTTP BC通过客户API创建一个InOut exchange message. 消息发送到NMR,随后到checkAvailability 构件
3. checkAvailability 构件发送请求到JMS队列
4. 此Web 服务使用MDB实现,它的"onMessage" 方法监听队列中的消息
5. MDB处理请求,并通过一个临时队列发送回应消息给checkAvailability构件。回应或者是‘true’,或者是‘false’
6. checkAvailability 构件从队列中收到回应消息
7. checkAvailability 构件回应 HTTP 客户
8. 客户发送结果给Web Form,显示处理结果。
程序细节
对应的servicemix.xml文件:
<component id="checkAvailability" service="foo:checkAvailability" class="org.servicemix.components.wsif.WSIFBinding">
<property name="definitionResource" value="classpath:org/servicemix/components/wsif/service.wsdl"/>
</component>
Service.wsdl:位于servicemix_src_install_dir/servicemix-components/src/test/resources/org/apache/servicemix/components/wsif
<service name='CheckServiceAvailability'>
<port name='CheckAvailabilityPort' binding='tns:CheckAvailabilityJMSBinding'>
<!-- ActiveMQ configuration -->
<jms:address destinationStyle="queue"
jndiDestinationName="dynamicQueues/test.org.servicemix.example.wsif"
jndiConnectionFactoryName="ConnectionFactory"
initialContextFactory="org.activemq.jndi.ActiveMQInitialContextFactory"
jndiProviderURL="tcp://localhost:61626"/>
</port>
</service>
<jms:address jmsVendorURI="xxx"
jndiDestinationName="xxx"
destinationStyle="xxx"
jndiConnectionFactoryName="xxx"
initialContextFactory="xxx"
jndiProviderURL="xxx"
jmsProviderDestinationName="xxx"
jmsImplementationSpecificURI="xxx" />
- port 元素中只能有一个jms:address
- jmsVendorURI 是可选元素,WSIF不使用。不过允许客户端来获取JMS实现。
- destinationStyle 、jmsImplementationSpecificURI中必须有一个元素(且仅有一个)要指明
- jmsImplementationSpecificURI 以实现特定的格式表示的队列管理器和队列.WSIF目前未实现
- destinationStyle 的值为queue 或topic, 但目前WSIF没有实现Topic。
- 若指明了destinationStyle,则jndiDestinationNamejms、ProviderDestinationName二选一地要指明其中一个.
- jndiDestinationName 是WSIF将发送请求消息的目的JMS Queue的JNDI名字
- jmsProviderDestinationName 是WSIF将发送请求消息的目的JMS Queue的JMS名字
- 若指明了jndiDestinationNamethen或jmsProviderDestinationName,则必须指定jndiConnectionFactoryName
- 当指定的是jmsProviderDestinationName名时,jndiConnectionFactoryName 有可能要使用,因为replyTo Queue的JNDI名字有可能要传到WSIF中
- jndiConnectionFactoryName是WSIF将使用的连接工厂的JNDI名字
- 若指明了destinationStyle, 则jndiProviderURL /initialContextFactory (同时或者任意一个都可以)需要指明。
- jndiProviderURL 和 initialContextFactory 指明了要使用的JNDI数据库。未指定则WSIF使用缺省的JNDI
WSIF 使用下列顺序查找JNDI中的队列和队列管理器 :
1. 在缺省的(本地)JNDI查找 java:comp/env/<name>
2. 在WSDL中指定的JNDI中查找 java:comp/env/<name>
3. 在缺省的(本地)JNDI查找 <name>
4. 在WSDL中指定的JNDI中查找 <name>
编码WSDL1.1的parts:XML vs. properties
JBI规范要求使用XML编码机制编码WSDL1.1的parts。ServiceMix支持此要求。另外,ServiceMix也支持NMR消息的消息属性,直接使用wsdl文件中的命名parts,因此避免了不必要的XML marshalling。
作为可选方案,也可以写一个Java客户而不是Web Form来调用Web服务。下列Java客户端代码使用支持WSIF方式调用的ServiceMix客户API,传入和传出命名参数。此Java客户功能类似上图中的Http Client。也需要配置来与”checkAvailablity”服务通讯,即要配置”checkAvailablity”为它的NMR消息目的地。
InOut exchange = client.createInOutExchange();
exchange.getInMessage().setProperty("zipCode", "10505");
client.sendSync(exchange);
NormalizedMessage out = exchange.getOutMessage();
String result = (String) out.getProperty("result");
System.out.println("Found value: " + result);
上述Java 代码使用WSDL 1.1 service.wsdl ,直接使用它的命名的parts:
<message name='checkAvailabilityRequest'>
<part name='zipCode' type='xsd:string'/>
</message>
<message name='checkAvailabilityResponse'>
<part name='result' type='xsd:string'/>
</message>
<portType name='CheckAvailabilityPortType'>
<operation name='checkAvailability'>
<input message='tns:checkAvailabilityRequest'/>
<output message='tns:checkAvailabilityResponse'/>
</operation>
</portType>
<binding name='CheckAvailabilityJMSBinding' type='tns:CheckAvailabilityPortType'>
<jms:binding type="TextMessage"/>
<format:typeMapping encoding="XML" style="Java">
<format:typeMap typeName="xsd:string" formatType="java.lang.String"/>
</format:typeMapping>
<operation name='checkAvailability'>
<input>
<jms:input parts="zipCode"/>
<jms:property message="Request" part="myInt"/>
<jms:propertyValue name="myLiteralString" type="xsd:string" value="Hello World"/>
</input>
<output>
<jms:output parts="result"/>
</output>
</operation>
</binding>
WebServices消息在不同的技术中的表示方法
如果通过BPEL服务引擎、JAXRPC应用等等发送JBI实现的WSDL (1.1)消息,就必须对其加以转换/调整。
如下的WSDL消息:
<definitions xmlns="http://schemas.xmlsoap.org/wsdl/"
targetNamespace="urn:sample"
xmlns:tns="urn:sample"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" >
<types>
<xsd:schema targetNamespace="urn:sample">
<xsd:element name="value" type="xsd:string" />
</xsd:schema>
</types>
<message name="sampleMessage">
<part name="part1" type="xsd:string" />
<part name="part2" element="tns:value" />
</message>
</definitions>
对应的两个消息实例:
A 在bpel2.0中:概念化地在一个独立的xml文档中实现每个WSDL消息部分(part)。在这种情况下,或者文档元素相当于一个合成元素,其类型等同于部分(part)的type属性;或者文档元素就是部分(part)的element属性本身所引用的元素。
<ns2:message xmlns:ns2="http://com.bea.sample.bpelmessage">
first value!
</ns2:message>
<ns1:value xmlns:ns1="urn:sample">second value!</ns1:value>
B JBI中
对于JBI,WSDL (1.1)消息是在一个xml文档中实现的,该文档的元素将每个消息部分“包装”为子元素。
<jbi:message version="1.0" type="ns1:sampleMessage"
xmlns:ns1="urn:sample" xmlns:jbi="...">
<jbi:part>first value!</jbi:part>
<jbi:part>
<ns1:value>second value!</ns1:value>
</jbi:part>
</jbi:message>
C JAXRPC 2.0:
JAXRPC方法稍微有点不同,因为JAXRPC将WSDL消息绑定到Java,而不是在xml文档中表示它。但是这种方法还是类似于BPEL,因为它将每个WSDL消息部分绑定到单独的Java方法参数。
在这种情况下,可以考虑稍微改动一下前面的WSDL定义,使其更适合于RPC:
<definitions xmlns="http://schemas.xmlsoap.org/wsdl/"
targetNamespace="urn:sample"
xmlns:tns="urn:sample"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" >
<types>
<xsd:schema targetNamespace="urn:sample">
<xsd:element name="value1" type="xsd:string" />
<xsd:element name="value2" type="xsd:string" />
</xsd:schema>
</types>
<message name="sampleMessage">
<part name="part1" element="tns:value1" />
<part name="part2" element="tns:value2" />
</message>
<portType name="samplePT">
<operation name="sampleOper">
<input message="tns:sampleMessage" />
</operation>
</portType>
</definitions>
对应的Java绑定如下:
void sampleOper(java.lang.String part1, java.lang.String part2)throws RemoteException
D WSDL 2.0:
WSDL 2.0是这样解决这个问题的:避开WSDL消息部分概念,使用W 3C Schema元素指定WSDL消息,W 3C Schema元素在实现为文档实例时就已经具有显式映射了。